summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore34
-rw-r--r--AUTHORS9
-rw-r--r--COPYING339
-rw-r--r--ChangeLog353
-rw-r--r--LICENSE3
-rw-r--r--Makefile.am72
-rw-r--r--NEWS740
-rw-r--r--README2
-rw-r--r--amd8111e.c306
-rw-r--r--at76c50x-usb.c32
-rwxr-xr-xautogen.sh11
-rw-r--r--bnxt.c97
-rw-r--r--cmis.c1041
-rw-r--r--cmis.h252
-rw-r--r--common.c211
-rw-r--r--common.h49
-rw-r--r--configure.ac86
-rw-r--r--cpsw.c193
-rw-r--r--de2104x.c783
-rw-r--r--dsa.c882
-rw-r--r--e100.c238
-rw-r--r--e1000.c640
-rw-r--r--et131x.c124
-rw-r--r--ethtool.8.in1780
-rw-r--r--ethtool.c6488
-rw-r--r--ethtool.spec.in41
-rw-r--r--fec.c220
-rw-r--r--fec_8xx.c82
-rw-r--r--fjes.c90
-rw-r--r--fsl_enetc.c259
-rw-r--r--hns3.c829
-rw-r--r--ibm_emac.c334
-rw-r--r--igb.c876
-rw-r--r--igc.c284
-rw-r--r--internal.h413
-rw-r--r--ixgb.c147
-rw-r--r--ixgbe.c1296
-rw-r--r--ixgbevf.c181
-rw-r--r--json_print.c228
-rw-r--r--json_print.h67
-rw-r--r--json_writer.c391
-rw-r--r--json_writer.h76
-rw-r--r--lan743x.c73
-rw-r--r--lan78xx.c88
-rw-r--r--list.h34
-rw-r--r--m4/ax_append_flag.m471
-rw-r--r--m4/ax_check_compile_flag.m474
-rw-r--r--marvell.c455
-rw-r--r--natsemi.c987
-rw-r--r--netlink/bitset.c259
-rw-r--r--netlink/bitset.h28
-rw-r--r--netlink/cable_test.c595
-rw-r--r--netlink/channels.c143
-rw-r--r--netlink/coalesce.c335
-rw-r--r--netlink/desc-ethtool.c595
-rw-r--r--netlink/desc-genlctrl.c113
-rw-r--r--netlink/desc-rtnl.c96
-rw-r--r--netlink/eee.c189
-rw-r--r--netlink/extapi.h136
-rw-r--r--netlink/features.c569
-rw-r--r--netlink/fec.c360
-rw-r--r--netlink/mm.c270
-rw-r--r--netlink/module-eeprom.c310
-rw-r--r--netlink/module.c179
-rw-r--r--netlink/monitor.c324
-rw-r--r--netlink/msgbuff.c256
-rw-r--r--netlink/msgbuff.h123
-rw-r--r--netlink/netlink.c527
-rw-r--r--netlink/netlink.h178
-rw-r--r--netlink/nlsock.c405
-rw-r--r--netlink/nlsock.h45
-rw-r--r--netlink/parser.c1141
-rw-r--r--netlink/parser.h153
-rw-r--r--netlink/pause.c331
-rw-r--r--netlink/permaddr.c114
-rw-r--r--netlink/plca.c296
-rw-r--r--netlink/prettymsg.c262
-rw-r--r--netlink/prettymsg.h146
-rw-r--r--netlink/privflags.c158
-rw-r--r--netlink/pse-pd.c193
-rw-r--r--netlink/rings.c237
-rw-r--r--netlink/rss.c230
-rw-r--r--netlink/settings.c1377
-rw-r--r--netlink/stats.c333
-rw-r--r--netlink/strset.c297
-rw-r--r--netlink/strset.h25
-rw-r--r--netlink/tsinfo.c124
-rw-r--r--netlink/tunnels.c236
-rw-r--r--pcnet32.c249
-rw-r--r--qsfp.c1059
-rw-r--r--qsfp.h636
-rw-r--r--realtek.c689
-rw-r--r--rxclass.c1459
-rwxr-xr-xscripts/ethtool-import-uapi67
-rw-r--r--sfc.c3928
-rw-r--r--sff-common.c387
-rw-r--r--sff-common.h214
-rw-r--r--sfpdiag.c281
-rw-r--r--sfpid.c505
-rw-r--r--shell-completion/bash/ethtool1281
-rw-r--r--smsc911x.c91
-rw-r--r--stmmac.c71
-rw-r--r--test-cmdline.c340
-rw-r--r--test-common.c385
-rw-r--r--test-features.c555
-rw-r--r--tg3.c42
-rw-r--r--tse.c112
-rw-r--r--uapi/linux/const.h36
-rw-r--r--uapi/linux/ethtool.h2206
-rw-r--r--uapi/linux/ethtool_netlink.h984
-rw-r--r--uapi/linux/genetlink.h103
-rw-r--r--uapi/linux/hdlc/ioctl.h94
-rw-r--r--uapi/linux/if.h296
-rw-r--r--uapi/linux/if_addr.h77
-rw-r--r--uapi/linux/if_ether.h181
-rw-r--r--uapi/linux/if_link.h1427
-rw-r--r--uapi/linux/libc-compat.h267
-rw-r--r--uapi/linux/neighbour.h224
-rw-r--r--uapi/linux/net_tstamp.h205
-rw-r--r--uapi/linux/netlink.h379
-rw-r--r--uapi/linux/posix_types.h38
-rw-r--r--uapi/linux/rtnetlink.h830
-rw-r--r--uapi/linux/socket.h38
-rw-r--r--uapi/linux/stddef.h58
-rw-r--r--uapi/linux/types.h57
-rw-r--r--vioc.c34
-rw-r--r--vmxnet3.c199
127 files changed, 54133 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2679fae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,34 @@
+*.o
+
+Makefile
+Makefile.in
+
+INSTALL
+missing
+depcomp
+install-sh
+compile
+ethtool-config.h*
+ethtool.spec
+ethtool.8
+ethtool
+test-cmdline
+test-driver
+test-features
+stamp-h1
+config.*
+aclocal.m4
+configure
+*.tar.gz
+push
+
+.dotest
+autom4te.cache
+.deps
+test-*.log
+test-*.trs
+
+.*.swp
+*.patch
+.dirstamp
+tags
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..e1f0715
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,9 @@
+David Miller <davem@redhat.com>
+Jakub Jelinek <jj@ultra.linux.cz>
+Jeff Garzik <jgarzik@pobox.com>
+Tim Hockin <thockin@sun.com>
+Eli Kupermann <eli.kupermann@intel.com>
+Chris Leech <christopher.leech@intel.com>
+Scott Feldman <scott.feldman@intel.com>
+Andi Kleen
+Ben Hutchings <ben@decadent.org.uk>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/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/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..1fd043e
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,353 @@
+The changelog after March 2005 can be obtained from the git repository
+at <git://git.kernel.org/pub/scm/network/ethtool/ethtool.git>.
+
+The changelog after version 2 up to March 2005 can be obtained from the
+BitKeeper repository at <bk://gkernel.bkbits.net/ethtool>.
+
+
+Tue Aug 17 2004 Jeff Garzik <jgarzik@pobox.com>
+
+ * NEWS, configure.ac: Release version 2
+
+Fri Jul 2 2004 Jeff Garzik <jgarzik@pobox.com>
+
+ Merged
+ * fec_8xx.c, ethtool-util.h, Makefile.am: Add fec_8xx register dump.
+ Contributed by Pantelis Antoniou <panto@intracom.gr>
+
+ * Update ethtool.c to iterate through a list of drivers
+ * Fixed fec_8xx.c warnings on 64-bit
+
+Fri Jul 2 2004 Jim Lewis <jim@jklewis.com>
+
+ * pcnet32.c, ethtool-util.h, Makefile.am: Add pcnet32 register dump.
+
+Fri Apr 9 2004 Jason Lunz <lunz@reflexsecurity.com>
+
+ * ethtool.c: Remove incorrect restriction on ethernet interface
+ names.
+
+Fri Apr 9 2004 OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
+
+ * ethtool.c: This fixes the bogus tail backslash that I did.
+
+Fri Apr 9 2004 Jim Lewis <jim@jklewis.com>
+
+ * ethtool.c: Return results of self-test back to OS,
+ via exit(2).
+
+Fri Apr 9 2004 Jeb Cramer <cramerj@intel.com>
+
+ * e1000.c: Update device id list and add printout of phy type in
+ register dump. Set default mac_type to 82543 since register offsets
+ haven't changed.
+
+Fri Apr 9 2004 Jeff Garzik <jgarzik@pobox.com>
+
+ * configure.ac, Makefile.am, ethtool.c, etc.:
+ convert to more recent autoconf.
+
+Sat Aug 30 2003 OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
+
+ * ethtool.8, ethtool.c: ethtool register dump raw mode
+
+Sat Jul 19 2003 Scott Feldman <scott.feldman@intel.com>
+
+ * ethtool.8, ethtool.c, ethtool-copy.h:
+ Add support for TSO get/set. Corresponds to NETIF_F_TSO.
+ Extended -k|K option to included tso, and changed meaning from
+ just "checksum/sg" to more general "offload". Now covers Rx/Tx
+ csum, SG, and TSO.
+
+Thu May 28 2003 Ganesh Venkatesan <ganesh.venkatesan@intel.com>
+
+ * ethtool-copy.h: new definitions for 10GbE
+
+Thu May 28 2003 Scott Feldman <scott.feldman@intel.com>
+
+ * ethtool.c: Add ethtool -E to write EEPROM byte.
+ * e100.c: Added MDI/MDI-X status to register dump.
+
+Thu May 28 2003 Reeja John <reeja.john@amd.com>
+
+ * amd8111e.c: new file, support for AMD-8111e NICs
+ * ethtool.c: properly set ecmd.advertising
+
+Sat Mar 29 2003 OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
+
+ * realtek.c: clean up chip enumeration, support additional chips
+
+Fri Mar 28 2003 Jeb Cramer <cramerj@intel.com>
+
+ * e1000.c: Update supported devices (82541 & 82547). Add bus type,
+ speed and width to register dump printout.
+ * ethtool.c (show_usage): Add -S to printout of supported commands.
+
+Tue Jan 22 2003 Jeff Garzik <jgarzik@pobox.com>
+
+ * natsemi.c (PRINT_INTR, __print_intr): Decompose PRINT_INTR
+ macro into macro abuse and function call portions. Move the
+ actual function body to new static functoin __print_intr.
+
+ This eliminates the annoying build warning :)
+
+Thu Jan 16 2003 Jeb Cramer <jeb.j.cramer@intel.com>
+
+ * ethtool.c (do_regs, dump_eeprom): Fix memory leaks on failed
+ operations. Add error handling of dump_regs(). Modify printout of
+ eeprom dump to accomodate larger eeproms.
+ * e1000.c: Update supported devices. Add error conditions for
+ unsupported devices.
+
+Mon Oct 21 2002 Ben Collins <bcollins@debian.org>
+
+ * ethtool.c: Add new parameters to -e, for raw EEPROM output, and
+ offset and length options.
+ * natsemi.c (natsemi_dump_eeprom): Show correct offset using new
+ offset feature above.
+ * tg3.c: New file, implements tg3_dump_eeprom.
+ * Makefile.am: Add it to the build sources.
+ * ethtool-util.h: Prototype tg3_dump_eeprom.
+ * ethtool.8: Document new -e options.
+
+Thu Oct 17 2002 Tim Hockin <thockin@sun.com>
+
+ * ethtool.c: make calls to strtol() use base 0
+
+Wed Sep 18 2002 Scott Feldman <scott.feldman@intel.com>
+
+ * ethtool.c (dump_regs): call e100_dump_regs if e100
+ * e100.c: new file
+ * ethtool-util.h: prototype e100_dump_regs
+
+Thu Jun 20 2002 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * ethtool.8: document new -S stats dump argument
+ * configure.in, NEWS: release version 1.6
+
+Fri Jun 14 2002 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * realtek.c (realtek_dump_regs): dump legacy 8139 registers
+ * ethtool.c (do_gstats, doit, parse_cmdline):
+ support dumping of NIC-specific statistics
+
+Fri Jun 14 2002 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * realtek.c (realtek_dump_regs): dump RTL8139C+ registers
+
+Fri Jun 14 2002 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * realtek.c: new file, dumps RealTek RTL8169 PCI NIC's registers
+ * Makefile.am, ethtool.c, ethtool-util.h: use it
+
+Tue Jun 11 2002 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * NEWS: list new commands added recently
+ * ethtool.c (do_gcoalesce, do_scoalesce, dump_coalesce): new
+ (parse_cmdline, doit): handle get/set coalesce parameters (-c,-C)
+ (do_[gs]*): convert to use table-driven cmd line parsing
+ * ethtool.8: document -c and -C
+
+Tue Jun 11 2002 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * ethtool.c (do_gring, do_sring, dump_ring,
+ parse_ring_cmdline): new functions
+ (parse_cmdline, doit): handle get/set ring parameters (-g,-G)
+ (do_spause): fix off-by-one bugs
+ * ethtool.8: document -g and -G
+
+Tue Jun 11 2002 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * ethtool.c (do_gpause, do_spause, dump_pause,
+ parse_pause_cmdline): new functions
+ (parse_cmdline, doit): handle get/set pause parameters (-a,-A)
+ * ethtool.8: document -a, -A, -e, and -p
+
+Wed May 22 2002 Chris Leech <christopher.leech@intel.com>
+ Scott Feldman <scott.feldman@intel.com>
+
+ * ethtool-copy.h: add support for ETHTOOL_PHYS_ID function.
+ * ethtool.c: add support for ETHTOOL_PHYS_ID function, add
+ support for e1000 reg dump.
+ * Makefile.am: add e1000.c
+ * e1000.c: reg dump support for Intel(R) PRO/1000 adapters.
+ * ethtool-util.h: add e1000 reg dump support.
+
+Sat May 11 2002 Eli Kupermann <eli.kupermann@intel.com>
+
+ * ethtool.c (do_test): add support for online/offline test modes
+ Elsewhere: document "-t" arg usage, and handle usage
+
+Sat May 11 2002 Jes Sorensen <jes@wildopensource.com>
+
+ * ethtool.c (dump_ecmd): If unknown value is
+ encountered in speed, duplex, or port ETHTOOL_GSET
+ return data, print the numeric value returned.
+
+Wed May 1 2002 Eli Kupermann <eli.kupermann@intel.com>
+
+ * ethtool.8: document new -t test option
+
+Wed May 1 2002 Christoph Hellwig <hch@lst.de>
+
+ * Makefile.am (dist-hook): Use $(top-srcdir) for refering to sources.
+
+Mon Apr 29 2002 Christoph Hellwig <hch@lst.de>
+
+ * Makefile.am (SUBDIRS): Remove.
+ (RPMSRCS): Likewise.
+ (TMPDIR): Likewise.
+ (rpm): Likewise.
+ (EXTRA_DIST): Add ethtool.spec.in.
+ (dist-hook): New rule. Create rpm specfile.
+ * configure.in (AC_OUTPUT): Add ethtool.spec.
+ * ethtool.spec.in: New file. Rpm specfile template.
+ * redhat/ethtool.spec.in: Removed.
+ * redhat/Makefile.am: Removed.
+
+Wed Mar 20 2002 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * ethtool-copy.h: Merge coalescing param, ring
+ param, and pause param ioctl structs from kernel 2.5.7.
+ Merge ethtool_test changes fromkernel 2.5.7.
+ * ethtool: Update for ethtool_test cleanups.
+
+Wed Mar 20 2002 Eli Kupermann <eli.kupermann@intel.com>
+
+ * ethtool.c: (do_test): new function
+ Elsewhere: add support for 'perform test' function,
+ via a new "-t" arg, by calling do_test.
+
+Sun Mar 3 2002 Brad Hards <bhards@bigpond.net.au>
+
+ * ethtool.c (parse_cmdline): Support "usb"
+ as well as "eth" network interfaces. USB networking
+ uses a different prefix.
+
+Fri Feb 8 2002 "Noam, Amir" <amir.noam@intel.com>,
+ "Kupermann, Eli" <eli.kupermann@intel.com>
+
+ * ethtool.c (dump_advertised): new function.
+ (dump_ecmd): Call it.
+ Elsewhere: reformat code.
+
+Wed Nov 28 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * configure.in, Makefile.am, redhat/Makefile.am:
+ make sure redhat spec is included in dist tarball.
+
+Tue Nov 27 2001 Tim Hockin <thockin@sun.com>
+
+ * natsemi.c: strings changes
+ * ethtool.c: print messagelevel as hex (netif_msg_* shows better :)
+
+Sun Nov 18 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * NEWS: update with recent changes
+ * ethtool.8: phy address can be used if implemented in the
+ driver, so remove "Not used yet" remark.
+
+Sun Nov 18 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * Makefile.am, de2104x.c, ethtool-util.h, ethtool.c:
+ Support register dumps for de2104x driver.
+
+Tue Nov 13 2001 Tim Hockin <thockin@sun.com>
+
+ * natsemi.c, ethtool.c: use u8 data for ethtool_regs
+ * ethtool-copy.h: latest from kernel
+ * natsemi.c, ethtool.c: support ETHTOOL_GEEPROM via -e param
+
+Mon Nov 12 2001 Tim Hockin <thockin@sun.com>
+
+ * natsemi.c: check version, conditionally print RFCR-indexed data
+
+Wed Nov 07 2001 Tim Hockin <thockin@sun.com>
+
+ * ethtool.c: print less errors for unsupported ioctl()s
+ * ethtool.c: warn if all ioctl()s are unsupported or failed
+ * ethtool.c: change autoneg-restart mechanism to -r (as per jgarzik)
+ * ethtool.c: check for "eth" in devicename (per jg)
+ * ethtool.c: remove 'extraneous' braces
+
+Wed Nov 07 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * ethtool.c, ethtool.8: support bnc port/media
+
+Tue Nov 06 2001 Tim Hockin <thockin@sun.com>
+
+ * ethtool.c: clean up output for unhandled register dumps
+ * natsemi.c: finish pretty-printing register dumps
+ * ethtool.8: document -d option
+ * various: add copyright info, where applicable
+ * ethtool.c: be nicer about unsupported ioctl()s where possible
+ and be more verbose where nice is not an option.
+
+Mon Nov 05 2001 Tim Hockin <thockin@sun.com>
+
+ * natsemi.c: first cut at 'pretty-printing' register dumps
+
+Fri Nov 02 2001 Tim Hockin <thockin@sun.com>
+
+ * ethtool.c: add support for ETHTOOL_GREGS via -d (dump) flag
+ * ethtool.c: add support for device-specific dumps for known devices
+ * ethtool.c: make mode-specific handling allocate ifr_data
+ * Makefile.am: import ChangeLog to rpm specfile
+ * natsemi.c: added
+ * ethtool-util.h: added
+
+Thu Nov 01 2001 Tim Hockin <thockin@sun.com>
+
+ * ethtool.c: add support for ETHTOOL_GLINK in output
+ * ethtool.c: add support for ETHTOOL_NWAY_RST via 'autoneg restart'
+ * ethtool.c: add support for ETHTOOL_[GS]MSGLVL via 'msglvl' param
+ * ethtool.8: add documentation for above
+ * ethtool-copy.h: updated to sync with kernel
+
+Fri Oct 26 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * ethtool.8: Update contributors list, home page URL.
+ * ethtool.8: Much cleanup, no content change.
+ Contributed by Andre Majorel.
+ * ethtool.c: Clean up '-h' usage message.
+ Contributed by Andre Majorel.
+
+Fri Oct 26 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * Configure.in: bump version to 1.4cvs
+ * Makefile.am: include ethtool-copy.h in list of sources
+ * ethtool-copy.h:
+ Import ethtool.h from kernel 2.4.13.
+ * ethtool.c:
+ Define SIOCETHTOOL if it is missing,
+ trim trailing whitespace.
+ * NEWS: update for these changes
+
+Wed Sep 19 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * ethtool.c, ethtool-copy.h:
+ Import copy of kernel 2.4.10-pre12's ethtool.h.
+
+Wed Sep 19 2001 Tim Hockin <thockin@sun.com>
+
+ * Makefile.am, redhat/ethtool.spec.in:
+ Basic "make rpm" support.
+
+Wed Sep 19 2001 Tim Hockin <thockin@sun.com>
+
+ * AUTHORS, NEWS, ethtool.8, ethtool.c:
+ Wake-on-LAN support.
+
+Thu May 17 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * configure.in, NEWS, README: Version 1.2 release
+
+ * ethtool.c: Support ETHTOOL_GDRVINFO.
+ * ethtool.8: Document it.
+
+Fri Mar 20 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * Makefile.am, configure.in, autogen.sh, NEWS,
+ ChangeLog, AUTHORS, README:
+ Add autoconf/automake support.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d68dccb
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,3 @@
+ethtool is available under the terms of the GNU Public License version 2.
+
+See COPYING for details.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..b9e06ad
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,72 @@
+AM_CFLAGS = -Wall -Wextra -D_POSIX_C_SOURCE=200809L
+AM_CPPFLAGS = -I$(top_srcdir)/uapi
+LDADD = -lm
+
+man_MANS = ethtool.8
+EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh
+
+sbin_PROGRAMS = ethtool
+ethtool_SOURCES = ethtool.c uapi/linux/const.h uapi/linux/ethtool.h internal.h \
+ uapi/linux/net_tstamp.h uapi/linux/if.h uapi/linux/hdlc/ioctl.h \
+ uapi/linux/if_addr.h uapi/linux/if_ether.h uapi/linux/if_link.h \
+ uapi/linux/libc-compat.h uapi/linux/net_tstamp.h uapi/linux/neighbour.h \
+ uapi/linux/posix_types.h uapi/linux/rtnetlink.h uapi/linux/socket.h \
+ uapi/linux/stddef.h uapi/linux/types.h \
+ rxclass.c common.c common.h \
+ json_writer.c json_writer.h json_print.c json_print.h \
+ list.h
+if ETHTOOL_ENABLE_PRETTY_DUMP
+ethtool_SOURCES += \
+ amd8111e.c de2104x.c dsa.c e100.c e1000.c et131x.c igb.c \
+ fec.c fec_8xx.c fsl_enetc.c ibm_emac.c ixgb.c ixgbe.c \
+ natsemi.c pcnet32.c realtek.c tg3.c marvell.c vioc.c \
+ smsc911x.c at76c50x-usb.c sfc.c stmmac.c \
+ sff-common.c sff-common.h sfpid.c sfpdiag.c \
+ ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c \
+ igc.c cmis.c cmis.h bnxt.c cpsw.c lan743x.c hns3.c
+endif
+
+if ENABLE_BASH_COMPLETION
+bashcompletiondir = $(BASH_COMPLETION_DIR)
+dist_bashcompletion_DATA = shell-completion/bash/ethtool
+endif
+
+if ETHTOOL_ENABLE_NETLINK
+ethtool_SOURCES += \
+ netlink/netlink.c netlink/netlink.h netlink/extapi.h \
+ netlink/msgbuff.c netlink/msgbuff.h netlink/nlsock.c \
+ netlink/nlsock.h netlink/strset.c netlink/strset.h \
+ netlink/monitor.c netlink/bitset.c netlink/bitset.h \
+ netlink/settings.c netlink/parser.c netlink/parser.h \
+ netlink/permaddr.c netlink/prettymsg.c netlink/prettymsg.h \
+ netlink/features.c netlink/privflags.c netlink/rings.c \
+ netlink/channels.c netlink/coalesce.c netlink/pause.c \
+ netlink/eee.c netlink/tsinfo.c netlink/fec.c \
+ netlink/stats.c netlink/mm.c \
+ netlink/desc-ethtool.c netlink/desc-genlctrl.c \
+ netlink/module-eeprom.c netlink/module.c netlink/rss.c \
+ netlink/desc-rtnl.c netlink/cable_test.c netlink/tunnels.c \
+ netlink/plca.c \
+ netlink/pse-pd.c \
+ uapi/linux/ethtool_netlink.h \
+ uapi/linux/netlink.h uapi/linux/genetlink.h \
+ uapi/linux/rtnetlink.h uapi/linux/if_link.h \
+ uapi/linux/if.h uapi/linux/hdlc/ioctl.h
+AM_CPPFLAGS += @MNL_CFLAGS@
+LDADD += @MNL_LIBS@
+endif
+
+TESTS = test-cmdline
+check_PROGRAMS = test-cmdline
+test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES)
+test_cmdline_CFLAGS = -DTEST_ETHTOOL
+if !ETHTOOL_ENABLE_NETLINK
+TESTS += test-features
+check_PROGRAMS += test-features
+test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES)
+test_features_CFLAGS = -DTEST_ETHTOOL
+endif
+
+dist-hook:
+ cp $(top_srcdir)/ethtool.spec $(distdir)
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..7b47e5e
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,740 @@
+Version 6.7 - January 29, 2024
+ * Feature: support for setting TCP data split
+ * Fix: fix new gcc14 warning
+ * Fix: fix SFF-8472 transceiver module identification (-m)
+ * Misc: code cleanup
+
+Version 6.6 - November 23, 2023
+ * Feature: support for more CMIS transceiver modules (-m)
+ * Fix: fix build on systems with old kernel uapi headers
+
+Version 6.5 - September 12, 2023
+ * Feature: register dump for hns3 driver (-d)
+ * Fix: fix fallback to ioctl for sset (-s)
+ * Fix: fix empty slot search in rmgr (-N)
+
+Version 6.4 - July 1, 2023
+ * Feature: get/set Tx push buffer length (-G)
+ * Feature: sff-8636 and cmis: report LOL / LOS / Tx Fault (-m)
+ * Fix: fix duplex setting parser (-s)
+ * Misc: check and require C11 language standard
+ * Misc: clean up obsolete pre-build checks
+
+Version 6.3 - May 8, 2023
+ * Feature: PLCA support (--[gs]et-plca-cfg, --get-plca-status)
+ * Feature: MAC Merge layer support (--show-mm, --set-mm)
+ * Feature: pass source of statistics for port stats
+ * Feature: get/set rx push in ringparams (-g and -G)
+ * Feature: coalesce tx aggregation parameters (-c and -C)
+ * Feature: PSE and PD devices (--show-pse, --set-pse)
+ * Fix: minor fixes of help text (--help)
+ * Fix: fix build on systems with older system headers
+ * Fix: fix netlink support when PLCA is not present (no option)
+ * Fix: fixes for issues found with gcc13 -fanalyzer
+ * Fix: fix return code in rxclass_rule_ins (-N)
+ * Fix: more robust argc/argv handling
+
+Version 6.2 - February 21, 2023
+ * Feature: link down event statistics (no option)
+ * Feature: JSON output for coalesce (-c)
+ * Feature: new link modes (no option)
+ * Feature: JSON output for ring (-g)
+ * Feature: netlink handler for RSS get (-x)
+ * Fix: fix boolean value output in JSON output
+ * Fix: fix build errors and warnings
+
+Version 6.1 - December 19, 2022
+ * Feature: update link mode tables
+ * Feature: register dump for NXP ENETC driver (-d)
+ * Feature: report TCP header-data split (-g)
+ * Feature: support new message types in pretty print
+ * Fix: fix compiler warnings
+ * Fix: man page syntax fixes
+
+Version 6.0 - October 10, 2022
+ * Fix: advertisement modes autoselection by lanes (-s)
+
+Version 5.19 - August 22, 2022
+ * Feature: get/set tx push (-g and -G)
+ * Feature: register dump support for TI CPSW (-d)
+ * Feature: register dump support for lan743x chipset (-d)
+ * Fix: fix missing sff-8472 output in netlink path (-m)
+ * Fix: fix EEPROM byte write (-E)
+
+Version 5.18 - June 14, 2022
+ * Feature: get/set cqe size (-g and -G)
+ * Fix: fix typo in man page
+ * Fix: fix help text alignment
+ * Fix: improve attribute label (--show-fec)
+
+Version 5.17 - April 4, 2022
+ * Feature: transceiver module power mode (--set-module)
+ * Feature: transceiver module extended state (--show-module)
+ * Feature: get/set rx buffer length (-g and -G)
+ * Feature: tx copybreak buffer size (--get-tunable and --set-tunable)
+ * Feature: JSON output for features (-k)
+ * Feature: support OSFP transceiver modules (-m)
+ * Fix: add missing free() calls (--get-tunable and --set-tunable)
+
+Version 5.16 - January 19, 2022
+ * Feature: use memory maps for module EEPROM parsing (-m)
+ * Feature: show CMIS diagnostic information (-m)
+ * Fix: fix dumping advertised FEC modes (--show-fec)
+ * Fix: ignore cable test notifications from other devices (--cable-test)
+ * Fix: do not show duplicate options in help text (--help)
+
+Version 5.15 - November 9, 2021
+ * Feature: new extended link substates for bad signal (no arg)
+ * Feature: coalesce cqe mode attributes (-c and -C)
+ * Fix: multiple fixes of EEPROM module data parsing (-m)
+ * Fix: fix condition to display MDI-X info (no arg)
+
+Version 5.14 - September 12, 2021
+ * Feature: do not silently ignore --json if unsupported
+ * Feature: support new message types in pretty print
+
+Version 5.13 - July 9, 2021
+ * Feature: netlink handler for FEC (--show-fec and --set-fec)
+ * Feature: FEC stats support (--show-fec)
+ * Feature: standard based stats support (-S)
+ * Feature: netlink handler for module EEPROM dump (-m)
+ * Feature: page, bank and i2c selection in module dump (-m)
+
+Version 5.12 - May 2, 2021
+ * Feature: support lanes count (no option and -s)
+ * Fix: fix help message for master-slave parameter (-s)
+ * Fix: better error message for master-slave in ioctl code path
+ * Fix: get rid of compiler warnings in "make check"
+
+Version 5.10 - Dec 16, 2020
+ * Feature: infrastructure for JSON output
+ * Feature: separate FLAGS in -h output
+ * Feature: use policy dumps to check flags support
+ * Feature: show pause stats (-a)
+ * Feature: pretty printing of policy dumps
+ * Feature: improve error message when SFP module is missing
+ * Fix: use after free in netlink_run_handler()
+ * Fix: leaked instances of struct nl_socket
+ * Fix: improve compatibility between netlink and ioctl (-s)
+
+Version 5.9 - Oct 15, 2020
+ * Feature: extended link state
+ * Feature: QSFP-DD support
+ * Feature: tunnel information (--show-tunnels)
+ * Feature: Broadcom bnxt support
+ * Fix: improve compatibility between ioctl and netlink output
+ * Fix: cable test TDR amplitude output
+ * Fix: get rid of build warnings
+ * Fix: null pointer dereference running against old kernel (no arg)
+ * Fix: update link mode tables
+ * Fix: fix memory leaks and error handling found by static analysis
+
+Version 5.8 - Aug 4, 2020
+ * Feature: more ethtool netlink message format descriptions
+ * Feature: omit test-features if netlink is enabled
+ * Feature: netlink handler for gfeatures (-k)
+ * Feature: netlink handler for sfeatures (-K)
+ * Feature: netlink handler for gprivflags (--show-priv-flags)
+ * Feature: netlink handler for sprivflags (--set-priv-flags)
+ * Feature: netlink handler for gring (-g)
+ * Feature: netlink handler for sring (-G)
+ * Feature: netlink handler for gchannels (-l)
+ * Feature: netlink handler for schannels (-L)
+ * Feature: netlink handler for gcoalesce (-c)
+ * Feature: netlink handler for scoalesce (-C)
+ * Feature: netlink handler for gpause (-a)
+ * Feature: netlink handler for spause (-A)
+ * Feature: netlink handler for geee (--show-eee)
+ * Feature: netlink handler for seee (--set-eee)
+ * Feature: netlink handler for tsinfo (-T)
+ * Feature: master/slave configuration support
+ * Feature: LINKSTATE SQI support
+ * Feature: cable test support
+ * Feature: cable test TDR support
+ * Feature: JSON output for cable test commands
+ * Feature: igc driver support
+ * Feature: support for get/set ethtool_tunable
+ * Feature: dsa: mv88e6xxx: add pretty dump for 88E6352 SERDES
+ * Fix: fix build warnings
+ * Fix: fix nest type grouping in parser
+ * Fix: fix msgbuff_append() helper
+ * Fix: fix unwanted switch fall through in family_info_cb()
+ * Fix: fix netlink error message suppression
+ * Fix: fix netlink bitmasks when sent as NOMASK
+ * Fix: use "Not reported" when no FEC modes are provided
+ * Fix: ioctl: do not pass transceiver value back to kernel
+ * Fix: ethtool.spec: Add bash completion script
+
+Version 5.7 - Jun 4, 2020
+ * Feature: ethtool: Add support for Low Latency Reed Solomon
+ * Fix: ethtool.c: Report transceiver correctly
+ * Feature: features: accept long legacy flag names when setting features
+ * Feature: refactor interface between ioctl and netlink code
+ * Feature: netlink: use genetlink ops information to decide about fallback
+ * Feature: netlink: show netlink error even without extack
+ * Feature: ethtool: add support for newer SFF-8024 compliance codes
+ * Feature: Rewrite printf() due to -Werror=format-security
+
+Version 5.6 - May 12, 2020
+ * Feature: add --debug option to control debugging messages
+ * Feature: use named initializers in command line option list
+ * Feature: netlink: add netlink related UAPI header files
+ * Feature: netlink: introduce the netlink interface
+ * Feature: netlink: message buffer and composition helpers
+ * Feature: netlink: netlink socket wrapper and helpers
+ * Feature: netlink: initialize ethtool netlink socket
+ * Feature: netlink: add support for string sets
+ * Feature: netlink: add notification monitor
+ * Feature: netlink: add bitset helpers
+ * Feature: netlink: partial netlink handler for gset (no option)
+ * Feature: netlink: support getting wake-on-lan and debugging settings
+ * Feature: netlink: add basic command line parsing helpers
+ * Feature: netlink: add bitset command line parser handlers
+ * Feature: netlink: add netlink handler for sset (-s)
+ * Feature: netlink: support tests with netlink enabled
+ * Feature: netlink: add handler for permaddr (-P)
+ * Feature: netlink: support for pretty printing netlink messages
+ * Feature: netlink: message format description for ethtool netlink
+ * Feature: netlink: message format descriptions for genetlink control
+ * Feature: netlink: message format descriptions for rtnetlink
+ * Feature: netlink: use pretty printing for ethtool netlink messages
+
+Version 5.4 - January 10, 2020
+
+ * Feature: ethtool: implement support for Energy Detect Power Down
+ * Fix: fix arithmetic on pointer to void is a GNU extension warning
+ * Fix: fix unused parameter warnings in do_version() and show_usage()
+ * Fix: fix unused parameter warning in find_option()
+ * Fix: fix unused parameter warning in dump_eeprom()
+ * Fix: fix unused parameter warning in altera_tse_dump_regs()
+ * Fix: fix unused parameter warning in sfc_dump_regs()
+ * Fix: fix unused parameter warning in print_simple_table()
+ * Fix: fix unused parameter warning in natsemi_dump_regs()
+ * Fix: fix unused parameter warning in netsemi_dump_eeprom()
+ * Fix: fix unused parameter warning in ixgbe_dump_regs()
+ * Fix: fix unused parameter warning in realtek_dump_regs()
+ * Fix: fix unused parameter warning in lan78xx_dump_regs()
+ * Fix: fix unused parameter warning in {skge, sky2}_dump_regs()
+ * Fix: fix unused parameter warning in dsa_dump_regs()
+ * Fix: fix unused parameter warning in vmxnet3_dump_regs()
+ * Fix: fix unused parameter warning in st_{mac100, gmac}_dump_regs()
+ * Fix: fix unused parameter warning in ixgbevf_dump_regs()
+ * Fix: fix unused parameter warning in fec_8xx_dump_regs()
+ * Fix: fix unused parameter warning in tg3_dump_{eeprom, regs}()
+ * Fix: fix unused parameter warning in vioc_dump_regs()
+ * Fix: fix unused parameter warning in e100_dump_regs()
+ * Fix: fix unused parameter warning in de2104[01]_dump_regs()
+ * Fix: fix unused parameter warning in igb_dump_regs()
+ * Fix: fix unused parameter warning in e1000_dump_regs()
+ * Fix: fix unused parameter warning in smsc911x_dump_regs()
+ * Fix: fix unused parameter warning in at76c50x_usb_dump_regs()
+ * Fix: fix unused parameter warning in fec_dump_regs()
+ * Fix: fix unused parameter warning in amd8111e_dump_regs()
+ * Fix: fix unused parameter warning in et131x_dump_regs()
+ * Fix: fix unused parameter warning in ibm_emac_dump_regs()
+ * Fix: fix unused parameter warning in ixgb_dump_regs()
+ * Fix: fix unused parameter warning in fjes_dump_regs()
+ * Fix: fix unused parameter warning in e1000_get_mac_type()
+ * Fix: ethtool: correctly interpret bitrate of 255
+ * Fix: ethtool: mark 10G Base-ER as SFF-8472 revision 10.4 onwards
+ * Fix: ethtool: add 0x16 and 0x1c extended compliance codes
+
+Version 5.3 - September 23, 2019
+ * Feature: igb: dump RR2DCDELAY register
+ * Feature: dump nested registers
+
+Version 5.2 - July 25, 2019
+ * Feature: Add 100BaseT1 and 1000BaseT1 link modes
+ * Feature: Use standard file location macros in ethtool.spec
+
+Version 5.1 - May 17, 2019
+ * Feature: Add support for 200Gbps (50Gbps per lane) link mode
+ * Feature: simplify handling of PHY tunable downshift
+ * Feature: add support for PHY tunable Fast Link Down
+ * Feature: add PHY Fast Link Down tunable to man page
+ * Feature: Add a 'start N' option when specifying the Rx flow hash indirection table.
+ * Feature: Add bash-completion script
+ * Feature: add 10000baseR_FEC link mode name
+ * Fix: qsfp: fix special value comparison
+ * Feature: move option parsing related code into function
+ * Feature: move cmdline_coalesce out of do_scoalesce
+ * Feature: introduce new ioctl for per-queue settings
+ * Feature: support per-queue sub command --show-coalesce
+ * Feature: support per-queue sub command --coalesce
+ * Fix: fix up dump_coalesce output to match actual option names
+ * Feature: fec: add pretty dump
+
+Version 5.0 - March 13, 2019
+ * Feature: don't report UFO on kernels v4.14 and above
+ * Fix: zero initialize coalesce struct
+ * Feature: dsa: add pretty dump
+ * Feature: dsa: mv88e6xxx: add pretty dump
+ * Feature: dsa: mv88e6xxx: add pretty dump for 88E6185
+ * Feature: dsa: mv88e6xxx: add pretty dump for 88E6161
+ * Feature: dsa: mv88e6xxx: add pretty dump for 88E6352
+ * Feature: dsa: mv88e6xxx: add pretty dump for 88E6390
+ * Feature: dsa: mv88e6xxx: add pretty dump for others
+
+Version 4.19 - November 2, 2018
+ * Feature: support combinations of FEC modes
+ * Feature: better syntax for combinations of FEC modes
+ * Fix: Fix uninitialized variable use at qsfp dump
+
+Version 4.18 - August 24, 2018
+ * Feature: Add support for WAKE_FILTER (WoL using filters)
+ * Feature: Add support for action value -2 (wake-up filter)
+ * Fix: document WoL filters option also in help message
+ * Feature: ixgbe dump strings for security registers
+
+Version 4.17 - June 15, 2018
+
+ * Fix: In ethtool.8, remove superfluous and incorrect \c.
+ * Fix: fix uninitialized return value
+ * Fix: fix RING_VF assignment
+ * Fix: remove unused global variable
+ * Fix: several fixes in do_gregs()
+ * Fix: correctly free hkey when get_stringset() fails
+ * Fix: remove unreachable code
+ * Fix: fix stack clash in do_get_phy_tunable and do_set_phy_tunable
+ * Feature: Add register dump support for MICROCHIP LAN78xx
+
+Version 4.16 - April 13, 2018
+
+ * Feature: add support for extra RSS contexts and RSS steering filters
+ * Feature: Document RSS context control and RSS filters
+ * Fix: don't fall back to grxfhindir when context was specified
+ * Fix: correct display of VF when showing vf/queue filters
+ * Fix: show VF and queue in the help for -N
+ * Fix: correct VF index values for the ring_cookie parameter
+ * Feature: Add SFF 8636 date code parsing support
+
+Version 4.15 - February 1, 2018
+
+ * Feature: Support for FEC encoding control
+ * Fix: Fix coding style warnings and errors reported by checkpatch
+ * Feature: Add extended compliance codes parsing to sfp modules
+ * Fix: Revert "ethtool: Add DMA Coalescing support"
+ * Feature: Add ETHTOOL_RESET support via --reset command
+ * Fix: fix MFLCN register dump for 82599 and newer
+
+Version 4.13 - October 27, 2017
+
+ * Fix: Do not return error code if no changes were attempted.
+ * Fix: Fix formatting of advertise bitmask
+ * Feature: Document 56000 advertise link modes
+ * Fix: fix the rx vs tx mixup in set channel message
+ * Feature: add support for HWTSTAMP_FILTER_NTP_ALL
+ * Feature: Add DMA Coalescing support
+ * Feature: Remove UDP Fragmentation Offload error prints
+ * Feature: stmmac: Add macros for number of registers
+ * Feature: stmmac: Add DMA HW Feature Register
+
+Version 4.11 - June 2, 2017
+
+ * Feature: Support for configurable RSS hash function
+ * Feature: support queue and VF fields for rxclass filters
+ * Feature: Add support for 2500baseT/5000baseT link modes
+ * Fix: Fix SFF 8079 cable technology bit parsing
+ * Fix: sync help output for -x/-X with man page
+
+Version 4.10 - March 24, 2017
+
+ * Fix: Fix the "advertise" parameter logic.
+ * Feature: Implement ETHTOOL_PHY_GTUNABLE/ETHTOOL_PHY_STUNABLE and PHY downshift
+ * Feature: add register dump support for fjes driver (-d option)
+
+Version 4.8 - October 3, 2016
+
+ * Feature: QSFP Plus/QSFP28 Diagnostics Information Support
+ * Feature: Enhancing link mode bits to support 25G/50G/100G
+ * Feature: add support for 1000BaseX and missing 10G link mode
+ * Fixes: address Coverity issues 1363118 - 1363125
+
+Version 4.6 - June 26, 2016
+
+ * Feature: Support register dump on Intel X550 NICs (-d option)
+ * Fix: Correct some reported register offsets on Intel 10GbE NICs
+ (-d option)
+ * Feature: Add IPv6 support to NFC (-n, -N, -u and -U options)
+ * Feature: Add support for ETHTOOL_xLINKSETTINGS ioctls (no option
+ and -s option)
+ * Feature: Use netlink socket when AF_INET not available
+
+Version 4.5 - March 14, 2016
+
+ * Tests: Fix missing function declarations when building tests
+ * Tests: Fix return type of test_free() prorotype
+ * Feature: Add PHY statistics support (--phy-statistics option)
+ * Doc: Properly indent sub-options in man page
+ * Feature: Support setting default Rx flow indirection table
+ (-X option)
+ * Fix: Use 'sane' kernel type definitions on 64-bit architectures
+ * Fix: Don't ignore fread() return value (-d option)
+ * Fix: Heap corruption when dumping registers from a file (-d option)
+ * Fix: Stricter input validation for EEPROM setting (-E option)
+ * Fix: Fix strict-aliasing compiler warnings in marvell.c
+ * Tests: Fix use of uninitialised variable in test_realloc()
+ * Tests: Fix compiler warning in test-features.c
+
+Version 4.2 - October 9, 2015
+
+ * Feature: Support soldered-on modules in module EEPROM dump (-m option)
+ * Feature: Add register dump support for VMware vmxnet3 (-d option)
+ * Feature: Update register dump support for IBM EMAC (-d option)
+ (requires Linux 4.3 or a future stable update to 4.1 or 4.2)
+ * Doc: Fix typo in man page
+
+Version 4.0 - May 31, 2015
+
+ * Fix: Formatting of RX flow hash indirection table when size not
+ divisible by 8 (-x option)
+ * Fix: Add missing Advertised speeds (no option and -s option)
+ * Feature: Add support to get expansion ROM version (-i option)
+ * Feature: Include SFP serial number and date in EEPROM dump
+ (-m option)
+
+Version 3.18 - December 14, 2014
+
+ * Fix: Lookup of SFP Tx bias in SFF-8472 module diagnostics (-m option)
+ * Fix: Build with musl by using more common typedefs
+
+Version 3.16 - September 22, 2014
+
+ * Feature: Support for configurable RSS hash key (-x/-X options)
+
+Version 3.15 - July 20, 2014
+
+ * Feature: Add register dump support for Altera Triple Speed Ethernet
+ (-d option)
+
+Version 3.14 - April 21, 2014
+
+ * Fix: Report Backplane as supported port (no option)
+ * Feature: Allow building a smaller executable without pretty-
+ printing of register/EEPROM dumps (./configure --disable-pretty-dump)
+ * Fix: Typo in channel parameter format in an error message (-L option)
+
+Version 3.13 - January 27, 2014
+
+ * Doc: Update GPL text to include current address of the FSF
+ * Fix: Spelling fixes
+ * Doc: Fix advertising flag values in manual page for 20G link modes,
+ and add missing 1G, 10G and 40G link modes
+
+Version 3.12.1 - November 8, 2013
+
+ * Fix: Memory corruption when applying external calibration to
+ SFF-8472 module diagnostics (-m option)
+ * Feature: Add Intel 82599 and x540 DCB registers to dump
+ (-d option)
+
+Version 3.12 - November 7, 2013
+
+ * Fix: Remove alternate method to check for VLAN tag offload on Linux
+ < 2.6.37 (-k/-K options)
+ * Fix: Hide state of VLAN tag offload and LRO if the kernel is too old
+ for us to reliably detect them (-k option)
+ * Feature: Add register dump support for Solarflare SFC9100 family
+ (-d option)
+
+Version 3.11 - September 12, 2013
+
+ * Feature: Update Realtek chip list for register dump to match
+ r8169 driver in Linux 3.11 (-d option)
+ * Feature: Add ixgbevf support for register dump (-d option)
+ * Feature: Filter ixgbe register dump according to the specific chip
+ (-d option)
+
+Version 3.10 - July 1, 2013
+
+ * Feature: Beautify private flags print (--show-priv-flags option)
+
+Version 3.9 - April 30, 2013
+
+ * Feature: Display support for 10000BASE-KR link mode (no options)
+ * Feature: Add support for new versions of ixgbe register dump
+ (-d dump)
+
+Version 3.8 - February 28, 2013
+
+ * Feature: Allow setting destination MAC address in L3/L4 flow spec
+ rules (-N/-U option)
+ * Fix: Show full 64 bits of user-data (-n/-u option)
+ * Fix: Add version check for et131x regs (-d option)
+ * Doc: Improve description of -f, -t, -s, -N/-U, -W options in man page
+ * Fix: Restore 20000baseKR2 cap display (no options)
+
+Version 3.7 - December 13, 2012
+
+ * Fix: Gracefully handle failure of register pretty-printer (-d option)
+ * Feature: Add support for et131x registers (-d option)
+ * Feature: Basic optical diagnostics for SFF-8472 modules (-m option)
+
+Version 3.6 - October 5, 2012
+
+ * Feature: Allow setting MDI-X state (-s option)
+ * Fix: Preserve pause advertising bits when setting speed and
+ duplex with autoneg on (-s option)
+ * Fix: Don't call ioctl to set EEE parameters if they are the same
+ as the current parameters (--set-eee option)
+
+Version 3.5 - August 2, 2012
+
+ * Feature: Display support for 1000BASE-KX and 10GBASE-KX4 link modes
+ * Feature: Energy-Efficient Ethernet (EEE) configuration
+ (--show-eee and --set-eee options)
+ * Fix: Don't trust drivers to null-terminate strings
+ * Feature: Display support for 40G link modes
+ * Package: Update RPM summary, description and URL
+ * Package: Exclude redundant documentation from RPM
+
+Version 3.4.2 - July 16, 2012
+
+ * Fix: Fix regression in RX NFC rule insertion for drivers that do
+ not select rule locations (-N/-U option)
+ * Fix: Remove bogus error message when changing offload settings
+ on Linux < 2.6.39 (-K option)
+ * Fix: Use alternate method to check for VLAN tag offload on Linux
+ < 2.6.37 (-k option)
+
+Version 3.4.1 - June 13, 2012
+
+ * Fix: Work around failure of ETHTOOL_GSSET_INFO for unprivileged
+ users (-k option)
+ * Fix: Report any unexpected error code from ETHTOOL_GSSET_INFO
+ (-k and -K options)
+ * Doc: Fix the date of the man page to match the last update
+
+Version 3.4 - June 8, 2012
+
+ * Cleanup: Merge RX NFC options
+ * Doc: Improve description of RX NFC options
+ * Doc: Add ntuple to the -K option in the man page
+ * Feature: Show time stamping capabilities (-T option)
+ * Feature: Dump plug-in module EEPROM (-m option)
+ * Feature: Show and change all generic net device features
+ (-k and -K options)
+
+Version 3.2 - January 12, 2012
+
+ * Feature: Add support for querying and setting private flags
+ (--show-priv-flags, --set-priv-flags options)
+ * Feature: Omit zero values in Solarflare register tables (-d option)
+ * Feature: Allow driver to select RX NFC rule location (-U option)
+ * Fix: Correct register dump offsets for Intel 82575 chipsets
+ (-d option)
+
+Version 3.1 - November 16, 2011
+
+ * Fix: Show all non-zero registers for tg3 (-d option)
+ * Feature: Add support for external loopback test (-t option)
+ * Fix: Show correct flow control registers for Intel 82599 (-d option)
+ * Feature: Add support for reporting and configuring numbers of
+ channels/queues (-l and -L options)
+ * Feature: Report pause frame autonegotiation result (-a option)
+ * Doc: Change device name metavariable from 'ethX' to 'devname'
+ * Doc: Fix various layout problems
+ * Cleanup: Reorganise and add test cases for argument parsing
+ * Fix: Strictly check for extraneous or missing arguments; in
+ particular, fail if the device name is missing
+
+Version 3.0 - August 4, 2011
+
+ * Feature: Report supported pause frame modes
+ * Feature: Support firmware dump (-w and -W options)
+ * Feature: Report advertised and supported 20G link modes
+ * Feature: Add an 'l4data' option for ip4 filters (-U option)
+ * Fix: Correct swapped h_source and h_dest fields for ether filters
+ (-U option)
+ * Fix: Set ip_ver field correctly for ip4 filters (-U option)
+ * Fix: Correct parameter validation for -e and -E options; in
+ particular, treat the 'magic' value as unsigned
+
+Version 2.6.39 - June 1, 2011
+
+ * Feature: Report some driver features (-i option)
+ * Doc: Remove misleading 'Auto' advertising mask from manual page
+ * Doc: Improve table formatting on manual page, using tbl
+ * Doc: Remove initial blank page in printed manual page
+ * Doc: Fix line-wrapping of options
+ * Feature: Add support for ESP as a separate protocol from AH
+ (-n, -N, -u and -U options)
+ * Cleanup: Remove support for showing RX n-tuple settings
+ (-u option), which was never implemented correctly in the kernel
+ * Feature: Add support for RX network flow classifier (NFC)
+ (-u and -U options)
+ * Feature: Add support for e1000 M88 PHY registers (-d option)
+ * Cleanup: Change bug-address to netdev
+
+Version 2.6.38 - March 15, 2011
+
+ * Doc: Fix spelling and spacing in online help
+ * Doc: Update date, version and web site reference in manual page
+ * Doc: Fix spelling, capitalisation, consistency and style in
+ manual page
+ * Doc: Generalise some references to network devices rather than
+ Ethernet devices
+ * Fix: Don't silently ignore speed/duplex when autoneg is on
+ * Fix: Report an error (rather than full usage information) if
+ given an unrecognised option
+ * Feature: Add --version option
+
+Version 2.6.37 - January 5, 2011
+
+ * Fix: Build fix for distributions with kernel headers from
+ Linux 2.6.9 or earlier
+
+Version 2.6.36 - November 16, 2010
+
+ * Fix: RX n-tuple masks and documentation
+ * Feature: Ethernet-level RX n-tuple filtering and 'clear' action
+ * Feature: stmmac register dump support
+ * Feature: get permanent address (-P) option
+ * Feature: VLAN acceleration control
+
+Version 2.6.35 - August 10, 2010
+
+ * Feature: sfc register dump support
+ * Feature: improve cmd line parsing of ints, IPv4 addresses
+ * Feature: support ethtool named flags, messaging types
+ * Feature: minor man page fixes
+ * Feature: control RX flow hash indirection
+
+Version 2.6.34 - May 26, 2010
+
+ * Feature: Support n-tuple filter programming
+ * Feature: Support rx hashing, v2 (targetted for 2.6.35)
+ * Feature: Add names of newer Marvell chips
+
+Version 2.6.33 - February 24, 2010
+
+ This version introduces a new release numbering scheme, based
+ on the latest upstream kernel interface supported.
+
+ * Fix: several man page corrections
+ * Feature: rx flow hash configuration
+ * Feature: report 10000baseT support, where available
+ * Feature: report MDI-X status, pause auto-neg, link partner adverts
+ * Feature: support additional port types
+ * Feature: support arbitrary speeds, faster than 65535 Mb
+ * Feature: large and generic receive offload (LRO, GRO) support
+ * Feature: option to flash firmware image from specified file
+ * Feature: support for block writing of EEPROMs
+ * Feature: marvell register dump update
+ * Feature: at76c50x-usb, e1000e, igb, ixgbe, r8169 register dump support
+ * Cleanup: remove support for RX hashing by port (was removed in
+ kernel by 59089d8d162ddcb5c434672e915331964d38a754)
+ * Doc: Explicitly ship GPLv2 license, rather than relying
+ on autotools to supply it for us (autotools started auto-installing
+ GPLv3 recently)
+
+Version 6 - July 26, 2007
+
+ * Fix/security: Fix handling of statistics where the label
+ is exactly 32 bytes (ETH_GSTRING_LEN).
+ * Feature: Add ability to change the advertised speed/duplex
+ to a different range of values, rather than all-or-one.
+ * Feature: ixgb register dump support
+ * Feature: sky2 register dump support
+ * Feature: Fabric7 VIOC register dump support
+ * Feature: Decode raw register dump stored in a file
+ * Feature: Add ability to force hex register dump, if desired
+ * Feature: update e1000 register dump
+ * Feature: Additional 10Gbps support
+ * Feature: Add 2.5G support
+ * Feature: Update r8169 register dump
+ * Feature: SMSC LAN911x/LAN921x register dump support
+ * Cleanup: Update internal ethtool.h copy to match upstream
+ kernel 2.6.23-rc1 version of ethtool.h.
+
+Version 5 - September 1, 2006
+
+ * Security: Avoid potential buffer overflow
+ * Feature: GSO support
+ * Feature: skge register dump
+
+Version 4 - July 18, 2006
+
+ * Feature: UFO support
+ * Feature: support long options
+ * Features: e1000, pcnet32, tg3 updates
+ * Feature: added PPC4xx EMAC support
+ * Feature: Use hexdump instead of single values for register dump
+
+Version 3 - January 27, 2005
+
+ * Feature: r8159 register dump support
+ * Feature / bug fix: Support advertising gigabit ethernet
+ * Bug fix: make sure to advertise 10baseT-HD
+ * Other minor bug fixes.
+
+Version 2 - August 17, 2004
+
+ * Feature: ethtool register dump raw mode
+ * Feature: return results of self-test back to OS via exit(2)
+ * Feature: add verbose register dump for pcnet32, fec_8xx
+ * Maintenance: update to more recent autoconf
+ * Maintenance: minor updates to e1000-specific module
+ * Bug fix: Remove silly restriction on ethernet interface naming
+
+Version 1.8 - July 19, 2003
+
+ * Feature: Support amd8111e register dumps
+ * Feature: Support TSO enable/disable
+ * Feature: Support 10 gigabit ethernet
+ * Feature: Support writing EEPROM data
+ * Feature: Output e100 MDI/MDI-x status in register dump
+ * Feature: Clean up RealTek (RTL) chip output, support new chips.
+ * Feature: More supported e1000 devices.
+ * Bug fix: Properly set ecmd.advertising
+ * Bug fix: Fix leaks, handle some error conditions better.
+
+Version 1.7 - October 21, 2002
+
+ * Feature: Support e100 register dumps
+ * Feature: Support tg3 eeprom dumps
+ * Feature: Support partial eeprom dumps (with non-zero offsets)
+ * Feature: Support decimal/octal/hex numbers transparently,
+ at the user's discretion.
+
+Version 1.6 - June 20, 2002
+
+ * Feature: Support e1000 register dumps
+ * Feature: Support RealTek RTL-8139C+ and RTL-8169 register dumps
+ * Feature: Support coalescing config (ETHTOOL_[GS]COALESCE)
+ * Feature: Support ring param config (ETHTOOL_[GS]RINGPARAM)
+ * Feature: Support pause param config (ETHTOOL_[GS]PAUSEPARAM)
+ * Feature: Support physical NIC identification (ETHTOOL_PHYS_ID)
+ * Feature: Support NIC self-testing (ETHTOOL_TEST)
+ * Feature: Support NIC checksum/scatter-gather configuration
+ (ETHTOOL_[GS]RXCSUM, ETHTOOL_[GS]TXCSUM, ETHTOOL_[GS]SG)
+
+Version 1.5 - Mar 4, 2002
+
+ * Fix: support usb network interfaces
+ * Fix: include redhat spec file in autoconf build system
+ * Fix: minor fixes to natsemi register dump
+ * Feature: report advertised as well as supported media,
+ when printing device settings.
+
+Version 1.4 - Nov 19, 2001
+
+ * Support builds on configurations missing SIOCETHTOOL constant.
+ * Import ethtool.h from kernel 2.4.15-pre6.
+ * Support retrieval/setting of per-driver debug levels
+ (ETHTOOL G/SMSGLVL)
+ * Support pretty-printing register dumps on natsemi, de2104x
+ (ETHTOOL GREGS)
+ * Support restarting autonegotiation (ETHTOOL NWAY_RST)
+ * Support obtaining link status (ETHTOOL GLINK)
+
+Version 1.3 - Aug 02, 2001
+
+ * Support Wake-on-LAN (ETHTOOL GWOL and ETHTOOL SWOL ioctl).
+
+Version 1.2 - May 17, 2001
+
+ * Support ETHTOOL_GDRVINFO ioctl, which obtains
+ information from the ethernet driver associated
+ with the specified interface.
+
diff --git a/README b/README
new file mode 100644
index 0000000..9e03205
--- /dev/null
+++ b/README
@@ -0,0 +1,2 @@
+ethtool is a small utility for examining and tuning your ethernet-based
+network interface. See the man page for more details.
diff --git a/amd8111e.c b/amd8111e.c
new file mode 100644
index 0000000..175516b
--- /dev/null
+++ b/amd8111e.c
@@ -0,0 +1,306 @@
+
+/* Copyright (C) 2003 Advanced Micro Devices Inc. */
+#include <stdio.h>
+#include "internal.h"
+
+typedef enum {
+ /* VAL2 */
+ RDMD0 = (1 << 16),
+ /* VAL1 */
+ TDMD3 = (1 << 11),
+ TDMD2 = (1 << 10),
+ TDMD1 = (1 << 9),
+ TDMD0 = (1 << 8),
+ /* VAL0 */
+ UINTCMD = (1 << 6),
+ RX_FAST_SPND = (1 << 5),
+ TX_FAST_SPND = (1 << 4),
+ RX_SPND = (1 << 3),
+ TX_SPND = (1 << 2),
+ INTREN = (1 << 1),
+ RUN = (1 << 0),
+
+ CMD0_CLEAR = 0x000F0F7F, /* Command style register */
+
+}CMD0_BITS;
+typedef enum {
+
+ /* VAL3 */
+ CONDUIT_MODE = (1 << 29),
+ /* VAL2 */
+ RPA = (1 << 19),
+ DRCVPA = (1 << 18),
+ DRCVBC = (1 << 17),
+ PROM = (1 << 16),
+ /* VAL1 */
+ ASTRP_RCV = (1 << 13),
+ RCV_DROP0 = (1 << 12),
+ EMBA = (1 << 11),
+ DXMT2PD = (1 << 10),
+ LTINTEN = (1 << 9),
+ DXMTFCS = (1 << 8),
+ /* VAL0 */
+ APAD_XMT = (1 << 6),
+ DRTY = (1 << 5),
+ INLOOP = (1 << 4),
+ EXLOOP = (1 << 3),
+ REX_RTRY = (1 << 2),
+ REX_UFLO = (1 << 1),
+ REX_LCOL = (1 << 0),
+
+ CMD2_CLEAR = 0x3F7F3F7F, /* Command style register */
+
+}CMD2_BITS;
+typedef enum {
+
+ /* VAL3 */
+ ASF_INIT_DONE_ALIAS = (1 << 29),
+ /* VAL2 */
+ JUMBO = (1 << 21),
+ VSIZE = (1 << 20),
+ VLONLY = (1 << 19),
+ VL_TAG_DEL = (1 << 18),
+ /* VAL1 */
+ EN_PMGR = (1 << 14),
+ INTLEVEL = (1 << 13),
+ FORCE_FULL_DUPLEX = (1 << 12),
+ FORCE_LINK_STATUS = (1 << 11),
+ APEP = (1 << 10),
+ MPPLBA = (1 << 9),
+ /* VAL0 */
+ RESET_PHY_PULSE = (1 << 2),
+ RESET_PHY = (1 << 1),
+ PHY_RST_POL = (1 << 0),
+
+}CMD3_BITS;
+typedef enum {
+
+ INTR = (1 << 31),
+ PCSINT = (1 << 28),
+ LCINT = (1 << 27),
+ APINT5 = (1 << 26),
+ APINT4 = (1 << 25),
+ APINT3 = (1 << 24),
+ TINT_SUM = (1 << 23),
+ APINT2 = (1 << 22),
+ APINT1 = (1 << 21),
+ APINT0 = (1 << 20),
+ MIIPDTINT = (1 << 19),
+ MCCINT = (1 << 17),
+ MREINT = (1 << 16),
+ RINT_SUM = (1 << 15),
+ SPNDINT = (1 << 14),
+ MPINT = (1 << 13),
+ SINT = (1 << 12),
+ TINT3 = (1 << 11),
+ TINT2 = (1 << 10),
+ TINT1 = (1 << 9),
+ TINT0 = (1 << 8),
+ UINT = (1 << 7),
+ STINT = (1 << 4),
+ RINT0 = (1 << 0),
+
+}INT0_BITS;
+typedef enum {
+
+ /* VAL3 */
+ LCINTEN = (1 << 27),
+ APINT5EN = (1 << 26),
+ APINT4EN = (1 << 25),
+ APINT3EN = (1 << 24),
+ /* VAL2 */
+ APINT2EN = (1 << 22),
+ APINT1EN = (1 << 21),
+ APINT0EN = (1 << 20),
+ MIIPDTINTEN = (1 << 19),
+ MCCIINTEN = (1 << 18),
+ MCCINTEN = (1 << 17),
+ MREINTEN = (1 << 16),
+ /* VAL1 */
+ SPNDINTEN = (1 << 14),
+ MPINTEN = (1 << 13),
+ TINTEN3 = (1 << 11),
+ SINTEN = (1 << 12),
+ TINTEN2 = (1 << 10),
+ TINTEN1 = (1 << 9),
+ TINTEN0 = (1 << 8),
+ /* VAL0 */
+ STINTEN = (1 << 4),
+ RINTEN0 = (1 << 0),
+
+ INTEN0_CLEAR = 0x1F7F7F1F, /* Command style register */
+
+}INTEN0_BITS;
+
+typedef enum {
+
+ PMAT_DET = (1 << 12),
+ MP_DET = (1 << 11),
+ LC_DET = (1 << 10),
+ SPEED_MASK = (1 << 9)|(1 << 8)|(1 << 7),
+ FULL_DPLX = (1 << 6),
+ LINK_STATS = (1 << 5),
+ AUTONEG_COMPLETE = (1 << 4),
+ MIIPD = (1 << 3),
+ RX_SUSPENDED = (1 << 2),
+ TX_SUSPENDED = (1 << 1),
+ RUNNING = (1 << 0),
+
+}STAT0_BITS;
+
+#define PHY_SPEED_10 0x2
+#define PHY_SPEED_100 0x3
+
+
+int amd8111e_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+
+ u32 *reg_buff = (u32 *)regs->data;
+ u32 reg;
+
+ fprintf(stdout, "Descriptor Registers\n");
+ fprintf(stdout, "---------------------\n");
+
+ /* Transmit descriptor base address register */
+ reg = reg_buff[0];
+ fprintf(stdout,
+ "0x00100: Transmit descriptor base address register %08X\n",reg);
+
+ /* Transmit descriptor length register */
+ reg = reg_buff[1];
+ fprintf(stdout,
+ "0x00140: Transmit descriptor length register 0x%08X\n",reg);
+
+ /* Receive descriptor base address register */
+ reg = reg_buff[2];
+ fprintf(stdout,
+ "0x00120: Receive descriptor base address register %08X\n",reg);
+
+ /* Receive descriptor length register */
+ reg = reg_buff[3];
+ fprintf(stdout,
+ "0x00150: Receive descriptor length register 0x%08X\n",reg);
+
+ fprintf(stdout, "\n");
+
+
+ fprintf(stdout, "Command Registers\n");
+ fprintf(stdout, "-------------------\n");
+
+ /* Command 0 Register */
+ reg = reg_buff[4];
+ fprintf(stdout,
+ "0x00048: Command 0 register 0x%08X\n"
+ " Interrupts: %s\n"
+ " Device: %s\n",
+ reg,
+ reg & INTREN ? "Enabled" : "Disabled",
+ reg & RUN ? "Running" : "Stopped");
+
+ /* Command 2 Register */
+ reg = reg_buff[5];
+ fprintf(stdout,
+ "0x00050: Command 2 register 0x%08X\n"
+ " Promiscuous mode: %s\n"
+ " Retransmit on underflow: %s\n",
+ reg,
+ reg & PROM ? "Enabled" : "Disabled",
+ reg & REX_UFLO ? "Enabled" : "Disabled");
+ /* Command 3 Register */
+ reg = reg_buff[6];
+ fprintf(stdout,
+ "0x00054: Command 3 register 0x%08X\n"
+ " Jumbo frame: %s\n"
+ " Admit only VLAN frame: %s\n"
+ " Delete VLAN tag: %s\n",
+ reg,
+ reg & JUMBO ? "Enabled" : "Disabled",
+ reg & VLONLY ? "Yes" : "No",
+ reg & VL_TAG_DEL ? "Yes" : "No");
+
+ /* Command 7 Register */
+ reg = reg_buff[7];
+ fprintf(stdout,
+ "0x00064: Command 7 register 0x%08X\n",
+ reg);
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "Interrupt Registers\n");
+ fprintf(stdout, "-------------------\n");
+
+ /* Interrupt 0 Register */
+ reg = reg_buff[8];
+ fprintf(stdout,
+ "0x00038: Interrupt register 0x%08X\n"
+ " Any interrupt is set: %s\n"
+ " Link change interrupt: %s\n"
+ " Register 0 auto-poll interrupt: %s\n"
+ " Transmit interrupt: %s\n"
+ " Software timer interrupt: %s\n"
+ " Receive interrupt: %s\n",
+ reg,
+ reg & INTR ? "Yes" : "No",
+ reg & LCINT ? "Yes" : "No",
+ reg & APINT0 ? "Yes" : "No",
+ reg & TINT0 ? "Yes" : "No",
+ reg & STINT ? "Yes" : "No",
+ reg & RINT0 ? "Yes" : "No"
+ );
+ /* Interrupt 0 enable Register */
+ reg = reg_buff[9];
+ fprintf(stdout,
+ "0x00040: Interrupt enable register 0x%08X\n"
+ " Link change interrupt: %s\n"
+ " Register 0 auto-poll interrupt: %s\n"
+ " Transmit interrupt: %s\n"
+ " Software timer interrupt: %s\n"
+ " Receive interrupt: %s\n",
+ reg,
+ reg & LCINTEN ? "Enabled" : "Disabled",
+ reg & APINT0EN ? "Enabled" : "Disabled",
+ reg & TINTEN0 ? "Enabled" : "Disabled",
+ reg & STINTEN ? "Enabled" : "Disabled",
+ reg & RINTEN0 ? "Enabled" : "Disabled"
+ );
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "Logical Address Filter Register\n");
+ fprintf(stdout, "-------------------\n");
+
+ /* Logical Address Filter Register */
+ fprintf(stdout,
+ "0x00168: Logical address filter register 0x%08X%08X\n",
+ reg_buff[11],reg_buff[10]);
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "Link status Register\n");
+ fprintf(stdout, "-------------------\n");
+
+ /* Status 0 Register */
+ reg = reg_buff[12];
+ if(reg & LINK_STATS){
+ fprintf(stdout,
+ "0x00030: Link status register 0x%08X\n"
+ " Link status: %s\n"
+ " Auto negotiation complete %s\n"
+ " Duplex %s\n"
+ " Speed %s\n",
+ reg,
+ reg & LINK_STATS ? "Valid" : "Invalid",
+ reg & AUTONEG_COMPLETE ? "Yes" : "No",
+ reg & FULL_DPLX ? "Full" : "Half",
+ ((reg & SPEED_MASK) >> 7 == PHY_SPEED_10) ? "10Mbits/ Sec":
+ "100Mbits/Sec");
+
+ }
+ else{
+ fprintf(stdout,
+ "0x00030: Link status register 0x%08X\n"
+ " Link status: %s\n",
+ reg,
+ reg & LINK_STATS ? "Valid" : "Invalid");
+ }
+ return 0;
+
+}
diff --git a/at76c50x-usb.c b/at76c50x-usb.c
new file mode 100644
index 0000000..fad41bf
--- /dev/null
+++ b/at76c50x-usb.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include "internal.h"
+
+static char *hw_versions[] = {
+ "503_ISL3861",
+ "503_ISL3863",
+ " 503",
+ " 503_ACC",
+ " 505",
+ " 505_2958",
+ " 505A",
+ " 505AMX",
+};
+
+int at76c50x_usb_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u8 version = (u8)(regs->version >> 24);
+ u8 rev_id = (u8)(regs->version);
+ char *ver_string;
+
+ if (version != 0)
+ return -1;
+
+ ver_string = hw_versions[rev_id];
+ fprintf(stdout,
+ "Hardware Version %s\n",
+ ver_string);
+
+ return 0;
+}
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..9f98ef8
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# You need autoconf 2.5x, preferably 2.57 or later
+# You need automake 1.7 or later. 1.6 might work.
+
+set -e
+
+aclocal
+autoheader
+automake --gnu --add-missing --copy
+autoconf
diff --git a/bnxt.c b/bnxt.c
new file mode 100644
index 0000000..2b0ac76
--- /dev/null
+++ b/bnxt.c
@@ -0,0 +1,97 @@
+/* Code to dump registers for NetXtreme-E/NetXtreme-C Broadcom devices.
+ *
+ * Copyright (c) 2020 Broadcom Inc.
+ */
+#include <stdio.h>
+#include "internal.h"
+
+#define BNXT_PXP_REG_LEN 0x3110
+#define BNXT_PCIE_STATS_LEN (12 * sizeof(u64))
+
+struct bnxt_pcie_stat {
+ const char *name;
+ u16 offset;
+ u8 size;
+ const char *format;
+};
+
+static const struct bnxt_pcie_stat bnxt_pcie_stats[] = {
+ { .name = "PL Signal integrity errors", .offset = 0, .size = 4, .format = "%llu" },
+ { .name = "DL Signal integrity errors", .offset = 4, .size = 4, .format = "%llu" },
+ { .name = "TLP Signal integrity errors", .offset = 8, .size = 4, .format = "%llu" },
+ { .name = "Link integrity", .offset = 12, .size = 4, .format = "%llu" },
+ { .name = "TX TLP traffic rate", .offset = 16, .size = 4, .format = "%llu" },
+ { .name = "RX TLP traffic rate", .offset = 20, .size = 4, .format = "%llu" },
+ { .name = "TX DLLP traffic rate", .offset = 24, .size = 4, .format = "%llu" },
+ { .name = "RX DLLP traffic rate", .offset = 28, .size = 4, .format = "%llu" },
+ { .name = "Equalization Phase 0 time(ms)", .offset = 33, .size = 1, .format = "0x%x" },
+ { .name = "Equalization Phase 1 time(ms)", .offset = 32, .size = 1, .format = "0x%x" },
+ { .name = "Equalization Phase 2 time(ms)", .offset = 35, .size = 1, .format = "0x%x" },
+ { .name = "Equalization Phase 3 time(ms)", .offset = 34, .size = 1, .format = "0x%x" },
+ { .name = "PHY LTSSM Histogram 0", .offset = 36, .size = 2, .format = "0x%x"},
+ { .name = "PHY LTSSM Histogram 1", .offset = 38, .size = 2, .format = "0x%x"},
+ { .name = "PHY LTSSM Histogram 2", .offset = 40, .size = 2, .format = "0x%x"},
+ { .name = "PHY LTSSM Histogram 3", .offset = 42, .size = 2, .format = "0x%x"},
+ { .name = "Recovery Histogram 0", .offset = 44, .size = 2, .format = "0x%x"},
+ { .name = "Recovery Histogram 1", .offset = 46, .size = 2, .format = "0x%x"},
+};
+
+int bnxt_dump_regs(struct ethtool_drvinfo *info __maybe_unused, struct ethtool_regs *regs)
+{
+ const struct bnxt_pcie_stat *stats = bnxt_pcie_stats;
+ u16 *pcie_stats, pcie_stat16;
+ u32 reg, i, pcie_stat32;
+ u64 pcie_stat64;
+
+ if (regs->len < BNXT_PXP_REG_LEN) {
+ fprintf(stdout, "Length too short, expected at least 0x%x\n",
+ BNXT_PXP_REG_LEN);
+ return -1;
+ }
+
+ fprintf(stdout, "PXP Registers\n");
+ fprintf(stdout, "Offset\tValue\n");
+ fprintf(stdout, "------\t-------\n");
+ for (i = 0; i < BNXT_PXP_REG_LEN; i += sizeof(reg)) {
+ memcpy(&reg, &regs->data[i], sizeof(reg));
+ if (reg)
+ fprintf(stdout, "0x%04x\t0x%08x\n", i, reg);
+ }
+ fprintf(stdout, "\n");
+
+ if (!regs->version)
+ return 0;
+
+ if (regs->len < (BNXT_PXP_REG_LEN + BNXT_PCIE_STATS_LEN)) {
+ fprintf(stdout, "Length is too short, expected 0x%zx\n",
+ BNXT_PXP_REG_LEN + BNXT_PCIE_STATS_LEN);
+ return -1;
+ }
+
+ pcie_stats = (u16 *)(regs->data + BNXT_PXP_REG_LEN);
+ fprintf(stdout, "PCIe statistics:\n");
+ fprintf(stdout, "----------------\n");
+ for (i = 0; i < ARRAY_SIZE(bnxt_pcie_stats); i++) {
+ fprintf(stdout, "%-30s : ", stats[i].name);
+ switch (stats[i].size) {
+ case 1:
+ pcie_stat16 = 0;
+ memcpy(&pcie_stat16, &pcie_stats[stats[i].offset], sizeof(u16));
+ fprintf(stdout, stats[i].format, pcie_stat16);
+ break;
+ case 2:
+ pcie_stat32 = 0;
+ memcpy(&pcie_stat32, &pcie_stats[stats[i].offset], sizeof(u32));
+ fprintf(stdout, stats[i].format, pcie_stat32);
+ break;
+ case 4:
+ pcie_stat64 = 0;
+ memcpy(&pcie_stat64, &pcie_stats[stats[i].offset], sizeof(u64));
+ fprintf(stdout, stats[i].format, pcie_stat64);
+ break;
+ }
+ fprintf(stdout, "\n");
+ }
+
+ return 0;
+}
diff --git a/cmis.c b/cmis.c
new file mode 100644
index 0000000..531932e
--- /dev/null
+++ b/cmis.c
@@ -0,0 +1,1041 @@
+/**
+ * Description:
+ *
+ * This module adds CMIS support to ethtool. The changes are similar to
+ * the ones already existing in qsfp.c, but customized to use the memory
+ * addresses and logic as defined in the specification's document.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <errno.h>
+#include "internal.h"
+#include "sff-common.h"
+#include "cmis.h"
+#include "netlink/extapi.h"
+
+/* The maximum number of supported Banks. Relevant documents:
+ * [1] CMIS Rev. 5, page. 128, section 8.4.4, Table 8-40
+ */
+#define CMIS_MAX_BANKS 4
+#define CMIS_CHANNELS_PER_BANK 8
+#define CMIS_MAX_CHANNEL_NUM (CMIS_MAX_BANKS * CMIS_CHANNELS_PER_BANK)
+
+/* We are not parsing further than Page 11h. */
+#define CMIS_MAX_PAGES 18
+
+struct cmis_memory_map {
+ const __u8 *lower_memory;
+ const __u8 *upper_memory[CMIS_MAX_BANKS][CMIS_MAX_PAGES];
+#define page_00h upper_memory[0x0][0x0]
+#define page_01h upper_memory[0x0][0x1]
+#define page_02h upper_memory[0x0][0x2]
+};
+
+#define CMIS_PAGE_SIZE 0x80
+#define CMIS_I2C_ADDRESS 0x50
+
+static struct {
+ const char *str;
+ int offset;
+ __u8 value; /* Alarm is on if (offset & value) != 0. */
+} cmis_aw_mod_flags[] = {
+ { "Module temperature high alarm",
+ CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HALARM_STATUS },
+ { "Module temperature low alarm",
+ CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LALARM_STATUS },
+ { "Module temperature high warning",
+ CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HWARN_STATUS },
+ { "Module temperature low warning",
+ CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LWARN_STATUS },
+
+ { "Module voltage high alarm",
+ CMIS_VCC_AW_OFFSET, CMIS_VCC_HALARM_STATUS },
+ { "Module voltage low alarm",
+ CMIS_VCC_AW_OFFSET, CMIS_VCC_LALARM_STATUS },
+ { "Module voltage high warning",
+ CMIS_VCC_AW_OFFSET, CMIS_VCC_HWARN_STATUS },
+ { "Module voltage low warning",
+ CMIS_VCC_AW_OFFSET, CMIS_VCC_LWARN_STATUS },
+
+ { NULL, 0, 0 },
+};
+
+static struct {
+ const char *fmt_str;
+ int offset;
+ int adver_offset; /* In Page 01h. */
+ __u8 adver_value; /* Supported if (offset & value) != 0. */
+} cmis_aw_chan_flags[] = {
+ { "Laser bias current high alarm (Chan %d)",
+ CMIS_TX_BIAS_AW_HALARM_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
+ { "Laser bias current low alarm (Chan %d)",
+ CMIS_TX_BIAS_AW_LALARM_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
+ { "Laser bias current high warning (Chan %d)",
+ CMIS_TX_BIAS_AW_HWARN_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
+ { "Laser bias current low warning (Chan %d)",
+ CMIS_TX_BIAS_AW_LWARN_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
+
+ { "Laser tx power high alarm (Channel %d)",
+ CMIS_TX_PWR_AW_HALARM_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
+ { "Laser tx power low alarm (Channel %d)",
+ CMIS_TX_PWR_AW_LALARM_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
+ { "Laser tx power high warning (Channel %d)",
+ CMIS_TX_PWR_AW_HWARN_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
+ { "Laser tx power low warning (Channel %d)",
+ CMIS_TX_PWR_AW_LWARN_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
+
+ { "Laser rx power high alarm (Channel %d)",
+ CMIS_RX_PWR_AW_HALARM_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
+ { "Laser rx power low alarm (Channel %d)",
+ CMIS_RX_PWR_AW_LALARM_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
+ { "Laser rx power high warning (Channel %d)",
+ CMIS_RX_PWR_AW_HWARN_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
+ { "Laser rx power low warning (Channel %d)",
+ CMIS_RX_PWR_AW_LWARN_OFFSET,
+ CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
+
+ { NULL, 0, 0, 0 },
+};
+
+static void cmis_show_identifier(const struct cmis_memory_map *map)
+{
+ sff8024_show_identifier(map->lower_memory, CMIS_ID_OFFSET);
+}
+
+static void cmis_show_connector(const struct cmis_memory_map *map)
+{
+ sff8024_show_connector(map->page_00h, CMIS_CTOR_OFFSET);
+}
+
+static void cmis_show_oui(const struct cmis_memory_map *map)
+{
+ sff8024_show_oui(map->page_00h, CMIS_VENDOR_OUI_OFFSET);
+}
+
+/**
+ * Print the revision compliance. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18
+ * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2
+ */
+static void cmis_show_rev_compliance(const struct cmis_memory_map *map)
+{
+ __u8 rev = map->lower_memory[CMIS_REV_COMPLIANCE_OFFSET];
+ int major = (rev >> 4) & 0x0F;
+ int minor = rev & 0x0F;
+
+ printf("\t%-41s : Rev. %d.%d\n", "Revision compliance", major, minor);
+}
+
+static void
+cmis_show_signals_one(const struct cmis_memory_map *map, const char *name,
+ int off, int ioff, unsigned int imask)
+{
+ unsigned int v;
+ int i;
+
+ if (!map->page_01h)
+ return;
+
+ v = 0;
+ for (i = 0; i < CMIS_MAX_BANKS && map->upper_memory[i][0x11]; i++)
+ v |= map->upper_memory[i][0x11][off] << (i * 8);
+
+ if (map->page_01h[ioff] & imask)
+ sff_show_lane_status(name, i * 8, "Yes", "No", v);
+}
+
+static void cmis_show_signals(const struct cmis_memory_map *map)
+{
+ cmis_show_signals_one(map, "Rx loss of signal", CMIS_RX_LOS_OFFSET,
+ CMIS_DIAG_FLAGS_RX_OFFSET, CMIS_DIAG_FL_RX_LOS);
+ cmis_show_signals_one(map, "Tx loss of signal", CMIS_TX_LOS_OFFSET,
+ CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_LOS);
+
+ cmis_show_signals_one(map, "Rx loss of lock", CMIS_RX_LOL_OFFSET,
+ CMIS_DIAG_FLAGS_RX_OFFSET, CMIS_DIAG_FL_RX_LOL);
+ cmis_show_signals_one(map, "Tx loss of lock", CMIS_TX_LOL_OFFSET,
+ CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_LOL);
+
+ cmis_show_signals_one(map, "Tx fault", CMIS_TX_FAIL_OFFSET,
+ CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_FAIL);
+
+ cmis_show_signals_one(map, "Tx adaptive eq fault",
+ CMIS_TX_EQ_FAIL_OFFSET, CMIS_DIAG_FLAGS_TX_OFFSET,
+ CMIS_DIAG_FL_TX_ADAPTIVE_EQ_FAIL);
+}
+
+/**
+ * Print information about the device's power consumption.
+ * Relevant documents:
+ * [1] CMIS Rev. 3, pag. 59, section 1.7.3.9, Table 30
+ * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18
+ * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1
+ */
+static void cmis_show_power_info(const struct cmis_memory_map *map)
+{
+ float max_power = 0.0f;
+ __u8 base_power = 0;
+ __u8 power_class;
+
+ /* Get the power class (first 3 most significat bytes) */
+ power_class = (map->page_00h[CMIS_PWR_CLASS_OFFSET] >> 5) & 0x07;
+
+ /* Get the base power in multiples of 0.25W */
+ base_power = map->page_00h[CMIS_PWR_MAX_POWER_OFFSET];
+ max_power = base_power * 0.25f;
+
+ printf("\t%-41s : %d\n", "Power class", power_class + 1);
+ printf("\t%-41s : %.02fW\n", "Max power", max_power);
+}
+
+/**
+ * Print the cable assembly length, for both passive copper and active
+ * optical or electrical cables. The base length (bits 5-0) must be
+ * multiplied with the SMF length multiplier (bits 7-6) to obtain the
+ * correct value. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31
+ * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19
+ */
+static void cmis_show_cbl_asm_len(const struct cmis_memory_map *map)
+{
+ static const char *fn = "Cable assembly length";
+ float mul = 1.0f;
+ float val = 0.0f;
+
+ /* Check if max length */
+ if (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] == CMIS_6300M_MAX_LEN) {
+ printf("\t%-41s : > 6.3km\n", fn);
+ return;
+ }
+
+ /* Get the multiplier from the first two bits */
+ switch (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
+ case CMIS_MULTIPLIER_00:
+ mul = 0.1f;
+ break;
+ case CMIS_MULTIPLIER_01:
+ mul = 1.0f;
+ break;
+ case CMIS_MULTIPLIER_10:
+ mul = 10.0f;
+ break;
+ case CMIS_MULTIPLIER_11:
+ mul = 100.0f;
+ break;
+ default:
+ break;
+ }
+
+ /* Get base value from first 6 bits and multiply by mul */
+ val = (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
+ val = (float)val * mul;
+ printf("\t%-41s : %0.2fm\n", fn, val);
+}
+
+/**
+ * Print the length for SMF fiber. The base length (bits 5-0) must be
+ * multiplied with the SMF length multiplier (bits 7-6) to obtain the
+ * correct value. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39
+ * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27
+ */
+static void cmis_print_smf_cbl_len(const struct cmis_memory_map *map)
+{
+ static const char *fn = "Length (SMF)";
+ float mul = 1.0f;
+ float val = 0.0f;
+
+ if (!map->page_01h)
+ return;
+
+ /* Get the multiplier from the first two bits */
+ switch (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
+ case CMIS_MULTIPLIER_00:
+ mul = 0.1f;
+ break;
+ case CMIS_MULTIPLIER_01:
+ mul = 1.0f;
+ break;
+ default:
+ break;
+ }
+
+ /* Get base value from first 6 bits and multiply by mul */
+ val = (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
+ val = (float)val * mul;
+ printf("\t%-41s : %0.2fkm\n", fn, val);
+}
+
+/**
+ * Print relevant signal integrity control properties. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46
+ * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34
+ */
+static void cmis_show_sig_integrity(const struct cmis_memory_map *map)
+{
+ if (!map->page_01h)
+ return;
+
+ /* CDR Bypass control: 2nd bit from each byte */
+ printf("\t%-41s : ", "Tx CDR bypass control");
+ printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x02));
+
+ printf("\t%-41s : ", "Rx CDR bypass control");
+ printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x02));
+
+ /* CDR Implementation: 1st bit from each byte */
+ printf("\t%-41s : ", "Tx CDR");
+ printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x01));
+
+ printf("\t%-41s : ", "Rx CDR");
+ printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x01));
+}
+
+/**
+ * Print relevant media interface technology info. Relevant documents:
+ * [1] CMIS Rev. 3
+ * --> pag. 61, section 1.7.3.14, Table 36
+ * --> pag. 64, section 1.7.4.3, 1.7.4.4
+ * [2] CMIS Rev. 4
+ * --> pag. 97, section 8.3.14, Table 8-24
+ * --> pag. 98, section 8.4, Table 8-25
+ * --> page 100, section 8.4.3, 8.4.4
+ */
+static void cmis_show_mit_compliance(const struct cmis_memory_map *map)
+{
+ static const char *cc = " (Copper cable,";
+
+ printf("\t%-41s : 0x%02x", "Transmitter technology",
+ map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]);
+
+ switch (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]) {
+ case CMIS_850_VCSEL:
+ printf(" (850 nm VCSEL)\n");
+ break;
+ case CMIS_1310_VCSEL:
+ printf(" (1310 nm VCSEL)\n");
+ break;
+ case CMIS_1550_VCSEL:
+ printf(" (1550 nm VCSEL)\n");
+ break;
+ case CMIS_1310_FP:
+ printf(" (1310 nm FP)\n");
+ break;
+ case CMIS_1310_DFB:
+ printf(" (1310 nm DFB)\n");
+ break;
+ case CMIS_1550_DFB:
+ printf(" (1550 nm DFB)\n");
+ break;
+ case CMIS_1310_EML:
+ printf(" (1310 nm EML)\n");
+ break;
+ case CMIS_1550_EML:
+ printf(" (1550 nm EML)\n");
+ break;
+ case CMIS_OTHERS:
+ printf(" (Others/Undefined)\n");
+ break;
+ case CMIS_1490_DFB:
+ printf(" (1490 nm DFB)\n");
+ break;
+ case CMIS_COPPER_UNEQUAL:
+ printf("%s unequalized)\n", cc);
+ break;
+ case CMIS_COPPER_PASS_EQUAL:
+ printf("%s passive equalized)\n", cc);
+ break;
+ case CMIS_COPPER_NF_EQUAL:
+ printf("%s near and far end limiting active equalizers)\n", cc);
+ break;
+ case CMIS_COPPER_F_EQUAL:
+ printf("%s far end limiting active equalizers)\n", cc);
+ break;
+ case CMIS_COPPER_N_EQUAL:
+ printf("%s near end limiting active equalizers)\n", cc);
+ break;
+ case CMIS_COPPER_LINEAR_EQUAL:
+ printf("%s linear active equalizers)\n", cc);
+ break;
+ }
+
+ if (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET] >= CMIS_COPPER_UNEQUAL) {
+ printf("\t%-41s : %udb\n", "Attenuation at 5GHz",
+ map->page_00h[CMIS_COPPER_ATT_5GHZ]);
+ printf("\t%-41s : %udb\n", "Attenuation at 7GHz",
+ map->page_00h[CMIS_COPPER_ATT_7GHZ]);
+ printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
+ map->page_00h[CMIS_COPPER_ATT_12P9GHZ]);
+ printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz",
+ map->page_00h[CMIS_COPPER_ATT_25P8GHZ]);
+ } else if (map->page_01h) {
+ printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
+ (((map->page_01h[CMIS_NOM_WAVELENGTH_MSB] << 8) |
+ map->page_01h[CMIS_NOM_WAVELENGTH_LSB]) * 0.05));
+ printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
+ (((map->page_01h[CMIS_WAVELENGTH_TOL_MSB] << 8) |
+ map->page_01h[CMIS_WAVELENGTH_TOL_LSB]) * 0.005));
+ }
+}
+
+/**
+ * Print relevant info about the maximum supported fiber media length
+ * for each type of fiber media at the maximum module-supported bit rate.
+ * Relevant documents:
+ * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39
+ * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27
+ */
+static void cmis_show_link_len(const struct cmis_memory_map *map)
+{
+ cmis_print_smf_cbl_len(map);
+ if (!map->page_01h)
+ return;
+ sff_show_value_with_unit(map->page_01h, CMIS_OM5_LEN_OFFSET,
+ "Length (OM5)", 2, "m");
+ sff_show_value_with_unit(map->page_01h, CMIS_OM4_LEN_OFFSET,
+ "Length (OM4)", 2, "m");
+ sff_show_value_with_unit(map->page_01h, CMIS_OM3_LEN_OFFSET,
+ "Length (OM3 50/125um)", 2, "m");
+ sff_show_value_with_unit(map->page_01h, CMIS_OM2_LEN_OFFSET,
+ "Length (OM2 50/125um)", 1, "m");
+}
+
+/**
+ * Show relevant information about the vendor. Relevant documents:
+ * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27
+ * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15
+ */
+static void cmis_show_vendor_info(const struct cmis_memory_map *map)
+{
+ const char *clei;
+
+ sff_show_ascii(map->page_00h, CMIS_VENDOR_NAME_START_OFFSET,
+ CMIS_VENDOR_NAME_END_OFFSET, "Vendor name");
+ cmis_show_oui(map);
+ sff_show_ascii(map->page_00h, CMIS_VENDOR_PN_START_OFFSET,
+ CMIS_VENDOR_PN_END_OFFSET, "Vendor PN");
+ sff_show_ascii(map->page_00h, CMIS_VENDOR_REV_START_OFFSET,
+ CMIS_VENDOR_REV_END_OFFSET, "Vendor rev");
+ sff_show_ascii(map->page_00h, CMIS_VENDOR_SN_START_OFFSET,
+ CMIS_VENDOR_SN_END_OFFSET, "Vendor SN");
+ sff_show_ascii(map->page_00h, CMIS_DATE_YEAR_OFFSET,
+ CMIS_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+
+ clei = (const char *)(map->page_00h + CMIS_CLEI_START_OFFSET);
+ if (*clei && strncmp(clei, CMIS_CLEI_BLANK, CMIS_CLEI_LEN))
+ sff_show_ascii(map->page_00h, CMIS_CLEI_START_OFFSET,
+ CMIS_CLEI_END_OFFSET, "CLEI code");
+}
+
+/* Print the current Module State. Relevant documents:
+ * [1] CMIS Rev. 5, pag. 57, section 6.3.2.2, Figure 6-3
+ * [2] CMIS Rev. 5, pag. 60, section 6.3.2.3, Figure 6-4
+ * [3] CMIS Rev. 5, pag. 107, section 8.2.2, Table 8-6
+ */
+static void cmis_show_mod_state(const struct cmis_memory_map *map)
+{
+ __u8 mod_state;
+
+ mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] &
+ CMIS_MODULE_STATE_MASK) >> 1;
+ printf("\t%-41s : 0x%02x", "Module State", mod_state);
+ switch (mod_state) {
+ case CMIS_MODULE_STATE_MODULE_LOW_PWR:
+ printf(" (ModuleLowPwr)\n");
+ break;
+ case CMIS_MODULE_STATE_MODULE_PWR_UP:
+ printf(" (ModulePwrUp)\n");
+ break;
+ case CMIS_MODULE_STATE_MODULE_READY:
+ printf(" (ModuleReady)\n");
+ break;
+ case CMIS_MODULE_STATE_MODULE_PWR_DN:
+ printf(" (ModulePwrDn)\n");
+ break;
+ case CMIS_MODULE_STATE_MODULE_FAULT:
+ printf(" (ModuleFault)\n");
+ break;
+ default:
+ printf(" (reserved or unknown)\n");
+ break;
+ }
+}
+
+/* Print the Module Fault Information. Relevant documents:
+ * [1] CMIS Rev. 5, pag. 64, section 6.3.2.12
+ * [2] CMIS Rev. 5, pag. 115, section 8.2.10, Table 8-15
+ */
+static void cmis_show_mod_fault_cause(const struct cmis_memory_map *map)
+{
+ __u8 mod_state, fault_cause;
+
+ mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] &
+ CMIS_MODULE_STATE_MASK) >> 1;
+ if (mod_state != CMIS_MODULE_STATE_MODULE_FAULT)
+ return;
+
+ fault_cause = map->lower_memory[CMIS_MODULE_FAULT_OFFSET];
+ printf("\t%-41s : 0x%02x", "Module Fault Cause", fault_cause);
+ switch (fault_cause) {
+ case CMIS_MODULE_FAULT_NO_FAULT:
+ printf(" (No fault detected / not supported)\n");
+ break;
+ case CMIS_MODULE_FAULT_TEC_RUNAWAY:
+ printf(" (TEC runaway)\n");
+ break;
+ case CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED:
+ printf(" (Data memory corrupted)\n");
+ break;
+ case CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED:
+ printf(" (Program memory corrupted)\n");
+ break;
+ default:
+ printf(" (reserved or unknown)\n");
+ break;
+ }
+}
+
+/* Print the current Module-Level Controls. Relevant documents:
+ * [1] CMIS Rev. 5, pag. 58, section 6.3.2.2, Table 6-12
+ * [2] CMIS Rev. 5, pag. 111, section 8.2.6, Table 8-10
+ */
+static void cmis_show_mod_lvl_controls(const struct cmis_memory_map *map)
+{
+ printf("\t%-41s : ", "LowPwrAllowRequestHW");
+ printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] &
+ CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK));
+ printf("\t%-41s : ", "LowPwrRequestSW");
+ printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] &
+ CMIS_LOW_PWR_REQUEST_SW_MASK));
+}
+
+static void cmis_parse_dom_power_type(const struct cmis_memory_map *map,
+ struct sff_diags *sd)
+{
+ sd->rx_power_type = map->page_01h[CMIS_DIAG_TYPE_OFFSET] &
+ CMIS_RX_PWR_TYPE_MASK;
+ sd->tx_power_type = map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] &
+ CMIS_TX_PWR_MON_MASK;
+}
+
+static void cmis_parse_dom_mod_lvl_monitors(const struct cmis_memory_map *map,
+ struct sff_diags *sd)
+{
+ sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(map->lower_memory,
+ CMIS_CURR_VCC_OFFSET);
+ sd->sfp_temp[MCURR] = (__s16)OFFSET_TO_U16_PTR(map->lower_memory,
+ CMIS_CURR_TEMP_OFFSET);
+}
+
+static void cmis_parse_dom_mod_lvl_thresh(const struct cmis_memory_map *map,
+ struct sff_diags *sd)
+{
+ /* Page is not present in IOCTL path. */
+ if (!map->page_02h)
+ return;
+ sd->supports_alarms = 1;
+
+ sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_VCC_HALRM_OFFSET);
+ sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_VCC_LALRM_OFFSET);
+ sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_VCC_HWARN_OFFSET);
+ sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_VCC_LWARN_OFFSET);
+
+ sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TEMP_HALRM_OFFSET);
+ sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TEMP_LALRM_OFFSET);
+ sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TEMP_HWARN_OFFSET);
+ sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TEMP_LWARN_OFFSET);
+}
+
+static __u8 cmis_tx_bias_mul(const struct cmis_memory_map *map)
+{
+ switch (map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] &
+ CMIS_TX_BIAS_MUL_MASK) {
+ case CMIS_TX_BIAS_MUL_1:
+ return 0;
+ case CMIS_TX_BIAS_MUL_2:
+ return 1;
+ case CMIS_TX_BIAS_MUL_4:
+ return 2;
+ }
+
+ return 0;
+}
+
+static void
+cmis_parse_dom_chan_lvl_monitors_bank(const struct cmis_memory_map *map,
+ struct sff_diags *sd, int bank)
+{
+ const __u8 *page_11h = map->upper_memory[bank][0x11];
+ int i;
+
+ if (!page_11h)
+ return;
+
+ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
+ __u8 tx_bias_offset, rx_power_offset, tx_power_offset;
+ int chan = bank * CMIS_CHANNELS_PER_BANK + i;
+ __u8 bias_mul = cmis_tx_bias_mul(map);
+
+ tx_bias_offset = CMIS_TX_BIAS_OFFSET + i * sizeof(__u16);
+ rx_power_offset = CMIS_RX_PWR_OFFSET + i * sizeof(__u16);
+ tx_power_offset = CMIS_TX_PWR_OFFSET + i * sizeof(__u16);
+
+ sd->scd[chan].bias_cur = OFFSET_TO_U16_PTR(page_11h,
+ tx_bias_offset);
+ sd->scd[chan].bias_cur >>= bias_mul;
+ sd->scd[chan].rx_power = OFFSET_TO_U16_PTR(page_11h,
+ rx_power_offset);
+ sd->scd[chan].tx_power = OFFSET_TO_U16_PTR(page_11h,
+ tx_power_offset);
+ }
+}
+
+static void cmis_parse_dom_chan_lvl_monitors(const struct cmis_memory_map *map,
+ struct sff_diags *sd)
+{
+ int i;
+
+ for (i = 0; i < CMIS_MAX_BANKS; i++)
+ cmis_parse_dom_chan_lvl_monitors_bank(map, sd, i);
+}
+
+static void cmis_parse_dom_chan_lvl_thresh(const struct cmis_memory_map *map,
+ struct sff_diags *sd)
+{
+ __u8 bias_mul = cmis_tx_bias_mul(map);
+
+ if (!map->page_02h)
+ return;
+
+ sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TX_BIAS_HALRM_OFFSET);
+ sd->bias_cur[HALRM] >>= bias_mul;
+ sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TX_BIAS_LALRM_OFFSET);
+ sd->bias_cur[LALRM] >>= bias_mul;
+ sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TX_BIAS_HWARN_OFFSET);
+ sd->bias_cur[HWARN] >>= bias_mul;
+ sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TX_BIAS_LWARN_OFFSET);
+ sd->bias_cur[LWARN] >>= bias_mul;
+
+ sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TX_PWR_HALRM_OFFSET);
+ sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TX_PWR_LALRM_OFFSET);
+ sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TX_PWR_HWARN_OFFSET);
+ sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_TX_PWR_LWARN_OFFSET);
+
+ sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_RX_PWR_HALRM_OFFSET);
+ sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_RX_PWR_LALRM_OFFSET);
+ sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_RX_PWR_HWARN_OFFSET);
+ sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
+ CMIS_RX_PWR_LWARN_OFFSET);
+}
+
+static void cmis_parse_dom(const struct cmis_memory_map *map,
+ struct sff_diags *sd)
+{
+ cmis_parse_dom_power_type(map, sd);
+ cmis_parse_dom_mod_lvl_monitors(map, sd);
+ cmis_parse_dom_mod_lvl_thresh(map, sd);
+ cmis_parse_dom_chan_lvl_monitors(map, sd);
+ cmis_parse_dom_chan_lvl_thresh(map, sd);
+}
+
+/* Print module-level monitoring values. Relevant documents:
+ * [1] CMIS Rev. 5, page 110, section 8.2.5, Table 8-9
+ */
+static void cmis_show_dom_mod_lvl_monitors(const struct sff_diags *sd)
+{
+ PRINT_TEMP("Module temperature", sd->sfp_temp[MCURR]);
+ PRINT_VCC("Module voltage", sd->sfp_voltage[MCURR]);
+}
+
+/* Print channel Tx laser bias current. Relevant documents:
+ * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79
+ */
+static void
+cmis_show_dom_chan_lvl_tx_bias_bank(const struct cmis_memory_map *map,
+ const struct sff_diags *sd, int bank)
+{
+ const __u8 *page_11h = map->upper_memory[bank][0x11];
+ int i;
+
+ if (!page_11h)
+ return;
+
+ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
+ int chan = bank * CMIS_CHANNELS_PER_BANK + i;
+ char fmt_str[80];
+
+ snprintf(fmt_str, 80, "%s (Channel %d)",
+ "Laser tx bias current", chan + 1);
+ PRINT_BIAS(fmt_str, sd->scd[chan].bias_cur);
+ }
+}
+
+static void cmis_show_dom_chan_lvl_tx_bias(const struct cmis_memory_map *map,
+ const struct sff_diags *sd)
+{
+ int i;
+
+ if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] &
+ CMIS_TX_BIAS_MON_MASK))
+ return;
+
+ for (i = 0; i < CMIS_MAX_BANKS; i++)
+ cmis_show_dom_chan_lvl_tx_bias_bank(map, sd, i);
+}
+
+/* Print channel Tx average optical power. Relevant documents:
+ * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79
+ */
+static void
+cmis_show_dom_chan_lvl_tx_power_bank(const struct cmis_memory_map *map,
+ const struct sff_diags *sd, int bank)
+{
+ const __u8 *page_11h = map->upper_memory[bank][0x11];
+ int i;
+
+ if (!page_11h)
+ return;
+
+ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
+ int chan = bank * CMIS_CHANNELS_PER_BANK + i;
+ char fmt_str[80];
+
+ snprintf(fmt_str, 80, "%s (Channel %d)",
+ "Transmit avg optical power", chan + 1);
+ PRINT_xX_PWR(fmt_str, sd->scd[chan].tx_power);
+ }
+}
+
+static void cmis_show_dom_chan_lvl_tx_power(const struct cmis_memory_map *map,
+ const struct sff_diags *sd)
+{
+ int i;
+
+ if (!sd->tx_power_type)
+ return;
+
+ for (i = 0; i < CMIS_MAX_BANKS; i++)
+ cmis_show_dom_chan_lvl_tx_power_bank(map, sd, i);
+}
+
+/* Print channel Rx input optical power. Relevant documents:
+ * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79
+ */
+static void
+cmis_show_dom_chan_lvl_rx_power_bank(const struct cmis_memory_map *map,
+ const struct sff_diags *sd, int bank)
+{
+ const __u8 *page_11h = map->upper_memory[bank][0x11];
+ int i;
+
+ if (!page_11h)
+ return;
+
+ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
+ int chan = bank * CMIS_CHANNELS_PER_BANK + i;
+ char *rx_power_str;
+ char fmt_str[80];
+
+ if (!sd->rx_power_type)
+ rx_power_str = "Receiver signal OMA";
+ else
+ rx_power_str = "Rcvr signal avg optical power";
+
+ snprintf(fmt_str, 80, "%s (Channel %d)", rx_power_str,
+ chan + 1);
+ PRINT_xX_PWR(fmt_str, sd->scd[chan].rx_power);
+ }
+}
+
+static void cmis_show_dom_chan_lvl_rx_power(const struct cmis_memory_map *map,
+ const struct sff_diags *sd)
+{
+ int i;
+
+ if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & CMIS_RX_PWR_MON_MASK))
+ return;
+
+ for (i = 0; i < CMIS_MAX_BANKS; i++)
+ cmis_show_dom_chan_lvl_rx_power_bank(map, sd, i);
+}
+
+static void cmis_show_dom_chan_lvl_monitors(const struct cmis_memory_map *map,
+ const struct sff_diags *sd)
+{
+ cmis_show_dom_chan_lvl_tx_bias(map, sd);
+ cmis_show_dom_chan_lvl_tx_power(map, sd);
+ cmis_show_dom_chan_lvl_rx_power(map, sd);
+}
+
+/* Print module-level flags. Relevant documents:
+ * [1] CMIS Rev. 5, page 109, section 8.2.4, Table 8-8
+ */
+static void cmis_show_dom_mod_lvl_flags(const struct cmis_memory_map *map)
+{
+ int i;
+
+ for (i = 0; cmis_aw_mod_flags[i].str; i++) {
+ printf("\t%-41s : %s\n", cmis_aw_mod_flags[i].str,
+ map->lower_memory[cmis_aw_mod_flags[i].offset] &
+ cmis_aw_mod_flags[i].value ? "On" : "Off");
+ }
+}
+
+/* Print channel-level flags. Relevant documents:
+ * [1] CMIS Rev. 5, page 162, section 8.9.3, Table 8-77
+ * [1] CMIS Rev. 5, page 164, section 8.9.3, Table 8-78
+ */
+static void cmis_show_dom_chan_lvl_flags_chan(const struct cmis_memory_map *map,
+ int bank, int chan)
+{
+ const __u8 *page_11h = map->upper_memory[bank][0x11];
+ int i;
+
+ for (i = 0; cmis_aw_chan_flags[i].fmt_str; i++) {
+ char str[80];
+
+ if (!(map->page_01h[cmis_aw_chan_flags[i].adver_offset] &
+ cmis_aw_chan_flags[i].adver_value))
+ continue;
+
+ snprintf(str, 80, cmis_aw_chan_flags[i].fmt_str, chan + 1);
+ printf("\t%-41s : %s\n", str,
+ page_11h[cmis_aw_chan_flags[i].offset] & chan ?
+ "On" : "Off");
+ }
+}
+
+static void
+cmis_show_dom_chan_lvl_flags_bank(const struct cmis_memory_map *map,
+ int bank)
+{
+ const __u8 *page_11h = map->upper_memory[bank][0x11];
+ int i;
+
+ if (!page_11h)
+ return;
+
+ for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
+ int chan = bank * CMIS_CHANNELS_PER_BANK + i;
+
+ cmis_show_dom_chan_lvl_flags_chan(map, bank, chan);
+ }
+}
+
+static void cmis_show_dom_chan_lvl_flags(const struct cmis_memory_map *map)
+{
+ int i;
+
+ for (i = 0; i < CMIS_MAX_BANKS; i++)
+ cmis_show_dom_chan_lvl_flags_bank(map, i);
+}
+
+
+static void cmis_show_dom(const struct cmis_memory_map *map)
+{
+ struct sff_diags sd = {};
+
+ /* Diagnostic information is only relevant when the module memory
+ * model is paged and not flat.
+ */
+ if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
+ CMIS_MEMORY_MODEL_MASK)
+ return;
+
+ cmis_parse_dom(map, &sd);
+
+ cmis_show_dom_mod_lvl_monitors(&sd);
+ cmis_show_dom_chan_lvl_monitors(map, &sd);
+ cmis_show_dom_mod_lvl_flags(map);
+ cmis_show_dom_chan_lvl_flags(map);
+ if (sd.supports_alarms)
+ sff_show_thresholds(sd);
+}
+
+static void cmis_show_all_common(const struct cmis_memory_map *map)
+{
+ cmis_show_identifier(map);
+ cmis_show_power_info(map);
+ cmis_show_connector(map);
+ cmis_show_cbl_asm_len(map);
+ cmis_show_sig_integrity(map);
+ cmis_show_mit_compliance(map);
+ cmis_show_link_len(map);
+ cmis_show_vendor_info(map);
+ cmis_show_rev_compliance(map);
+ cmis_show_signals(map);
+ cmis_show_mod_state(map);
+ cmis_show_mod_fault_cause(map);
+ cmis_show_mod_lvl_controls(map);
+ cmis_show_dom(map);
+}
+
+static void cmis_memory_map_init_buf(struct cmis_memory_map *map,
+ const __u8 *id)
+{
+ /* Lower Memory and Page 00h are always present.
+ *
+ * Offset into Upper Memory is between page size and twice the page
+ * size. Therefore, set the base address of each page to base address
+ * plus page size multiplied by the page number.
+ */
+ map->lower_memory = id;
+ map->page_00h = id;
+
+ /* Page 01h is only present when the module memory model is paged and
+ * not flat.
+ */
+ if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
+ CMIS_MEMORY_MODEL_MASK)
+ return;
+
+ map->page_01h = id + CMIS_PAGE_SIZE;
+}
+
+void cmis_show_all_ioctl(const __u8 *id)
+{
+ struct cmis_memory_map map = {};
+
+ cmis_memory_map_init_buf(&map, id);
+ cmis_show_all_common(&map);
+}
+
+static void cmis_request_init(struct ethtool_module_eeprom *request, u8 bank,
+ u8 page, u32 offset)
+{
+ request->offset = offset;
+ request->length = CMIS_PAGE_SIZE;
+ request->page = page;
+ request->bank = bank;
+ request->i2c_address = CMIS_I2C_ADDRESS;
+ request->data = NULL;
+}
+
+static int cmis_num_banks_get(const struct cmis_memory_map *map,
+ int *p_num_banks)
+{
+ switch (map->page_01h[CMIS_PAGES_ADVER_OFFSET] &
+ CMIS_BANKS_SUPPORTED_MASK) {
+ case CMIS_BANK_0_SUPPORTED:
+ *p_num_banks = 1;
+ break;
+ case CMIS_BANK_0_1_SUPPORTED:
+ *p_num_banks = 2;
+ break;
+ case CMIS_BANK_0_3_SUPPORTED:
+ *p_num_banks = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+cmis_memory_map_init_pages(struct cmd_context *ctx,
+ struct cmis_memory_map *map)
+{
+ struct ethtool_module_eeprom request;
+ int num_banks, i, ret;
+
+ /* Lower Memory and Page 00h are always present.
+ *
+ * Offset into Upper Memory is between page size and twice the page
+ * size. Therefore, set the base address of each page to its base
+ * address minus page size.
+ */
+ cmis_request_init(&request, 0, 0x0, 0);
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+ map->lower_memory = request.data;
+
+ cmis_request_init(&request, 0, 0x0, CMIS_PAGE_SIZE);
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+ map->page_00h = request.data - CMIS_PAGE_SIZE;
+
+ /* Pages 01h and 02h are only present when the module memory model is
+ * paged and not flat.
+ */
+ if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
+ CMIS_MEMORY_MODEL_MASK)
+ return 0;
+
+ cmis_request_init(&request, 0, 0x1, CMIS_PAGE_SIZE);
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+ map->page_01h = request.data - CMIS_PAGE_SIZE;
+
+ cmis_request_init(&request, 0, 0x2, CMIS_PAGE_SIZE);
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+ map->page_02h = request.data - CMIS_PAGE_SIZE;
+
+ /* Bank 0 of Page 11h provides lane-specific registers for the first 8
+ * lanes, and each additional Banks provides support for an additional
+ * 8 lanes. Only initialize supported Banks.
+ */
+ ret = cmis_num_banks_get(map, &num_banks);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < num_banks; i++) {
+ cmis_request_init(&request, i, 0x11, CMIS_PAGE_SIZE);
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+ map->upper_memory[i][0x11] = request.data - CMIS_PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+int cmis_show_all_nl(struct cmd_context *ctx)
+{
+ struct cmis_memory_map map = {};
+ int ret;
+
+ ret = cmis_memory_map_init_pages(ctx, &map);
+ if (ret < 0)
+ return ret;
+ cmis_show_all_common(&map);
+
+ return 0;
+}
diff --git a/cmis.h b/cmis.h
new file mode 100644
index 0000000..8d66f92
--- /dev/null
+++ b/cmis.h
@@ -0,0 +1,252 @@
+#ifndef CMIS_H__
+#define CMIS_H__
+
+/* Identifier and revision compliance (Page 0) */
+#define CMIS_ID_OFFSET 0x00
+#define CMIS_REV_COMPLIANCE_OFFSET 0x01
+#define CMIS_MEMORY_MODEL_OFFSET 0x02
+#define CMIS_MEMORY_MODEL_MASK 0x80
+
+/* Global Status Information (Page 0) */
+#define CMIS_MODULE_STATE_OFFSET 0x03
+#define CMIS_MODULE_STATE_MASK 0x0E
+#define CMIS_MODULE_STATE_MODULE_LOW_PWR 0x01
+#define CMIS_MODULE_STATE_MODULE_PWR_UP 0x02
+#define CMIS_MODULE_STATE_MODULE_READY 0x03
+#define CMIS_MODULE_STATE_MODULE_PWR_DN 0x04
+#define CMIS_MODULE_STATE_MODULE_FAULT 0x05
+
+/* Module Flags (Page 0) */
+#define CMIS_VCC_AW_OFFSET 0x09
+#define CMIS_VCC_LWARN_STATUS 0x80
+#define CMIS_VCC_HWARN_STATUS 0x40
+#define CMIS_VCC_LALARM_STATUS 0x20
+#define CMIS_VCC_HALARM_STATUS 0x10
+#define CMIS_TEMP_AW_OFFSET 0x09
+#define CMIS_TEMP_LWARN_STATUS 0x08
+#define CMIS_TEMP_HWARN_STATUS 0x04
+#define CMIS_TEMP_LALARM_STATUS 0x02
+#define CMIS_TEMP_HALARM_STATUS 0x01
+
+#define CMIS_MODULE_TYPE_OFFSET 0x55
+#define CMIS_MT_MMF 0x01
+#define CMIS_MT_SMF 0x02
+
+/* Module-Level Monitors (Page 0) */
+#define CMIS_CURR_TEMP_OFFSET 0x0E
+#define CMIS_CURR_VCC_OFFSET 0x10
+
+/* Module Global Controls (Page 0) */
+#define CMIS_MODULE_CONTROL_OFFSET 0x1A
+#define CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK 0x40
+#define CMIS_LOW_PWR_REQUEST_SW_MASK 0x10
+
+/* Module Fault Information (Page 0) */
+#define CMIS_MODULE_FAULT_OFFSET 0x29
+#define CMIS_MODULE_FAULT_NO_FAULT 0x00
+#define CMIS_MODULE_FAULT_TEC_RUNAWAY 0x01
+#define CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED 0x02
+#define CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED 0x03
+
+#define CMIS_CTOR_OFFSET 0xCB
+
+/* Vendor related information (Page 0) */
+#define CMIS_VENDOR_NAME_START_OFFSET 0x81
+#define CMIS_VENDOR_NAME_END_OFFSET 0x90
+
+#define CMIS_VENDOR_OUI_OFFSET 0x91
+
+#define CMIS_VENDOR_PN_START_OFFSET 0x94
+#define CMIS_VENDOR_PN_END_OFFSET 0xA3
+
+#define CMIS_VENDOR_REV_START_OFFSET 0xA4
+#define CMIS_VENDOR_REV_END_OFFSET 0xA5
+
+#define CMIS_VENDOR_SN_START_OFFSET 0xA6
+#define CMIS_VENDOR_SN_END_OFFSET 0xB5
+
+#define CMIS_DATE_YEAR_OFFSET 0xB6
+#define CMIS_DATE_VENDOR_LOT_OFFSET 0xBC
+
+/* CLEI Code (Page 0) */
+#define CMIS_CLEI_START_OFFSET 0xBE
+#define CMIS_CLEI_END_OFFSET 0xC7
+#define CMIS_CLEI_BLANK " "
+#define CMIS_CLEI_LEN 0x0A
+
+/* Cable assembly length */
+#define CMIS_CBL_ASM_LEN_OFFSET 0xCA
+#define CMIS_6300M_MAX_LEN 0xFF
+
+/* Cable length with multiplier */
+#define CMIS_MULTIPLIER_00 0x00
+#define CMIS_MULTIPLIER_01 0x40
+#define CMIS_MULTIPLIER_10 0x80
+#define CMIS_MULTIPLIER_11 0xC0
+#define CMIS_LEN_MUL_MASK 0xC0
+#define CMIS_LEN_VAL_MASK 0x3F
+
+/* Module power characteristics */
+#define CMIS_PWR_CLASS_OFFSET 0xC8
+#define CMIS_PWR_MAX_POWER_OFFSET 0xC9
+#define CMIS_PWR_CLASS_MASK 0xE0
+#define CMIS_PWR_CLASS_1 0x00
+#define CMIS_PWR_CLASS_2 0x01
+#define CMIS_PWR_CLASS_3 0x02
+#define CMIS_PWR_CLASS_4 0x03
+#define CMIS_PWR_CLASS_5 0x04
+#define CMIS_PWR_CLASS_6 0x05
+#define CMIS_PWR_CLASS_7 0x06
+#define CMIS_PWR_CLASS_8 0x07
+
+/* Copper cable attenuation */
+#define CMIS_COPPER_ATT_5GHZ 0xCC
+#define CMIS_COPPER_ATT_7GHZ 0xCD
+#define CMIS_COPPER_ATT_12P9GHZ 0xCE
+#define CMIS_COPPER_ATT_25P8GHZ 0xCF
+
+/* Cable assembly lane */
+#define CMIS_CABLE_ASM_NEAR_END_OFFSET 0xD2
+#define CMIS_CABLE_ASM_FAR_END_OFFSET 0xD3
+
+/* Media interface technology */
+#define CMIS_MEDIA_INTF_TECH_OFFSET 0xD4
+#define CMIS_850_VCSEL 0x00
+#define CMIS_1310_VCSEL 0x01
+#define CMIS_1550_VCSEL 0x02
+#define CMIS_1310_FP 0x03
+#define CMIS_1310_DFB 0x04
+#define CMIS_1550_DFB 0x05
+#define CMIS_1310_EML 0x06
+#define CMIS_1550_EML 0x07
+#define CMIS_OTHERS 0x08
+#define CMIS_1490_DFB 0x09
+#define CMIS_COPPER_UNEQUAL 0x0A
+#define CMIS_COPPER_PASS_EQUAL 0x0B
+#define CMIS_COPPER_NF_EQUAL 0x0C
+#define CMIS_COPPER_F_EQUAL 0x0D
+#define CMIS_COPPER_N_EQUAL 0x0E
+#define CMIS_COPPER_LINEAR_EQUAL 0x0F
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x01: contains advertising fields that define properties
+ * that are unique to active modules and cable assemblies.
+ * GlobalOffset = 2 * 0x80 + LocalOffset
+ */
+
+/* Supported Link Length (Page 1) */
+#define CMIS_SMF_LEN_OFFSET 0x84
+#define CMIS_OM5_LEN_OFFSET 0x85
+#define CMIS_OM4_LEN_OFFSET 0x86
+#define CMIS_OM3_LEN_OFFSET 0x87
+#define CMIS_OM2_LEN_OFFSET 0x88
+
+/* Wavelength (Page 1) */
+#define CMIS_NOM_WAVELENGTH_MSB 0x8A
+#define CMIS_NOM_WAVELENGTH_LSB 0x8B
+#define CMIS_WAVELENGTH_TOL_MSB 0x8C
+#define CMIS_WAVELENGTH_TOL_LSB 0x8D
+
+/* Supported Pages Advertising (Page 1) */
+#define CMIS_PAGES_ADVER_OFFSET 0x8E
+#define CMIS_BANKS_SUPPORTED_MASK 0x03
+#define CMIS_BANK_0_SUPPORTED 0x00
+#define CMIS_BANK_0_1_SUPPORTED 0x01
+#define CMIS_BANK_0_3_SUPPORTED 0x02
+
+/* Module Characteristics Advertising (Page 1) */
+#define CMIS_DIAG_TYPE_OFFSET 0x97
+#define CMIS_RX_PWR_TYPE_MASK 0x10
+
+/* Supported Flags Advertisement (Page 1) */
+#define CMIS_DIAG_FLAGS_TX_OFFSET 0x9d
+#define CMIS_DIAG_FL_TX_ADAPTIVE_EQ_FAIL (1 << 3)
+#define CMIS_DIAG_FL_TX_LOL (1 << 2)
+#define CMIS_DIAG_FL_TX_LOS (1 << 1)
+#define CMIS_DIAG_FL_TX_FAIL (1 << 0)
+
+#define CMIS_DIAG_FLAGS_RX_OFFSET 0x9e
+#define CMIS_DIAG_FL_RX_LOL (1 << 2)
+#define CMIS_DIAG_FL_RX_LOS (1 << 1)
+
+/* Supported Monitors Advertisement (Page 1) */
+#define CMIS_DIAG_CHAN_ADVER_OFFSET 0xA0
+#define CMIS_TX_BIAS_MON_MASK 0x01
+#define CMIS_TX_PWR_MON_MASK 0x02
+#define CMIS_RX_PWR_MON_MASK 0x04
+#define CMIS_TX_BIAS_MUL_MASK 0x18
+#define CMIS_TX_BIAS_MUL_1 0x00
+#define CMIS_TX_BIAS_MUL_2 0x08
+#define CMIS_TX_BIAS_MUL_4 0x10
+
+/* Signal integrity controls */
+#define CMIS_SIG_INTEG_TX_OFFSET 0xA1
+#define CMIS_SIG_INTEG_RX_OFFSET 0xA2
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x02: Optional Page that informs about module-defined
+ * thresholds for module-level and lane-specific threshold crossing monitors.
+ */
+
+/* Module-Level Monitor Thresholds (Page 2) */
+#define CMIS_TEMP_HALRM_OFFSET 0x80
+#define CMIS_TEMP_LALRM_OFFSET 0x82
+#define CMIS_TEMP_HWARN_OFFSET 0x84
+#define CMIS_TEMP_LWARN_OFFSET 0x86
+#define CMIS_VCC_HALRM_OFFSET 0x88
+#define CMIS_VCC_LALRM_OFFSET 0x8A
+#define CMIS_VCC_HWARN_OFFSET 0x8C
+#define CMIS_VCC_LWARN_OFFSET 0x8E
+
+/* Lane-Related Monitor Thresholds (Page 2) */
+#define CMIS_TX_PWR_HALRM_OFFSET 0xB0
+#define CMIS_TX_PWR_LALRM_OFFSET 0xB2
+#define CMIS_TX_PWR_HWARN_OFFSET 0xB4
+#define CMIS_TX_PWR_LWARN_OFFSET 0xB6
+#define CMIS_TX_BIAS_HALRM_OFFSET 0xB8
+#define CMIS_TX_BIAS_LALRM_OFFSET 0xBA
+#define CMIS_TX_BIAS_HWARN_OFFSET 0xBC
+#define CMIS_TX_BIAS_LWARN_OFFSET 0xBE
+#define CMIS_RX_PWR_HALRM_OFFSET 0xC0
+#define CMIS_RX_PWR_LALRM_OFFSET 0xC2
+#define CMIS_RX_PWR_HWARN_OFFSET 0xC4
+#define CMIS_RX_PWR_LWARN_OFFSET 0xC6
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x11: Optional Page that contains lane dynamic status
+ * bytes.
+ */
+
+/* Media Lane-Specific Flags (Page 0x11) */
+#define CMIS_TX_FAIL_OFFSET 0x87
+#define CMIS_TX_LOS_OFFSET 0x88
+#define CMIS_TX_LOL_OFFSET 0x89
+#define CMIS_TX_EQ_FAIL_OFFSET 0x8a
+#define CMIS_TX_PWR_AW_HALARM_OFFSET 0x8B
+#define CMIS_TX_PWR_AW_LALARM_OFFSET 0x8C
+#define CMIS_TX_PWR_AW_HWARN_OFFSET 0x8D
+#define CMIS_TX_PWR_AW_LWARN_OFFSET 0x8E
+#define CMIS_TX_BIAS_AW_HALARM_OFFSET 0x8F
+#define CMIS_TX_BIAS_AW_LALARM_OFFSET 0x90
+#define CMIS_TX_BIAS_AW_HWARN_OFFSET 0x91
+#define CMIS_TX_BIAS_AW_LWARN_OFFSET 0x92
+#define CMIS_RX_LOS_OFFSET 0x93
+#define CMIS_RX_LOL_OFFSET 0x94
+#define CMIS_RX_PWR_AW_HALARM_OFFSET 0x95
+#define CMIS_RX_PWR_AW_LALARM_OFFSET 0x96
+#define CMIS_RX_PWR_AW_HWARN_OFFSET 0x97
+#define CMIS_RX_PWR_AW_LWARN_OFFSET 0x98
+
+/* Media Lane-Specific Monitors (Page 0x11) */
+#define CMIS_TX_PWR_OFFSET 0x9A
+#define CMIS_TX_BIAS_OFFSET 0xAA
+#define CMIS_RX_PWR_OFFSET 0xBA
+
+#define YESNO(x) (((x) != 0) ? "Yes" : "No")
+#define ONOFF(x) (((x) != 0) ? "On" : "Off")
+
+void cmis_show_all_ioctl(const __u8 *id);
+
+int cmis_show_all_nl(struct cmd_context *ctx);
+
+#endif /* CMIS_H__ */
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..b8fd4d5
--- /dev/null
+++ b/common.c
@@ -0,0 +1,211 @@
+/*
+ * common.h - common code header
+ *
+ * Data and functions shared by ioctl and netlink implementation.
+ */
+
+#include "internal.h"
+#include "common.h"
+
+#ifndef HAVE_NETIF_MSG
+enum {
+ NETIF_MSG_DRV = 0x0001,
+ NETIF_MSG_PROBE = 0x0002,
+ NETIF_MSG_LINK = 0x0004,
+ NETIF_MSG_TIMER = 0x0008,
+ NETIF_MSG_IFDOWN = 0x0010,
+ NETIF_MSG_IFUP = 0x0020,
+ NETIF_MSG_RX_ERR = 0x0040,
+ NETIF_MSG_TX_ERR = 0x0080,
+ NETIF_MSG_TX_QUEUED = 0x0100,
+ NETIF_MSG_INTR = 0x0200,
+ NETIF_MSG_TX_DONE = 0x0400,
+ NETIF_MSG_RX_STATUS = 0x0800,
+ NETIF_MSG_PKTDATA = 0x1000,
+ NETIF_MSG_HW = 0x2000,
+ NETIF_MSG_WOL = 0x4000,
+};
+#endif
+
+const struct flag_info flags_msglvl[] = {
+ { "drv", NETIF_MSG_DRV },
+ { "probe", NETIF_MSG_PROBE },
+ { "link", NETIF_MSG_LINK },
+ { "timer", NETIF_MSG_TIMER },
+ { "ifdown", NETIF_MSG_IFDOWN },
+ { "ifup", NETIF_MSG_IFUP },
+ { "rx_err", NETIF_MSG_RX_ERR },
+ { "tx_err", NETIF_MSG_TX_ERR },
+ { "tx_queued", NETIF_MSG_TX_QUEUED },
+ { "intr", NETIF_MSG_INTR },
+ { "tx_done", NETIF_MSG_TX_DONE },
+ { "rx_status", NETIF_MSG_RX_STATUS },
+ { "pktdata", NETIF_MSG_PKTDATA },
+ { "hw", NETIF_MSG_HW },
+ { "wol", NETIF_MSG_WOL },
+ {}
+};
+const unsigned int n_flags_msglvl = ARRAY_SIZE(flags_msglvl) - 1;
+
+const struct off_flag_def off_flag_def[] = {
+ { "rx", "rx-checksumming", "rx-checksum",
+ ETHTOOL_GRXCSUM, ETHTOOL_SRXCSUM, ETH_FLAG_RXCSUM, 0 },
+ { "tx", "tx-checksumming", "tx-checksum-*",
+ ETHTOOL_GTXCSUM, ETHTOOL_STXCSUM, ETH_FLAG_TXCSUM, 0 },
+ { "sg", "scatter-gather", "tx-scatter-gather*",
+ ETHTOOL_GSG, ETHTOOL_SSG, ETH_FLAG_SG, 0 },
+ { "tso", "tcp-segmentation-offload", "tx-tcp*-segmentation",
+ ETHTOOL_GTSO, ETHTOOL_STSO, ETH_FLAG_TSO, 0 },
+ { "ufo", "udp-fragmentation-offload", "tx-udp-fragmentation",
+ ETHTOOL_GUFO, ETHTOOL_SUFO, ETH_FLAG_UFO, 0 },
+ { "gso", "generic-segmentation-offload", "tx-generic-segmentation",
+ ETHTOOL_GGSO, ETHTOOL_SGSO, ETH_FLAG_GSO, 0 },
+ { "gro", "generic-receive-offload", "rx-gro",
+ ETHTOOL_GGRO, ETHTOOL_SGRO, ETH_FLAG_GRO, 0 },
+ { "lro", "large-receive-offload", "rx-lro",
+ 0, 0, ETH_FLAG_LRO,
+ KERNEL_VERSION(2,6,24) },
+ { "rxvlan", "rx-vlan-offload", "rx-vlan-hw-parse",
+ 0, 0, ETH_FLAG_RXVLAN,
+ KERNEL_VERSION(2,6,37) },
+ { "txvlan", "tx-vlan-offload", "tx-vlan-hw-insert",
+ 0, 0, ETH_FLAG_TXVLAN,
+ KERNEL_VERSION(2,6,37) },
+ { "ntuple", "ntuple-filters", "rx-ntuple-filter",
+ 0, 0, ETH_FLAG_NTUPLE, 0 },
+ { "rxhash", "receive-hashing", "rx-hashing",
+ 0, 0, ETH_FLAG_RXHASH, 0 },
+};
+
+void print_flags(const struct flag_info *info, unsigned int n_info, u32 value)
+{
+ const char *sep = "";
+
+ while (n_info) {
+ if (value & info->value) {
+ printf("%s%s", sep, info->name);
+ sep = " ";
+ value &= ~info->value;
+ }
+ ++info;
+ --n_info;
+ }
+
+ /* Print any unrecognised flags in hex */
+ if (value)
+ printf("%s%#x", sep, value);
+}
+
+static char *unparse_wolopts(int wolopts)
+{
+ static char buf[16];
+ char *p = buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (wolopts) {
+ if (wolopts & WAKE_PHY)
+ *p++ = 'p';
+ if (wolopts & WAKE_UCAST)
+ *p++ = 'u';
+ if (wolopts & WAKE_MCAST)
+ *p++ = 'm';
+ if (wolopts & WAKE_BCAST)
+ *p++ = 'b';
+ if (wolopts & WAKE_ARP)
+ *p++ = 'a';
+ if (wolopts & WAKE_MAGIC)
+ *p++ = 'g';
+ if (wolopts & WAKE_MAGICSECURE)
+ *p++ = 's';
+ if (wolopts & WAKE_FILTER)
+ *p++ = 'f';
+ } else {
+ *p = 'd';
+ }
+
+ return buf;
+}
+
+int dump_wol(struct ethtool_wolinfo *wol)
+{
+ fprintf(stdout, " Supports Wake-on: %s\n",
+ unparse_wolopts(wol->supported));
+ fprintf(stdout, " Wake-on: %s\n",
+ unparse_wolopts(wol->wolopts));
+ if (wol->supported & WAKE_MAGICSECURE) {
+ int i;
+ int delim = 0;
+
+ fprintf(stdout, " SecureOn password: ");
+ for (i = 0; i < SOPASS_MAX; i++) {
+ fprintf(stdout, "%s%02x", delim ? ":" : "",
+ wol->sopass[i]);
+ delim = 1;
+ }
+ fprintf(stdout, "\n");
+ }
+
+ return 0;
+}
+
+void dump_mdix(u8 mdix, u8 mdix_ctrl)
+{
+ fprintf(stdout, " MDI-X: ");
+ if (mdix_ctrl == ETH_TP_MDI) {
+ fprintf(stdout, "off (forced)\n");
+ } else if (mdix_ctrl == ETH_TP_MDI_X) {
+ fprintf(stdout, "on (forced)\n");
+ } else {
+ switch (mdix) {
+ case ETH_TP_MDI:
+ fprintf(stdout, "off");
+ break;
+ case ETH_TP_MDI_X:
+ fprintf(stdout, "on");
+ break;
+ default:
+ fprintf(stdout, "Unknown");
+ break;
+ }
+ if (mdix_ctrl == ETH_TP_MDI_AUTO)
+ fprintf(stdout, " (auto)");
+ fprintf(stdout, "\n");
+ }
+}
+
+void print_indir_table(struct cmd_context *ctx, u64 ring_count,
+ u32 indir_size, u32 *indir)
+{
+ u32 i;
+
+ printf("RX flow hash indirection table for %s with %llu RX ring(s):\n",
+ ctx->devname, ring_count);
+
+ if (!indir_size)
+ printf("Operation not supported\n");
+
+ for (i = 0; i < indir_size; i++) {
+ if (i % 8 == 0)
+ printf("%5u: ", i);
+ printf(" %5u", indir[i]);
+ if (i % 8 == 7 || i == indir_size - 1)
+ fputc('\n', stdout);
+ }
+}
+
+void print_rss_hkey(u8 *hkey, u32 hkey_size)
+{
+ u32 i;
+
+ printf("RSS hash key:\n");
+ if (!hkey_size || !hkey)
+ printf("Operation not supported\n");
+
+ for (i = 0; i < hkey_size; i++) {
+ if (i == (hkey_size - 1))
+ printf("%02x\n", hkey[i]);
+ else
+ printf("%02x:", hkey[i]);
+ }
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..f975407
--- /dev/null
+++ b/common.h
@@ -0,0 +1,49 @@
+/*
+ * common.h - common code header
+ *
+ * Declarations for data and functions shared by ioctl and netlink code.
+ */
+
+#ifndef ETHTOOL_COMMON_H__
+#define ETHTOOL_COMMON_H__
+
+#include "internal.h"
+#include <stddef.h>
+#include <errno.h>
+
+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+
+struct flag_info {
+ const char *name;
+ u32 value;
+};
+
+extern const struct flag_info flags_msglvl[];
+extern const unsigned int n_flags_msglvl;
+
+struct off_flag_def {
+ const char *short_name;
+ const char *long_name;
+ const char *kernel_name;
+ u32 get_cmd, set_cmd;
+ u32 value;
+ /* For features exposed through ETHTOOL_GFLAGS, the oldest
+ * kernel version for which we can trust the result. Where
+ * the flag was added at the same time the kernel started
+ * supporting the feature, this is 0 (to allow for backports).
+ * Where the feature was supported before the flag was added,
+ * it is the version that introduced the flag.
+ */
+ u32 min_kernel_ver;
+};
+
+#define OFF_FLAG_DEF_SIZE 12
+extern const struct off_flag_def off_flag_def[OFF_FLAG_DEF_SIZE];
+
+void print_flags(const struct flag_info *info, unsigned int n_info, u32 value);
+int dump_wol(struct ethtool_wolinfo *wol);
+void dump_mdix(u8 mdix, u8 mdix_ctrl);
+void print_indir_table(struct cmd_context *ctx, u64 ring_count,
+ u32 indir_size, u32 *indir);
+void print_rss_hkey(u8 *hkey, u32 hkey_size);
+#endif /* ETHTOOL_COMMON_H__ */
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..c871558
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,86 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(ethtool, 6.7, netdev@vger.kernel.org)
+AC_PREREQ(2.52)
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_SRCDIR([ethtool.c])
+AM_INIT_AUTOMAKE([gnu subdir-objects])
+AC_CONFIG_HEADERS([ethtool-config.h])
+
+AM_MAINTAINER_MODE
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_GCC_TRADITIONAL
+AM_PROG_CC_C_O
+PKG_PROG_PKG_CONFIG
+
+AC_DEFUN([AX_CHECK_STDC],
+ [AX_CHECK_COMPILE_FLAG([-std=gnu11],
+ [AX_APPEND_FLAG([-std=gnu11])],
+ [AX_CHECK_COMPILE_FLAG([-std=c11],
+ [AX_APPEND_FLAG([-std=c11])],
+ [AC_MSG_ERROR([$PACKAGE requires a C11 compiler])])
+ ])
+ ])
+AX_CHECK_STDC
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+
+dnl Checks for library functions.
+AC_HEADER_STDC
+AC_CHECK_FUNCS(socket)
+
+dnl Check for options
+AC_ARG_ENABLE(pretty-dump,
+ [ --enable-pretty-dump enable registers, EEPROM and SFP pretty dumps (enabled by default)],
+ ,
+ enable_pretty_dump=yes)
+if test x$enable_pretty_dump = xyes; then
+ AC_DEFINE(ETHTOOL_ENABLE_PRETTY_DUMP, 1,
+ [Define this to enable register, EEPROM and SFP pretty dumps.])
+fi
+AM_CONDITIONAL([ETHTOOL_ENABLE_PRETTY_DUMP], [test x$enable_pretty_dump = xyes])
+
+AC_ARG_WITH([bash-completion-dir],
+ AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
+ [Install the bash-completion script in this directory. @<:@default=yes@:>@]),
+ [],
+ [with_bash_completion_dir=yes])
+AS_IF([test "x$with_bash_completion_dir" = xyes],
+ [AC_MSG_CHECKING([for bash-completion directory])
+ dnl Attempt to use pkg-config completionsdir variable with given $prefix.
+ dnl This matches distcheck expectation that all files install to $prefix.
+ dnl It works with /usr and /usr/local (for default $XDG_DATA_DIRS) but
+ dnl may install to directory not used by bash-completion in other cases.
+ dnl See: https://lore.kernel.org/netdev/20190417025333.GA28674@kevinolos/
+ AS_IF([test "x$PKG_CONFIG" != x \
+ && bash_completion_prefix=`"$PKG_CONFIG" --print-errors --variable=prefix bash-completion 2>&AS_MESSAGE_LOG_FD` \
+ && bash_completion_dir=`"$PKG_CONFIG" --print-errors --variable=completionsdir bash-completion 2>&AS_MESSAGE_LOG_FD`],
+ [bash_completion_dir="${bash_completion_dir#"$bash_completion_prefix"}"
+ bash_completion_dir="${bash_completion_dir#/}"
+ BASH_COMPLETION_DIR='${prefix}'/"$bash_completion_dir"],
+ [BASH_COMPLETION_DIR='${datadir}/bash-completion/completions'])
+ AC_MSG_RESULT([$BASH_COMPLETION_DIR])],
+ [BASH_COMPLETION_DIR="$with_bash_completion_dir"])
+AC_SUBST([BASH_COMPLETION_DIR])
+AM_CONDITIONAL([ENABLE_BASH_COMPLETION],
+ [test "x$with_bash_completion_dir" != xno])
+
+AC_ARG_ENABLE(netlink,
+ [ --enable-netlink enable netlink interface (enabled by default)],
+ ,
+ enable_netlink=yes)
+if test x$enable_netlink = xyes; then
+ PKG_PROG_PKG_CONFIG
+ PKG_CHECK_MODULES([MNL], [libmnl])
+ AC_DEFINE(ETHTOOL_ENABLE_NETLINK, 1,
+ Define this to support netlink interface to talk to kernel.)
+fi
+AM_CONDITIONAL([ETHTOOL_ENABLE_NETLINK], [test x$enable_netlink = xyes])
+
+AC_CONFIG_FILES([Makefile ethtool.spec ethtool.8])
+AC_OUTPUT
diff --git a/cpsw.c b/cpsw.c
new file mode 100644
index 0000000..68dcfac
--- /dev/null
+++ b/cpsw.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Code to dump registers for TI CPSW switch devices.
+ *
+ * Copyright (c) 2022 Linutronix GmbH
+ * Author: Benedikt Spranger <b.spranger@linutronix.de>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "internal.h"
+
+#define ALE_ENTRY_BITS 68
+#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
+#define ALE_ENTRY_BYTES (ALE_ENTRY_WORDS * 4)
+
+struct address_table_entry
+{
+ u8 port;
+ u8 reserved1;
+ u8 reserved2;
+ u8 reserved3;
+ u8 addr2;
+ u8 addr1;
+ u16 vlan;
+ u8 addr6;
+ u8 addr5;
+ u8 addr4;
+ u8 addr3;
+} __attribute__((packed));
+
+struct vlan_table_entry
+{
+ u8 reserved1;
+ u8 reserved2;
+ u8 reserved3;
+ u8 reserved4;
+ u8 reserved5;
+ u8 reserved6;
+ u16 vlan;
+ u8 member;
+ u8 mc_unreg;
+ u8 mc_reg;
+ u8 untag;
+} __attribute__((packed));
+
+union ale_entry {
+ struct address_table_entry addr;
+ struct vlan_table_entry vlan;
+ u32 val[3];
+ u8 byte[12];
+};
+
+enum entry_type {
+ FREE_ENTRY = 0,
+ ADDR_ENTRY,
+ VLAN_ENTRY,
+ VLAN_ADDR_ENTRY,
+ LAST_ENTRY
+};
+
+static char *fwd_state_name[] = {
+ "Forwarding",
+ "Blocking/Forwarding/Learning",
+ "Forwarding/Learning",
+ "Forwarding",
+};
+
+static char *type_name[] = {
+ "free entry",
+ "address entry",
+ "VLAN entry",
+ "VLAN address entry",
+ "invalid"
+};
+
+enum entry_type decode_type(union ale_entry *entry)
+{
+ /* Entry Type (61:60) */
+ return (entry->byte[7] >> 4) & 0x3;
+}
+
+static void print_addr(u8 *data)
+{
+ printf("%02x:%02x:%02x:%02x:%02x:%02x",
+ data[5], data[4], data[11], data[10], data[9], data[8]);
+}
+
+static void decode_multi_addr(union ale_entry *entry, int vlan)
+{
+ printf(" MULTI: ");
+ print_addr(entry->byte);
+ printf(" %s", fwd_state_name[entry->addr.vlan >> 14]);
+ printf("%s", (entry->addr.port & 0x02) ? " Super" : "");
+ printf(" Ports: 0x%x", (entry->addr.port >> 2) & 0x3);
+ if (vlan)
+ printf(" VLAN: %04d", entry->addr.vlan & 0x0fff);
+ printf("\n");
+}
+
+static void decode_uni_addr(union ale_entry *entry, int vlan)
+{
+ printf(" UNI : ");
+ print_addr(entry->byte);
+ printf("%s", (entry->addr.port & 0x01) ? " Secure" : "");
+ printf("%s", (entry->addr.port & 0x02) ? " Block" : "");
+ printf("%s", (entry->addr.port & 0x20) ? " DLR" : "");
+ printf(" Ports: 0x%x", (entry->addr.port >> 2) & 0x3);
+ if (vlan)
+ printf(" VLAN: %04d", entry->addr.vlan & 0x0fff);
+ printf("\n");
+}
+
+static void decode_oui_addr(union ale_entry *entry)
+{
+ printf(" OUI : ");
+ print_addr(entry->byte);
+ printf("\n");
+}
+
+static void decode_vlan(union ale_entry *entry)
+{
+ printf(" VLAN ");
+ printf("%04d: ", entry->vlan.vlan & 0x0fff);
+ printf("member: 0x%x ", entry->vlan.member & 0x7);
+ printf("mc flood unreg: 0x%x ", entry->vlan.mc_unreg & 0x7);
+ printf("mc flood reg: 0x%x ", entry->vlan.mc_reg & 0x7);
+ printf("untag: 0x%x\n", entry->vlan.untag & 0x7);
+}
+
+static enum entry_type decode_ale_entry(unsigned int idx, const u8 *data,
+ bool last_was_free)
+{
+ union ale_entry *entry = (union ale_entry *) data;
+ enum entry_type type;
+
+ entry = entry + idx;
+ type = decode_type(entry);
+
+ if (!last_was_free || type != FREE_ENTRY)
+ printf("%04d: %s\n", idx, type_name[type]);
+
+ switch (type)
+ {
+ case FREE_ENTRY:
+ goto out;
+ break;
+
+ case ADDR_ENTRY:
+ /* Multicast: OUI 01:00:5e:xx:xx:xx */
+ if (entry->addr.addr1 == 0x01)
+ decode_multi_addr(entry, 0);
+ else
+ if ((entry->addr.vlan >> 14) == 0x2)
+ decode_oui_addr(entry);
+ else
+ decode_uni_addr(entry, 0);
+ break;
+
+ case VLAN_ENTRY:
+ decode_vlan(entry);
+ break;
+
+ case VLAN_ADDR_ENTRY:
+ /* Check for Individual/Group bit */
+ if (entry->addr.addr1 & 0x01)
+ decode_multi_addr(entry, 1);
+ else
+ decode_uni_addr(entry, 1);
+ break;
+
+ default:
+ printf("internal failure.\n");
+ }
+
+out:
+ return type;
+}
+
+int cpsw_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ unsigned int entries = regs->len/ALE_ENTRY_BYTES;
+ enum entry_type type = LAST_ENTRY;
+ unsigned int i;
+
+ printf("ALE Entries (%d):\n", entries);
+
+ for (i = 0; i < entries; i++)
+ type = decode_ale_entry(i, regs->data, (type == FREE_ENTRY));
+
+ return 0;
+}
diff --git a/de2104x.c b/de2104x.c
new file mode 100644
index 0000000..190422f
--- /dev/null
+++ b/de2104x.c
@@ -0,0 +1,783 @@
+/* Copyright 2001 Sun Microsystems (thockin@sun.com) */
+#include <stdio.h>
+#include "internal.h"
+
+static const char * const csr0_tap[4] = {
+ "No transmit automatic polling",
+ "Transmit automatic polling every 200 seconds",
+ "Transmit automatic polling every 800 seconds",
+ "Transmit automatic polling every 1.6 milliseconds",
+};
+
+static const char * const csr0_cache_al[4] = {
+ "not used",
+ "8-longword boundary alignment",
+ "16-longword boundary alignment",
+ "32-longword boundary alignment",
+};
+
+static const char * const csr5_buserr[8] = {
+ " Bus error: parity",
+ " Bus error: master abort",
+ " Bus error: target abort",
+ " Bus error: (unknown code, reserved)",
+ " Bus error: (unknown code, reserved)",
+ " Bus error: (unknown code, reserved)",
+ " Bus error: (unknown code, reserved)",
+ " Bus error: (unknown code, reserved)",
+};
+
+static const int csr6_tx_thresh[4] = {
+ 72,
+ 96,
+ 128,
+ 160,
+};
+
+static const char * const csr6_om[4] = {
+ "normal",
+ "internal loopback",
+ "external loopback",
+ "unknown (not used)",
+};
+
+static const char * const csr5_tx_state[8] = {
+ "stopped",
+ "running: fetch desc",
+ "running: wait xmit end",
+ "running: read buf",
+ "unknown (reserved)",
+ "running: setup packet",
+ "suspended",
+ "running: close desc",
+};
+
+static const char * const csr5_rx_state[8] = {
+ "stopped",
+ "running: fetch desc",
+ "running: chk pkt end",
+ "running: wait for pkt",
+ "suspended",
+ "running: close",
+ "running: flush",
+ "running: queue",
+};
+
+static const char * const csr12_nway_state[8] = {
+ "Autonegotiation disable",
+ "Transmit disable",
+ "Ability detect",
+ "Acknowledge detect",
+ "Complete acknowledge",
+ "FLP link good, nway complete",
+ "Link check",
+ "unknown (reserved)",
+};
+
+static const char * const csr14_tp_comp[4] = {
+ "Compensation Disabled Mode",
+ "Compensation Disabled Mode",
+ "High Power Mode",
+ "Normal Compensation Mode",
+};
+
+static void
+print_ring_addresses(u32 csr3, u32 csr4)
+{
+ fprintf(stdout,
+ "0x18: CSR3 (Rx Ring Base Address) 0x%08x\n"
+ "0x20: CSR4 (Tx Ring Base Address) 0x%08x\n"
+ ,
+ csr3,
+ csr4);
+}
+
+static void
+print_rx_missed(u32 csr8)
+{
+ fprintf(stdout,
+ "0x40: CSR8 (Missed Frames Counter) 0x%08x\n", csr8);
+ if (csr8 & (1 << 16))
+ fprintf(stdout,
+ " Counter overflow\n");
+ else {
+ unsigned int rx_missed = csr8 & 0xffff;
+ if (!rx_missed)
+ fprintf(stdout,
+ " No missed frames\n");
+ else
+ fprintf(stdout,
+ " %u missed frames\n", rx_missed);
+ }
+}
+
+static void de21040_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 tmp, v, *data = (u32 *)regs->data;
+
+ fprintf(stdout, "21040 Registers\n");
+ fprintf(stdout, "---------------\n");
+
+ /*
+ * CSR0
+ */
+ v = data[0];
+ fprintf(stdout,
+ "0x00: CSR0 (Bus Mode) 0x%08x\n"
+ " %s\n"
+ " %s address space\n"
+ " Cache alignment: %s\n"
+ ,
+ v,
+ csr0_tap[(v >> 17) & 3],
+ v & (1 << 16) ? "Diagnostic" : "Standard",
+ csr0_cache_al[(v >> 14) & 3]);
+ tmp = (v >> 8) & 0x3f;
+ if (tmp == 0)
+ fprintf(stdout, " Programmable burst length unlimited\n");
+ else
+ fprintf(stdout,
+ " Programmable burst length %d longwords\n",
+ tmp);
+ fprintf(stdout,
+ " %s endian data buffers\n"
+ " Descriptor skip length %d longwords\n"
+ " %s bus arbitration scheme\n"
+ ,
+ v & (1 << 7) ? "Big" : "Little",
+ (v >> 2) & 0x1f,
+ v & (1 << 1) ? "Round-robin" : "RX-has-priority");
+ if (v & (1 << 0))
+ fprintf(stdout, " Software reset asserted\n");
+
+ /*
+ * CSR3, 4
+ */
+ print_ring_addresses(data[3], data[4]);
+
+ /*
+ * CSR5
+ */
+ v = data[5];
+ fprintf(stdout,
+ "0x28: CSR5 (Status) 0x%08x\n"
+ "%s"
+ " Transmit process %s\n"
+ " Receive process %s\n"
+ " Link %s\n"
+ ,
+ v,
+ v & (1 << 13) ? csr5_buserr[(v >> 23) & 0x7] : "",
+ csr5_tx_state[(v >> 20) & 0x7],
+ csr5_rx_state[(v >> 17) & 0x7],
+ v & (1 << 12) ? "fail" : "OK");
+ if (v & (1 << 16))
+ fprintf(stdout,
+ " Normal interrupts: %s%s%s\n"
+ ,
+ v & (1 << 0) ? "TxOK " : "",
+ v & (1 << 2) ? "TxNoBufs " : "",
+ v & (1 << 6) ? "RxOK" : "");
+ if (v & (1 << 15))
+ fprintf(stdout,
+ " Abnormal intr: %s%s%s%s%s%s%s%s\n"
+ ,
+ v & (1 << 1) ? "TxStop " : "",
+ v & (1 << 3) ? "TxJabber " : "",
+ v & (1 << 5) ? "TxUnder " : "",
+ v & (1 << 7) ? "RxNoBufs " : "",
+ v & (1 << 8) ? "RxStopped " : "",
+ v & (1 << 9) ? "RxTimeout " : "",
+ v & (1 << 10) ? "AUI_TP " : "",
+ v & (1 << 11) ? "FD_Short " : "");
+
+ /*
+ * CSR6
+ */
+ v = data[6];
+ fprintf(stdout,
+ "0x30: CSR6 (Operating Mode) 0x%08x\n"
+ "%s"
+ "%s"
+ " Transmit threshold %d bytes\n"
+ " Transmit DMA %sabled\n"
+ "%s"
+ " Operating mode: %s\n"
+ " %s duplex\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ " Receive DMA %sabled\n"
+ " %s filtering mode\n"
+ ,
+ v,
+ v & (1<<17) ? " Capture effect enabled\n" : "",
+ v & (1<<16) ? " Back pressure enabled\n" : "",
+ csr6_tx_thresh[(v >> 14) & 3],
+ v & (1<<13) ? "en" : "dis",
+ v & (1<<12) ? " Forcing collisions\n" : "",
+ csr6_om[(v >> 10) & 3],
+ v & (1<<9) ? "Full" : "Half",
+ v & (1<<8) ? " Flaky oscillator disable\n" : "",
+ v & (1<<7) ? " Pass All Multicast\n" : "",
+ v & (1<<6) ? " Promisc Mode\n" : "",
+ v & (1<<5) ? " Start/Stop Backoff Counter\n" : "",
+ v & (1<<4) ? " Inverse Filtering\n" : "",
+ v & (1<<3) ? " Pass Bad Frames\n" : "",
+ v & (1<<2) ? " Hash-only Filtering\n" : "",
+ v & (1<<1) ? "en" : "dis",
+ v & (1<<0) ? "Hash" : "Perfect");
+
+ /*
+ * CSR7
+ */
+ v = data[7];
+ fprintf(stdout,
+ "0x38: CSR7 (Interrupt Mask) 0x%08x\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ ,
+ v,
+ v & (1<<16) ? " Normal interrupt summary\n" : "",
+ v & (1<<15) ? " Abnormal interrupt summary\n" : "",
+ v & (1<<13) ? " System error\n" : "",
+ v & (1<<12) ? " Link fail\n" : "",
+ v & (1<<11) ? " Full duplex\n" : "",
+ v & (1<<10) ? " AUI_TP pin\n" : "",
+ v & (1<<9) ? " Receive watchdog timeout\n" : "",
+ v & (1<<8) ? " Receive stopped\n" : "",
+ v & (1<<7) ? " Receive buffer unavailable\n" : "",
+ v & (1<<6) ? " Receive interrupt\n" : "",
+ v & (1<<5) ? " Transmit underflow\n" : "",
+ v & (1<<3) ? " Transmit jabber timeout\n" : "",
+ v & (1<<2) ? " Transmit buffer unavailable\n" : "",
+ v & (1<<1) ? " Transmit stopped\n" : "",
+ v & (1<<0) ? " Transmit interrupt\n" : "");
+
+ /*
+ * CSR8
+ */
+ print_rx_missed(data[8]);
+
+ /*
+ * CSR9
+ */
+ v = data[9];
+ fprintf(stdout,
+ "0x48: CSR9 (Ethernet Address ROM) 0x%08x\n", v);
+
+ /*
+ * CSR11
+ */
+ v = data[11];
+ fprintf(stdout,
+ "0x58: CSR11 (Full Duplex Autoconfig) 0x%08x\n", v);
+
+ /*
+ * CSR12
+ */
+ v = data[12];
+ fprintf(stdout,
+ "0x60: CSR12 (SIA Status) 0x%08x\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ " AUI_TP pin: %s\n"
+ ,
+ v,
+ v & (1<<7) ? " PLL sampler high\n" : "",
+ v & (1<<6) ? " PLL sampler low\n" : "",
+ v & (1<<5) ? " PLL self-test pass\n" : "",
+ v & (1<<4) ? " PLL self-test done\n" : "",
+ v & (1<<3) ? " Autopolarity state\n" : "",
+ v & (1<<2) ? " Link fail\n" : "",
+ v & (1<<1) ? " Network connection error\n" : "",
+ v & (1<<0) ? "AUI" : "TP");
+
+ /*
+ * CSR13
+ */
+ v = data[13];
+ fprintf(stdout,
+ "0x68: CSR13 (SIA Connectivity) 0x%08x\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ " External port output multiplexer select: %u%u%u%u\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ " %s interface selected\n"
+ "%s"
+ "%s"
+ "%s"
+ ,
+ v,
+ v & (1<<15) ? " Enable pins 5, 6, 7\n" : "",
+ v & (1<<14) ? " Enable pins 2, 4\n" : "",
+ v & (1<<13) ? " Enable pins 1, 3\n" : "",
+ v & (1<<12) ? " Input enable\n" : "",
+ v & (1<<11) ? 1 : 0,
+ v & (1<<10) ? 1 : 0,
+ v & (1<<9) ? 1 : 0,
+ v & (1<<8) ? 1 : 0,
+ v & (1<<7) ? " APLL start\n" : "",
+ v & (1<<6) ? " Serial interface input multiplexer\n" : "",
+ v & (1<<5) ? " Encoder input multiplexer\n" : "",
+ v & (1<<4) ? " SIA PLL external input enable\n" : "",
+ v & (1<<3) ? "AUI" : "10base-T",
+ v & (1<<2) ? " CSR autoconfiguration\n" : "",
+ v & (1<<1) ? " AUI_TP pin autoconfiguration\n" : "",
+ v & (1<<0) ? " SIA reset\n" : "");
+
+ /*
+ * CSR14
+ */
+ v = data[14];
+ fprintf(stdout,
+ "0x70: CSR14 (SIA Transmit and Receive) 0x%08x\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ " %s\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ ,
+ v,
+ v & (1<<14) ? " Set polarity plus\n" : "",
+ v & (1<<13) ? " Autopolarity enable\n" : "",
+ v & (1<<12) ? " Link test enable\n" : "",
+ v & (1<<11) ? " Heartbeat enable\n" : "",
+ v & (1<<10) ? " Collision detect enable\n" : "",
+ v & (1<<9) ? " Collision squelch enable\n" : "",
+ v & (1<<8) ? " Receive squelch enable\n" : "",
+ csr14_tp_comp[(v >> 4) & 0x3],
+ v & (1<<3) ? " Link pulse send enable\n" : "",
+ v & (1<<2) ? " Driver enable\n" : "",
+ v & (1<<1) ? " Loopback enable\n" : "",
+ v & (1<<0) ? " Encoder enable\n" : "");
+
+ /*
+ * CSR15
+ */
+ v = data[15];
+ fprintf(stdout,
+ "0x78: CSR15 (SIA General) 0x%08x\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ ,
+ v,
+ v & (1<<13) ? " Force receiver low\n" : "",
+ v & (1<<12) ? " PLL self-test start\n" : "",
+ v & (1<<11) ? " Force link fail\n" : "",
+ v & (1<<9) ? " Force unsquelch\n" : "",
+ v & (1<<8) ? " Test clock\n" : "",
+ v & (1<<5) ? " Receive watchdog release\n" : "",
+ v & (1<<4) ? " Receive watchdog disable\n" : "",
+ v & (1<<2) ? " Jabber clock\n" : "",
+ v & (1<<1) ? " Host unjab\n" : "",
+ v & (1<<0) ? " Jabber disable\n" : "");
+}
+
+static void de21041_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 tmp, v, *data = (u32 *)regs->data;
+
+ fprintf(stdout, "21041 Registers\n");
+ fprintf(stdout, "---------------\n");
+
+ /*
+ * CSR0
+ */
+ v = data[0];
+ fprintf(stdout,
+ "0x00: CSR0 (Bus Mode) 0x%08x\n"
+ " %s endian descriptors\n"
+ " %s\n"
+ " %s address space\n"
+ " Cache alignment: %s\n"
+ ,
+ v,
+ v & (1 << 20) ? "Big" : "Little",
+ csr0_tap[(v >> 17) & 3],
+ v & (1 << 16) ? "Diagnostic" : "Standard",
+ csr0_cache_al[(v >> 14) & 3]);
+ tmp = (v >> 8) & 0x3f;
+ if (tmp == 0)
+ fprintf(stdout, " Programmable burst length unlimited\n");
+ else
+ fprintf(stdout,
+ " Programmable burst length %d longwords\n",
+ tmp);
+ fprintf(stdout,
+ " %s endian data buffers\n"
+ " Descriptor skip length %d longwords\n"
+ " %s bus arbitration scheme\n"
+ ,
+ v & (1 << 7) ? "Big" : "Little",
+ (v >> 2) & 0x1f,
+ v & (1 << 1) ? "Round-robin" : "RX-has-priority");
+ if (v & (1 << 0))
+ fprintf(stdout, " Software reset asserted\n");
+
+ /*
+ * CSR3, 4
+ */
+ print_ring_addresses(data[3], data[4]);
+
+ /*
+ * CSR5
+ */
+ v = data[5];
+ fprintf(stdout,
+ "0x28: CSR5 (Status) 0x%08x\n"
+ "%s"
+ " Transmit process %s\n"
+ " Receive process %s\n"
+ " Link %s\n"
+ ,
+ v,
+ v & (1 << 13) ? csr5_buserr[(v >> 23) & 0x7] : "",
+ csr5_tx_state[(v >> 20) & 0x7],
+ csr5_rx_state[(v >> 17) & 0x7],
+ v & (1 << 12) ? "fail" : "OK");
+ if (v & (1 << 16))
+ fprintf(stdout,
+ " Normal interrupts: %s%s%s%s%s\n"
+ ,
+ v & (1 << 0) ? "TxOK " : "",
+ v & (1 << 2) ? "TxNoBufs " : "",
+ v & (1 << 6) ? "RxOK" : "",
+ v & (1 << 11) ? "TimerExp " : "",
+ v & (1 << 14) ? "EarlyRx " : "");
+ if (v & (1 << 15))
+ fprintf(stdout,
+ " Abnormal intr: %s%s%s%s%s%s%s\n"
+ ,
+ v & (1 << 1) ? "TxStop " : "",
+ v & (1 << 3) ? "TxJabber " : "",
+ v & (1 << 4) ? "ANC " : "",
+ v & (1 << 5) ? "TxUnder " : "",
+ v & (1 << 7) ? "RxNoBufs " : "",
+ v & (1 << 8) ? "RxStopped " : "",
+ v & (1 << 9) ? "RxTimeout " : "");
+
+ /*
+ * CSR6
+ */
+ v = data[6];
+ fprintf(stdout,
+ "0x30: CSR6 (Operating Mode) 0x%08x\n"
+ "%s"
+ "%s"
+ " Transmit threshold %d bytes\n"
+ " Transmit DMA %sabled\n"
+ "%s"
+ " Operating mode: %s\n"
+ " %s duplex\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ " Receive DMA %sabled\n"
+ " %s filtering mode\n"
+ ,
+ v,
+ v & (1<<31) ? " Special capture effect enabled\n" : "",
+ v & (1<<17) ? " Capture effect enabled\n" : "",
+ csr6_tx_thresh[(v >> 14) & 3],
+ v & (1<<13) ? "en" : "dis",
+ v & (1<<12) ? " Forcing collisions\n" : "",
+ csr6_om[(v >> 10) & 3],
+ v & (1<<9) ? "Full" : "Half",
+ v & (1<<8) ? " Flaky oscillator disable\n" : "",
+ v & (1<<7) ? " Pass All Multicast\n" : "",
+ v & (1<<6) ? " Promisc Mode\n" : "",
+ v & (1<<5) ? " Start/Stop Backoff Counter\n" : "",
+ v & (1<<4) ? " Inverse Filtering\n" : "",
+ v & (1<<3) ? " Pass Bad Frames\n" : "",
+ v & (1<<2) ? " Hash-only Filtering\n" : "",
+ v & (1<<1) ? "en" : "dis",
+ v & (1<<0) ? "Hash" : "Perfect");
+
+ /*
+ * CSR7
+ */
+ v = data[7];
+ fprintf(stdout,
+ "0x38: CSR7 (Interrupt Mask) 0x%08x\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ ,
+ v,
+ v & (1<<16) ? " Normal interrupt summary\n" : "",
+ v & (1<<15) ? " Abnormal interrupt summary\n" : "",
+ v & (1<<14) ? " Early receive interrupt\n" : "",
+ v & (1<<13) ? " System error\n" : "",
+ v & (1<<12) ? " Link fail\n" : "",
+ v & (1<<11) ? " Timer expired\n" : "",
+ v & (1<<9) ? " Receive watchdog timeout\n" : "",
+ v & (1<<8) ? " Receive stopped\n" : "",
+ v & (1<<7) ? " Receive buffer unavailable\n" : "",
+ v & (1<<6) ? " Receive interrupt\n" : "",
+ v & (1<<5) ? " Transmit underflow\n" : "",
+ v & (1<<4) ? " Link pass\n" : "",
+ v & (1<<3) ? " Transmit jabber timeout\n" : "",
+ v & (1<<2) ? " Transmit buffer unavailable\n" : "",
+ v & (1<<1) ? " Transmit stopped\n" : "",
+ v & (1<<0) ? " Transmit interrupt\n" : "");
+
+ /*
+ * CSR8
+ */
+ print_rx_missed(data[8]);
+
+ /*
+ * CSR9
+ */
+ v = data[9];
+ fprintf(stdout,
+ "0x48: CSR9 (Boot and Ethernet ROMs) 0x%08x\n"
+ " Select bits: %s%s%s%s%s%s\n"
+ " Data: %d%d%d%d%d%d%d%d\n"
+ ,
+ v,
+ v & (1<<15) ? "Mode " : "",
+ v & (1<<14) ? "Read " : "",
+ v & (1<<13) ? "Write " : "",
+ v & (1<<12) ? "BootROM " : "",
+ v & (1<<11) ? "SROM " : "",
+ v & (1<<10) ? "ExtReg " : "",
+ v & (1<<7) ? 1 : 0,
+ v & (1<<6) ? 1 : 0,
+ v & (1<<5) ? 1 : 0,
+ v & (1<<4) ? 1 : 0,
+ v & (1<<3) ? 1 : 0,
+ v & (1<<2) ? 1 : 0,
+ v & (1<<1) ? 1 : 0,
+ v & (1<<0) ? 1 : 0);
+
+ /*
+ * CSR10
+ */
+ v = data[10];
+ fprintf(stdout,
+ "0x50: CSR10 (Boot ROM Address) 0x%08x\n", v);
+
+ /*
+ * CSR11
+ */
+ v = data[11];
+ fprintf(stdout,
+ "0x58: CSR11 (General Purpose Timer) 0x%08x\n"
+ "%s"
+ " Timer value: %u cycles\n"
+ ,
+ v,
+ v & (1<<16) ? " Continuous mode\n" : "",
+ v & 0xffff);
+
+ /*
+ * CSR12
+ */
+ v = data[12];
+ fprintf(stdout,
+ "0x60: CSR12 (SIA Status) 0x%08x\n"
+ " Link partner code word 0x%04x\n"
+ "%s"
+ " NWay state: %s\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ ,
+ v,
+ v >> 16,
+ v & (1<<15) ? " Link partner negotiable\n" : "",
+ csr12_nway_state[(v >> 12) & 0x7],
+ v & (1<<11) ? " Transmit remote fault\n" : "",
+ v & (1<<10) ? " Unstable NLP detected\n" : "",
+ v & (1<<9) ? " Non-selected port receive activity\n" : "",
+ v & (1<<8) ? " Selected port receive activity\n" : "",
+ v & (1<<7) ? " PLL sampler high\n" : "",
+ v & (1<<6) ? " PLL sampler low\n" : "",
+ v & (1<<5) ? " PLL self-test pass\n" : "",
+ v & (1<<4) ? " PLL self-test done\n" : "",
+ v & (1<<3) ? " Autopolarity state\n" : "",
+ v & (1<<2) ? " Link fail\n" : "",
+ v & (1<<1) ? " Network connection error\n" : "");
+
+ /*
+ * CSR13
+ */
+ v = data[13];
+ fprintf(stdout,
+ "0x68: CSR13 (SIA Connectivity) 0x%08x\n"
+ " SIA Diagnostic Mode 0x%04x\n"
+ " %s\n"
+ "%s"
+ "%s"
+ ,
+ v,
+ (v >> 4) & 0xfff,
+ v & (1<<3) ? "AUI/BNC port" : "10base-T port",
+ v & (1<<2) ? " CSR autoconfiguration enabled\n" : "",
+ v & (1<<0) ? " SIA register reset asserted\n" : "");
+
+ /*
+ * CSR14
+ */
+ v = data[14];
+ fprintf(stdout,
+ "0x70: CSR14 (SIA Transmit and Receive) 0x%08x\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ " %s\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ ,
+ v,
+ v & (1<<15) ? " 10base-T/AUI autosensing\n" : "",
+ v & (1<<14) ? " Set polarity plus\n" : "",
+ v & (1<<13) ? " Autopolarity enable\n" : "",
+ v & (1<<12) ? " Link test enable\n" : "",
+ v & (1<<11) ? " Heartbeat enable\n" : "",
+ v & (1<<10) ? " Collision detect enable\n" : "",
+ v & (1<<9) ? " Collision squelch enable\n" : "",
+ v & (1<<8) ? " Receive squelch enable\n" : "",
+ v & (1<<7) ? " Autonegotiation enable\n" : "",
+ v & (1<<6) ? " Must Be One\n" : "",
+ csr14_tp_comp[(v >> 4) & 0x3],
+ v & (1<<3) ? " Link pulse send enable\n" : "",
+ v & (1<<2) ? " Driver enable\n" : "",
+ v & (1<<1) ? " Loopback enable\n" : "",
+ v & (1<<0) ? " Encoder enable\n" : "");
+
+ /*
+ * CSR15
+ */
+ v = data[15];
+ fprintf(stdout,
+ "0x78: CSR15 (SIA General) 0x%08x\n"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ "%s"
+ " %s port selected\n"
+ "%s"
+ "%s"
+ "%s"
+ ,
+ v,
+ v & (1<<15) ? " GP LED2 on\n" : "",
+ v & (1<<14) ? " GP LED2 enable\n" : "",
+ v & (1<<13) ? " Force receiver low\n" : "",
+ v & (1<<12) ? " PLL self-test start\n" : "",
+ v & (1<<11) ? " LED stretch disable\n" : "",
+ v & (1<<10) ? " Force link fail\n" : "",
+ v & (1<<9) ? " Force unsquelch\n" : "",
+ v & (1<<8) ? " Test clock\n" : "",
+ v & (1<<7) ? " GP LED1 on\n" : "",
+ v & (1<<6) ? " GP LED1 enable\n" : "",
+ v & (1<<5) ? " Receive watchdog release\n" : "",
+ v & (1<<4) ? " Receive watchdog disable\n" : "",
+ v & (1<<3) ? "AUI" : "BNC",
+ v & (1<<2) ? " Jabber clock\n" : "",
+ v & (1<<1) ? " Host unjab\n" : "",
+ v & (1<<0) ? " Jabber disable\n" : "");
+}
+
+int
+de2104x_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs)
+{
+ unsigned int de21040 = regs->version & 1;
+
+ if (de21040)
+ de21040_dump_regs(info, regs);
+ else
+ de21041_dump_regs(info, regs);
+
+ return 0;
+}
+
diff --git a/dsa.c b/dsa.c
new file mode 100644
index 0000000..33c1d39
--- /dev/null
+++ b/dsa.c
@@ -0,0 +1,882 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "internal.h"
+
+#define SERDES_OFFSET 32
+
+/* Macros and dump functions for the 16-bit mv88e6xxx per-port registers */
+
+#define REG(_reg, _name, _val) \
+ printf("%.02u: %-38.38s 0x%.4x\n", _reg, _name, _val)
+
+#define FIELD(_name, _fmt, ...) \
+ printf(" %-36.36s " _fmt "\n", _name, ##__VA_ARGS__)
+
+#define FIELD_BITMAP(_name, _val) \
+ FIELD(_name, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", \
+ ((_val) & 0x0001) ? "0 " : "", \
+ ((_val) & 0x0002) ? "1 " : "", \
+ ((_val) & 0x0004) ? "2 " : "", \
+ ((_val) & 0x0008) ? "3 " : "", \
+ ((_val) & 0x0010) ? "4 " : "", \
+ ((_val) & 0x0020) ? "5 " : "", \
+ ((_val) & 0x0040) ? "6 " : "", \
+ ((_val) & 0x0080) ? "7 " : "", \
+ ((_val) & 0x0100) ? "8 " : "", \
+ ((_val) & 0x0200) ? "9 " : "", \
+ ((_val) & 0x0400) ? "10 " : "", \
+ ((_val) & 0x0800) ? "11 " : "", \
+ ((_val) & 0x1000) ? "12 " : "", \
+ ((_val) & 0x2000) ? "13 " : "", \
+ ((_val) & 0x4000) ? "14 " : "", \
+ ((_val) & 0x8000) ? "15 " : "")
+
+static void dsa_mv88e6161(int reg, u16 val)
+{
+ switch (reg) {
+ case 0:
+ REG(reg, "Port Status", val);
+ FIELD("Pause Enabled", "%u", !!(val & 0x8000));
+ FIELD("My Pause", "%u", !!(val & 0x4000));
+ FIELD("Half-duplex Flow Control", "%u", !!(val & 0x2000));
+ FIELD("802.3 PHY Detected", "%u", !!(val & 0x1000));
+ FIELD("Link Status", "%s", val & 0x0800 ? "Up" : "Down");
+ FIELD("Duplex", "%s", val & 0x0400 ? "Full" : "Half");
+ FIELD("Speed", "%s",
+ (val & 0x0300) == 0x0000 ? "10 Mbps" :
+ (val & 0x0300) == 0x0100 ? "100 Mbps" :
+ (val & 0x0300) == 0x0200 ? "1000 Mbps" :
+ (val & 0x0300) == 0x0300 ? "Reserved" : "?");
+ FIELD("Auto-Media Detect Disable", "%u", !!(val & 0x0040));
+ FIELD("Transmitter Paused", "%u", !!(val & 0x0020));
+ FIELD("Flow Control", "%u", !!(val & 0x0010));
+ FIELD("Config Duplex", "%s", val & 0x0008 ? "Full" : "Half");
+ FIELD("Config Mode", "0x%x", val & 0x0007);
+ break;
+ case 1:
+ REG(reg, "PCS Control", val);
+ FIELD("Flow Control's Forced value", "%u", !!(val & 0x0080));
+ FIELD("Force Flow Control", "%u", !!(val & 0x0040));
+ FIELD("Link's Forced value", "%s", val & 0x0020 ? "Up" : "Down");
+ FIELD("Force Link", "%u", !!(val & 0x0010));
+ FIELD("Duplex's Forced value", "%s", val & 0x0008 ? "Full" : "Half");
+ FIELD("Force Duplex", "%u", !!(val & 0x0004));
+ FIELD("Force Speed", "%s",
+ (val & 0x0003) == 0x0000 ? "10 Mbps" :
+ (val & 0x0003) == 0x0001 ? "100 Mbps" :
+ (val & 0x0003) == 0x0002 ? "1000 Mbps" :
+ (val & 0x0003) == 0x0003 ? "Not forced" : "?");
+ break;
+ case 2:
+ REG(reg, "Jamming Control", val);
+ break;
+ case 3:
+ REG(reg, "Switch Identifier", val);
+ break;
+ case 4:
+ REG(reg, "Port Control", val);
+ FIELD("Source Address Filtering controls", "%s",
+ (val & 0xc000) == 0x0000 ? "Disabled" :
+ (val & 0xc000) == 0x4000 ? "Drop On Lock" :
+ (val & 0xc000) == 0x8000 ? "Drop On Unlock" :
+ (val & 0xc000) == 0xc000 ? "Drop to CPU" : "?");
+ FIELD("Egress Mode", "%s",
+ (val & 0x3000) == 0x0000 ? "Unmodified" :
+ (val & 0x3000) == 0x1000 ? "Untagged" :
+ (val & 0x3000) == 0x2000 ? "Tagged" :
+ (val & 0x3000) == 0x3000 ? "Reserved" : "?");
+ FIELD("Ingress & Egress Header Mode", "%u", !!(val & 0x0800));
+ FIELD("IGMP and MLD Snooping", "%u", !!(val & 0x0400));
+ FIELD("Frame Mode", "%s",
+ (val & 0x0300) == 0x0000 ? "Normal" :
+ (val & 0x0300) == 0x0100 ? "DSA" :
+ (val & 0x0300) == 0x0200 ? "Provider" :
+ (val & 0x0300) == 0x0300 ? "Ether Type DSA" : "?");
+ FIELD("VLAN Tunnel", "%u", !!(val & 0x0080));
+ FIELD("TagIfBoth", "%u", !!(val & 0x0040));
+ FIELD("Initial Priority assignment", "%s",
+ (val & 0x0030) == 0x0000 ? "Defaults" :
+ (val & 0x0030) == 0x0010 ? "Tag Priority" :
+ (val & 0x0030) == 0x0020 ? "IP Priority" :
+ (val & 0x0030) == 0x0030 ? "Tag & IP Priority" : "?");
+ FIELD("Egress Flooding mode", "%s",
+ (val & 0x000c) == 0x0000 ? "No unknown DA" :
+ (val & 0x000c) == 0x0004 ? "No unknown multicast DA" :
+ (val & 0x000c) == 0x0008 ? "No unknown unicast DA" :
+ (val & 0x000c) == 0x000c ? "Allow unknown DA" : "?");
+ FIELD("Port State", "%s",
+ (val & 0x0003) == 0x0000 ? "Disabled" :
+ (val & 0x0003) == 0x0001 ? "Blocking/Listening" :
+ (val & 0x0003) == 0x0002 ? "Learning" :
+ (val & 0x0003) == 0x0003 ? "Forwarding" : "?");
+ break;
+ case 5:
+ REG(reg, "Port Control 1", val);
+ FIELD("Message Port", "%u", !!(val & 0x8000));
+ FIELD("Trunk Port", "%u", !!(val & 0x4000));
+ FIELD("Trunk ID", "%u", (val & 0x0f00) >> 8);
+ FIELD("FID[5:4]", "0x%.2x", (val & 0x0003) << 4);
+ break;
+ case 6:
+ REG(reg, "Port Base VLAN Map (Header)", val);
+ FIELD("FID[3:0]", "0x%.2x", (val & 0xf000) >> 12);
+ FIELD_BITMAP("VLANTable", val & 0x003f);
+ break;
+ case 7:
+ REG(reg, "Default VLAN ID & Priority", val);
+ FIELD("Default Priority", "0x%x", (val & 0xe000) >> 13);
+ FIELD("Force to use Default VID", "%u", !!(val & 0x1000));
+ FIELD("Default VLAN Identifier", "%u", val & 0x0fff);
+ break;
+ case 8:
+ REG(reg, "Port Control 2", val);
+ FIELD("Force good FCS in the frame", "%u", !!(val & 0x8000));
+ FIELD("Jumbo Mode", "%s",
+ (val & 0x3000) == 0x0000 ? "1522" :
+ (val & 0x3000) == 0x1000 ? "2048" :
+ (val & 0x3000) == 0x2000 ? "10240" :
+ (val & 0x3000) == 0x3000 ? "Reserved" : "?");
+ FIELD("802.1QMode", "%s",
+ (val & 0x0c00) == 0x0000 ? "Disabled" :
+ (val & 0x0c00) == 0x0400 ? "Fallback" :
+ (val & 0x0c00) == 0x0800 ? "Check" :
+ (val & 0x0c00) == 0x0c00 ? "Secure" : "?");
+ FIELD("Discard Tagged Frames", "%u", !!(val & 0x0200));
+ FIELD("Discard Untagged Frames", "%u", !!(val & 0x0100));
+ FIELD("Map using DA hits", "%u", !!(val & 0x0080));
+ FIELD("ARP Mirror enable", "%u", !!(val & 0x0040));
+ FIELD("Egress Monitor Source Port", "%u", !!(val & 0x0020));
+ FIELD("Ingress Monitor Source Port", "%u", !!(val & 0x0010));
+ break;
+ case 9:
+ REG(reg, "Egress Rate Control", val);
+ break;
+ case 10:
+ REG(reg, "Egress Rate Control 2", val);
+ break;
+ case 11:
+ REG(reg, "Port Association Vector", val);
+ break;
+ case 12:
+ REG(reg, "Port ATU Control", val);
+ break;
+ case 13:
+ REG(reg, "Priority Override", val);
+ break;
+ case 15:
+ REG(reg, "PortEType", val);
+ break;
+ case 16:
+ REG(reg, "InDiscardsLo Frame Counter", val);
+ break;
+ case 17:
+ REG(reg, "InDiscardsHi Frame Counter", val);
+ break;
+ case 18:
+ REG(reg, "InFiltered Frame Counter", val);
+ break;
+ case 19:
+ REG(reg, "OutFiltered Frame Counter", val);
+ break;
+ case 24:
+ REG(reg, "Tag Remap 0-3", val);
+ break;
+ case 25:
+ REG(reg, "Tag Remap 4-7", val);
+ break;
+ case 27:
+ REG(reg, "Queue Counters", val);
+ break;
+ default:
+ REG(reg, "Reserved", val);
+ break;
+ }
+}
+
+static void dsa_mv88e6185(int reg, u16 val)
+{
+ switch (reg) {
+ case 0:
+ REG(reg, "Port Status", val);
+ break;
+ case 1:
+ REG(reg, "PCS Control", val);
+ break;
+ case 3:
+ REG(reg, "Switch Identifier", val);
+ break;
+ case 4:
+ REG(reg, "Port Control", val);
+ break;
+ case 5:
+ REG(reg, "Port Control 1", val);
+ break;
+ case 6:
+ REG(reg, "Port Base VLAN Map (Header)", val);
+ break;
+ case 7:
+ REG(reg, "Default VLAN ID & Priority", val);
+ break;
+ case 8:
+ REG(reg, "Port Control 2", val);
+ break;
+ case 9:
+ REG(reg, "Rate Control", val);
+ break;
+ case 10:
+ REG(reg, "Rate Control 2", val);
+ break;
+ case 11:
+ REG(reg, "Port Association Vector", val);
+ break;
+ case 16:
+ REG(reg, "InDiscardsLo Frame Counter", val);
+ break;
+ case 17:
+ REG(reg, "InDiscardsHi Frame Counter", val);
+ break;
+ case 18:
+ REG(reg, "InFiltered Frame Counter", val);
+ break;
+ case 19:
+ REG(reg, "OutFiltered Frame Counter", val);
+ break;
+ case 24:
+ REG(reg, "Tag Remap 0-3", val);
+ break;
+ case 25:
+ REG(reg, "Tag Remap 4-7", val);
+ break;
+ default:
+ REG(reg, "Reserved", val);
+ break;
+ }
+};
+
+static void dsa_mv88e6352(int reg, u16 val)
+{
+ switch (reg) {
+ case 0:
+ REG(reg, "Port Status", val);
+ FIELD("Pause Enabled", "%u", !!(val & 0x8000));
+ FIELD("My Pause", "%u", !!(val & 0x4000));
+ FIELD("802.3 PHY Detected", "%u", !!(val & 0x1000));
+ FIELD("Link Status", "%s", val & 0x0800 ? "Up" : "Down");
+ FIELD("Duplex", "%s", val & 0x0400 ? "Full" : "Half");
+ FIELD("Speed", "%s",
+ (val & 0x0300) == 0x0000 ? "10 Mbps" :
+ (val & 0x0300) == 0x0100 ? "100 or 200 Mbps" :
+ (val & 0x0300) == 0x0200 ? "1000 Mbps" :
+ (val & 0x0300) == 0x0300 ? "Reserved" : "?");
+ FIELD("EEE Enabled", "%u", !!(val & 0x0040));
+ FIELD("Transmitter Paused", "%u", !!(val & 0x0020));
+ FIELD("Flow Control", "%u", !!(val & 0x0010));
+ FIELD("Config Mode", "0x%x", val & 0x000f);
+ break;
+ case 1:
+ REG(reg, "Physical Control", val);
+ FIELD("RGMII Receive Timing Control", "%s", val & 0x8000 ? "Delay" : "Default");
+ FIELD("RGMII Transmit Timing Control", "%s", val & 0x4000 ? "Delay" : "Default");
+ FIELD("200 BASE Mode", "%s", val & 0x1000 ? "200" : "100");
+ FIELD("Flow Control's Forced value", "%u", !!(val & 0x0080));
+ FIELD("Force Flow Control", "%u", !!(val & 0x0040));
+ FIELD("Link's Forced value", "%s", val & 0x0020 ? "Up" : "Down");
+ FIELD("Force Link", "%u", !!(val & 0x0010));
+ FIELD("Duplex's Forced value", "%s", val & 0x0008 ? "Full" : "Half");
+ FIELD("Force Duplex", "%u", !!(val & 0x0004));
+ FIELD("Force Speed", "%s",
+ (val & 0x0003) == 0x0000 ? "10 Mbps" :
+ (val & 0x0003) == 0x0001 ? "100 or 200 Mbps" :
+ (val & 0x0003) == 0x0002 ? "1000 Mbps" :
+ (val & 0x0003) == 0x0003 ? "Not forced" : "?");
+ break;
+ case 2:
+ REG(reg, "Jamming Control", val);
+ break;
+ case 3:
+ REG(reg, "Switch Identifier", val);
+ break;
+ case 4:
+ REG(reg, "Port Control", val);
+ FIELD("Source Address Filtering controls", "%s",
+ (val & 0xc000) == 0x0000 ? "Disabled" :
+ (val & 0xc000) == 0x4000 ? "Drop On Lock" :
+ (val & 0xc000) == 0x8000 ? "Drop On Unlock" :
+ (val & 0xc000) == 0xc000 ? "Drop to CPU" : "?");
+ FIELD("Egress Mode", "%s",
+ (val & 0x3000) == 0x0000 ? "Unmodified" :
+ (val & 0x3000) == 0x1000 ? "Untagged" :
+ (val & 0x3000) == 0x2000 ? "Tagged" :
+ (val & 0x3000) == 0x3000 ? "Reserved" : "?");
+ FIELD("Ingress & Egress Header Mode", "%u", !!(val & 0x0800));
+ FIELD("IGMP and MLD Snooping", "%u", !!(val & 0x0400));
+ FIELD("Frame Mode", "%s",
+ (val & 0x0300) == 0x0000 ? "Normal" :
+ (val & 0x0300) == 0x0100 ? "DSA" :
+ (val & 0x0300) == 0x0200 ? "Provider" :
+ (val & 0x0300) == 0x0300 ? "Ether Type DSA" : "?");
+ FIELD("VLAN Tunnel", "%u", !!(val & 0x0080));
+ FIELD("TagIfBoth", "%u", !!(val & 0x0040));
+ FIELD("Initial Priority assignment", "%s",
+ (val & 0x0030) == 0x0000 ? "Defaults" :
+ (val & 0x0030) == 0x0010 ? "Tag Priority" :
+ (val & 0x0030) == 0x0020 ? "IP Priority" :
+ (val & 0x0030) == 0x0030 ? "Tag & IP Priority" : "?");
+ FIELD("Egress Flooding mode", "%s",
+ (val & 0x000c) == 0x0000 ? "No unknown DA" :
+ (val & 0x000c) == 0x0004 ? "No unknown multicast DA" :
+ (val & 0x000c) == 0x0008 ? "No unknown unicast DA" :
+ (val & 0x000c) == 0x000c ? "Allow unknown DA" : "?");
+ FIELD("Port State", "%s",
+ (val & 0x0003) == 0x0000 ? "Disabled" :
+ (val & 0x0003) == 0x0001 ? "Blocking/Listening" :
+ (val & 0x0003) == 0x0002 ? "Learning" :
+ (val & 0x0003) == 0x0003 ? "Forwarding" : "?");
+ break;
+ case 5:
+ REG(reg, "Port Control 1", val);
+ FIELD("Message Port", "%u", !!(val & 0x8000));
+ FIELD("Trunk Port", "%u", !!(val & 0x4000));
+ FIELD("Trunk ID", "%u", (val & 0x0f00) >> 8);
+ FIELD("FID[11:4]", "0x%.3x", (val & 0x00ff) << 4);
+ break;
+ case 6:
+ REG(reg, "Port Base VLAN Map (Header)", val);
+ FIELD("FID[3:0]", "0x%.3x", (val & 0xf000) >> 12);
+ FIELD_BITMAP("VLANTable", val & 0x007f);
+ break;
+ case 7:
+ REG(reg, "Default VLAN ID & Priority", val);
+ FIELD("Default Priority", "0x%x", (val & 0xe000) >> 13);
+ FIELD("Force to use Default VID", "%u", !!(val & 0x1000));
+ FIELD("Default VLAN Identifier", "%u", val & 0x0fff);
+ break;
+ case 8:
+ REG(reg, "Port Control 2", val);
+ FIELD("Force good FCS in the frame", "%u", !!(val & 0x8000));
+ FIELD("Jumbo Mode", "%s",
+ (val & 0x3000) == 0x0000 ? "1522" :
+ (val & 0x3000) == 0x1000 ? "2048" :
+ (val & 0x3000) == 0x2000 ? "10240" :
+ (val & 0x3000) == 0x3000 ? "Reserved" : "?");
+ FIELD("802.1QMode", "%s",
+ (val & 0x0c00) == 0x0000 ? "Disabled" :
+ (val & 0x0c00) == 0x0400 ? "Fallback" :
+ (val & 0x0c00) == 0x0800 ? "Check" :
+ (val & 0x0c00) == 0x0c00 ? "Secure" : "?");
+ FIELD("Discard Tagged Frames", "%u", !!(val & 0x0200));
+ FIELD("Discard Untagged Frames", "%u", !!(val & 0x0100));
+ FIELD("Map using DA hits", "%u", !!(val & 0x0080));
+ FIELD("ARP Mirror enable", "%u", !!(val & 0x0040));
+ FIELD("Egress Monitor Source Port", "%u", !!(val & 0x0020));
+ FIELD("Ingress Monitor Source Port", "%u", !!(val & 0x0010));
+ FIELD("Use Default Queue Priority", "%u", !!(val & 0x0008));
+ FIELD("Default Queue Priority", "0x%x", (val & 0x0006) >> 1);
+ break;
+ case 9:
+ REG(reg, "Egress Rate Control", val);
+ break;
+ case 10:
+ REG(reg, "Egress Rate Control 2", val);
+ break;
+ case 11:
+ REG(reg, "Port Association Vector", val);
+ break;
+ case 12:
+ REG(reg, "Port ATU Control", val);
+ break;
+ case 13:
+ REG(reg, "Override", val);
+ break;
+ case 14:
+ REG(reg, "Policy Control", val);
+ break;
+ case 15:
+ REG(reg, "Port Ether Type", val);
+ break;
+ case 16:
+ REG(reg, "InDiscardsLo Frame Counter", val);
+ break;
+ case 17:
+ REG(reg, "InDiscardsHi Frame Counter", val);
+ break;
+ case 18:
+ REG(reg, "InFiltered/TcamCtr Frame Counter", val);
+ break;
+ case 19:
+ REG(reg, "Rx Frame Counter", val);
+ break;
+ case 20 ... 21:
+ REG(reg, "Reserved", val);
+ break;
+ case 22:
+ REG(reg, "LED Control", val);
+ break;
+ case 23:
+ REG(reg, "Reserved", val);
+ break;
+ case 24:
+ REG(reg, "Tag Remap 0-3", val);
+ break;
+ case 25:
+ REG(reg, "Tag Remap 4-7", val);
+ break;
+ case 26:
+ REG(reg, "Reserved", val);
+ break;
+ case 27:
+ REG(reg, "Queue Counters", val);
+ break;
+ case 28 ... 31:
+ REG(reg, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 0:
+ REG(reg - SERDES_OFFSET, "Fiber Control", val);
+ FIELD("Fiber Reset", "%u", !!(val & 0x8000));
+ FIELD("Loopback", "%u", !!(val & 0x4000));
+ FIELD("Speed", "%s",
+ (val & (0x2000 | 0x0040)) == 0x0000 ? "10 Mbps" :
+ (val & (0x2000 | 0x0040)) == 0x2000 ? "100 Mbps" :
+ (val & (0x2000 | 0x0040)) == 0x0040 ? "1000 Mbps" :
+ (val & (0x2000 | 0x0040)) == (0x2000 | 0x0040) ?
+ "Reserved" : "?");
+ FIELD("Autoneg Enable", "%u", !!(val & 0x1000));
+ FIELD("Power down", "%u", !!(val & 0x0800));
+ FIELD("Isolate", "%u", !!(val & 0x0400));
+ FIELD("Restart Autoneg", "%u", !!(val & 0x0200));
+ FIELD("Duplex", "%s", val & 0x0100 ? "Full" : "Half");
+ break;
+ case SERDES_OFFSET + 1:
+ REG(reg - SERDES_OFFSET, "Fiber Status", val);
+ FIELD("100Base-X FD", "%u", !!(val & 0x4000));
+ FIELD("100Base-X HD", "%u", !!(val & 0x2000));
+ FIELD("Autoneg Complete", "%u", !!(val & 0x0020));
+ FIELD("Remote Fault", "%u", !!(val & 0x0010));
+ FIELD("Autoneg Ability", "%u", !!(val & 0x0008));
+ FIELD("Link Status", "%s", val & 0x0004 ? "Up" : "Down");
+ break;
+ case SERDES_OFFSET + 2:
+ REG(reg - SERDES_OFFSET, "PHY ID 1", val);
+ break;
+ case SERDES_OFFSET + 3:
+ REG(reg - SERDES_OFFSET, "PHY ID 2", val);
+ break;
+ case SERDES_OFFSET + 4:
+ REG(reg - SERDES_OFFSET, "Fiber Autoneg Advertisement", val);
+ FIELD("Remote Fault", "%s",
+ (val & 0x3000) == 0x0000 ? "No error, link OK" :
+ (val & 0x3000) == 0x1000 ? "Link failure" :
+ (val & 0x3000) == 0x2000 ? "Offline" :
+ (val & 0x3000) == 0x3000 ? "Autoneg Error" : "?");
+ FIELD("Pause", "%s",
+ (val & 0x0180) == 0x0000 ? "No Pause" :
+ (val & 0x0180) == 0x0080 ? "Symmetric Pause" :
+ (val & 0x0180) == 0x0100 ? "Asymmetric Pause" :
+ (val & 0x0180) == 0x0180 ? "Symmetric & Asymmetric Pause" :
+ "?");
+ FIELD("1000BaseX HD", "%u", !!(val & 0x0040));
+ FIELD("1000BaseX FD", "%u", !!(val & 0x0020));
+ break;
+ case SERDES_OFFSET + 5:
+ REG(reg - SERDES_OFFSET, "Fiber Link Autoneg Ability", val);
+ FIELD("Acknowledge", "%u", !!(val & 0x4000));
+ FIELD("Remote Fault", "%s",
+ (val & 0x3000) == 0x0000 ? "No error, link OK" :
+ (val & 0x3000) == 0x1000 ? "Link failure" :
+ (val & 0x3000) == 0x2000 ? "Offline" :
+ (val & 0x3000) == 0x3000 ? "Autoneg Error" : "?");
+ FIELD("Pause", "%s",
+ (val & 0x0180) == 0x0000 ? "No Pause" :
+ (val & 0x0180) == 0x0080 ? "Symmetric Pause" :
+ (val & 0x0180) == 0x0100 ? "Asymmetric Pause" :
+ (val & 0x0180) == 0x0180 ? "Symmetric & Asymmetric Pause" :
+ "?");
+ FIELD("1000BaseX HD", "%u", !!(val & 0x0040));
+ FIELD("1000BaseX FD", "%u", !!(val & 0x0020));
+ break;
+ case SERDES_OFFSET + 6:
+ REG(reg - SERDES_OFFSET, "Fiber Autoneg Expansion", val);
+ FIELD("Link Partner Next Page Ability", "%u", !!(val & 0x0008));
+ FIELD("Page Received", "%u", !!(val & 0x0002));
+ FIELD("Link Partner Autoneg Ability", "%u", !!(val & 0x0001));
+ break;
+ case SERDES_OFFSET + 7:
+ REG(reg - SERDES_OFFSET, "Fiber Next Page Transmit", val);
+ break;
+ case SERDES_OFFSET + 8:
+ REG(reg - SERDES_OFFSET, "Fiber Link Partner Next Page", val);
+ break;
+ case SERDES_OFFSET + 9 ... SERDES_OFFSET + 14:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 15:
+ REG(reg - SERDES_OFFSET, "Extended Status", val);
+ break;
+ case SERDES_OFFSET + 16:
+ REG(reg - SERDES_OFFSET, "Fiber Specific Control", val);
+ FIELD("Fiber Transmit FIFO Depth", "%s",
+ (val & 0xc000) == 0x0000 ? "16 Bits" :
+ (val & 0xc000) == 0x4000 ? "24 Bits" :
+ (val & 0xc000) == 0x8000 ? "32 Bits" :
+ (val & 0xc000) == 0xc000 ? "40 Bits" : "?");
+ FIELD("SERDES Loopback", "%u", !!(val & 0x1000));
+ FIELD("Force Link Good", "%u", !!(val & 0x0400));
+ FIELD("MAC Interface Power Down", "%u", !!(val & 0x0008));
+ FIELD("Mode", "%s",
+ (val & 0x0003) == 0x0000 ? "100BaseFX" :
+ (val & 0x0003) == 0x0001 ? "1000BaseX" :
+ (val & 0x0003) == 0x0002 ? "SGMII System" :
+ (val & 0x0003) == 0x0003 ? "SGMII Media" : "?");
+ break;
+ case SERDES_OFFSET + 17:
+ REG(reg - SERDES_OFFSET, "Fiber Specific Status", val);
+ FIELD("Speed", "%s",
+ (val & 0xc000) == 0x0000 ? "10 Mbps" :
+ (val & 0xc000) == 0x4000 ? "100 Mbps" :
+ (val & 0xc000) == 0x8000 ? "1000 Mbps" :
+ (val & 0xc000) == 0xc000 ? "Reserved" : "?");
+ FIELD("Duplex", "%s", val & 0x2000 ? "Full" : "Half");
+ FIELD("Page Received", "%u", !!(val & 0x1000));
+ FIELD("Speed/Duplex Resolved", "%u", !!(val & 0x0800));
+ FIELD("Link", "%s", val & 0x0400 ? "Up" : "Down");
+ FIELD("Sync", "%u", !!(val & 0x0020));
+ FIELD("Energy Detect", "%s", val & 0x010 ? "False" : "True");
+ FIELD("Transmit Pause", "%u", !!(val & 0x0008));
+ FIELD("Receive Pause", "%u", !!(val & 0x00004));
+ break;
+ case SERDES_OFFSET + 18:
+ REG(reg - SERDES_OFFSET, "Fiber Interrupt Enable", val);
+ FIELD("Speed Changed", "%u", !!(val & 0x4000));
+ FIELD("Duplex Changed", "%u", !!(val & 0x2000));
+ FIELD("Page Received", "%u", !!(val & 0x1000));
+ FIELD("Autoneg Complete", "%u", !!(val & 0x0800));
+ FIELD("Link Status Change", "%u", !!(val & 0x0400));
+ FIELD("Symbol Error", "%u", !!(val & 0x0200));
+ FIELD("False Carrier", "%u", !!(val & 0x0100));
+ FIELD("Energy Detect", "%u", !!(val & 0x0010));
+ break;
+ case SERDES_OFFSET + 19:
+ REG(reg - SERDES_OFFSET, "Fiber Interrupt Status", val);
+ FIELD("Speed Changed", "%u", !!(val & 0x4000));
+ FIELD("Duplex Changed", "%u", !!(val & 0x2000));
+ FIELD("Page Received", "%u", !!(val & 0x1000));
+ FIELD("Autoneg Complete", "%u", !!(val & 0x0800));
+ FIELD("Link Status Change", "%u", !!(val & 0x0400));
+ FIELD("Symbol Error", "%u", !!(val & 0x0200));
+ FIELD("False Carrier", "%u", !!(val & 0x0100));
+ FIELD("Energy Detect", "%u", !!(val & 0x0010));
+ break;
+ case SERDES_OFFSET + 20:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 21:
+ REG(reg - SERDES_OFFSET, "Fiber Receive Error Counter", val);
+ break;
+ case SERDES_OFFSET + 22:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ case SERDES_OFFSET + 23:
+ REG(reg - SERDES_OFFSET, "PRBS Control", val);
+ break;
+ case SERDES_OFFSET + 24:
+ REG(reg - SERDES_OFFSET, "PRBS Error Counter LSB", val);
+ break;
+ case SERDES_OFFSET + 25:
+ REG(reg - SERDES_OFFSET, "PRBS Error Counter MSB", val);
+ break;
+ case SERDES_OFFSET + 26:
+ REG(reg - SERDES_OFFSET, "Fiber Specific Control 2", val);
+ FIELD("1000BaseX Noise Filtering", "%u", !!(val & 0x4000));
+ FIELD("1000BaseFX Noise Filtering", "%u", !!(val & 0x2000));
+ FIELD("SERDES Autoneg Bypass Enable", "%u", !!(val & 0x0040));
+ FIELD("SERDES Autoneg Bypass Status", "%u", !!(val & 0x0020));
+ FIELD("Fiber Transmitter Disable", "%u", !!(val & 0x0008));
+ FIELD("SGMII/Fiber Output Amplitude", "%s",
+ (val & 0x0007) == 0x0000 ? "14mV" :
+ (val & 0x0007) == 0x0001 ? "112mV" :
+ (val & 0x0007) == 0x0002 ? "210mV" :
+ (val & 0x0007) == 0x0003 ? "308mV" :
+ (val & 0x0007) == 0x0004 ? "406mV" :
+ (val & 0x0007) == 0x0005 ? "504mV" :
+ (val & 0x0007) == 0x0006 ? "602mV" :
+ (val & 0x0007) == 0x0007 ? "700mV" : "?");
+ break;
+ default:
+ REG(reg - SERDES_OFFSET, "Reserved", val);
+ break;
+ }
+};
+
+static void dsa_mv88e6390(int reg, u16 val)
+{
+ switch (reg) {
+ case 0:
+ REG(reg, "Port Status", val);
+ FIELD("Transmit Pause Enable bit", "%u", !!(val & 0x8000));
+ FIELD("Receive Pause Enable bit", "%u", !!(val & 0x4000));
+ FIELD("802.3 PHY Detected", "%u", !!(val & 0x1000));
+ FIELD("Link Status", "%s", val & 0x0800 ? "Up" : "Down");
+ FIELD("Duplex", "%s", val & 0x0400 ? "Full" : "Half");
+ FIELD("Speed", "%s",
+ (val & 0x0300) == 0x0000 ? "10 Mbps" :
+ (val & 0x0300) == 0x0100 ? "100 or 200 Mbps" :
+ (val & 0x0300) == 0x0200 ? "1000 Mbps" :
+ (val & 0x0300) == 0x0300 ? "10 Gb or 2500 Mbps" : "?");
+ FIELD("Duplex Fixed", "%u", !!(val & 0x0080));
+ FIELD("EEE Enabled", "%u", !!(val & 0x0040));
+ FIELD("Transmitter Paused", "%u", !!(val & 0x0020));
+ FIELD("Flow Control", "%u", !!(val & 0x0010));
+ FIELD("Config Mode", "0x%x", val & 0x000f);
+ break;
+ case 1:
+ REG(reg, "Physical Control", val);
+ FIELD("RGMII Receive Timing Control", "%s", val & 0x8000 ? "Delay" : "Default");
+ FIELD("RGMII Transmit Timing Control", "%s", val & 0x4000 ? "Delay" : "Default");
+ FIELD("Force Speed", "%u", !!(val & 0x2000));
+ FIELD("Alternate Speed Mode", "%s", val & 0x1000 ? "Alternate" : "Normal");
+ FIELD("MII PHY Mode", "%s", val & 0x0800 ? "PHY" : "MAC");
+ FIELD("EEE force value", "%u", !!(val & 0x0200));
+ FIELD("Force EEE", "%u", !!(val & 0x0100));
+ FIELD("Link's Forced value", "%s", val & 0x0020 ? "Up" : "Down");
+ FIELD("Force Link", "%u", !!(val & 0x0010));
+ FIELD("Duplex's Forced value", "%s", val & 0x0008 ? "Full" : "Half");
+ FIELD("Force Duplex", "%u", !!(val & 0x0004));
+ FIELD("Force Speed", "%s",
+ (val & 0x0003) == 0x0000 ? "10 Mbps" :
+ (val & 0x0003) == 0x0001 ? "100 or 200 Mbps" :
+ (val & 0x0003) == 0x0002 ? "1000 Mbps" :
+ (val & 0x0003) == 0x0003 ? "10 Gb or 2500 Mbps" : "?");
+ break;
+ case 2:
+ REG(reg, "Flow Control", val);
+ break;
+ case 3:
+ REG(reg, "Switch Identifier", val);
+ break;
+ case 4:
+ REG(reg, "Port Control", val);
+ FIELD("Source Address Filtering controls", "%s",
+ (val & 0xc000) == 0x0000 ? "Disabled" :
+ (val & 0xc000) == 0x4000 ? "Drop On Lock" :
+ (val & 0xc000) == 0x8000 ? "Drop On Unlock" :
+ (val & 0xc000) == 0xc000 ? "Drop to CPU" : "?");
+ FIELD("Egress Mode", "%s",
+ (val & 0x3000) == 0x0000 ? "Unmodified" :
+ (val & 0x3000) == 0x1000 ? "Untagged" :
+ (val & 0x3000) == 0x2000 ? "Tagged" :
+ (val & 0x3000) == 0x3000 ? "Reserved" : "?");
+ FIELD("Ingress & Egress Header Mode", "%u", !!(val & 0x0800));
+ FIELD("IGMP and MLD Snooping", "%u", !!(val & 0x0400));
+ FIELD("Frame Mode", "%s",
+ (val & 0x0300) == 0x0000 ? "Normal" :
+ (val & 0x0300) == 0x0100 ? "DSA" :
+ (val & 0x0300) == 0x0200 ? "Provider" :
+ (val & 0x0300) == 0x0300 ? "Ether Type DSA" : "?");
+ FIELD("VLAN Tunnel", "%u", !!(val & 0x0080));
+ FIELD("TagIfBoth", "%u", !!(val & 0x0040));
+ FIELD("Initial Priority assignment", "%s",
+ (val & 0x0030) == 0x0000 ? "Defaults" :
+ (val & 0x0030) == 0x0010 ? "Tag Priority" :
+ (val & 0x0030) == 0x0020 ? "IP Priority" :
+ (val & 0x0030) == 0x0030 ? "Tag & IP Priority" : "?");
+ FIELD("Egress Flooding mode", "%s",
+ (val & 0x000c) == 0x0000 ? "No unknown DA" :
+ (val & 0x000c) == 0x0004 ? "No unknown multicast DA" :
+ (val & 0x000c) == 0x0008 ? "No unknown unicast DA" :
+ (val & 0x000c) == 0x000c ? "Allow unknown DA" : "?");
+ FIELD("Port State", "%s",
+ (val & 0x0003) == 0x0000 ? "Disabled" :
+ (val & 0x0003) == 0x0001 ? "Blocking/Listening" :
+ (val & 0x0003) == 0x0002 ? "Learning" :
+ (val & 0x0003) == 0x0003 ? "Forwarding" : "?");
+ break;
+ case 5:
+ REG(reg, "Port Control 1", val);
+ FIELD("Message Port", "%u", !!(val & 0x8000));
+ FIELD("LAG Port", "%u", !!(val & 0x4000));
+ FIELD("VTU Page", "%u", !!(val & 0x2000));
+ FIELD("LAG ID", "%u", (val & 0x0f00) >> 8);
+ FIELD("FID[11:4]", "0x%.3x", (val & 0x00ff) << 4);
+ break;
+ case 6:
+ REG(reg, "Port Base VLAN Map (Header)", val);
+ FIELD("FID[3:0]", "0x%.3x", (val & 0xf000) >> 12);
+ FIELD("Force Mapping", "%u", !!(val & 0x0800));
+ FIELD_BITMAP("VLANTable", val & 0x007ff);
+ break;
+ case 7:
+ REG(reg, "Default VLAN ID & Priority", val);
+ FIELD("Default Priority", "0x%x", (val & 0xe000) >> 13);
+ FIELD("Force to use Default VID", "%u", !!(val & 0x1000));
+ FIELD("Default VLAN Identifier", "%u", val & 0x0fff);
+ break;
+ case 8:
+ REG(reg, "Port Control 2", val);
+ FIELD("Force good FCS in the frame", "%u", !!(val & 0x8000));
+ FIELD("Allow bad FCS", "%u", !!(val & 0x4000));
+ FIELD("Jumbo Mode", "%s",
+ (val & 0x3000) == 0x0000 ? "1522" :
+ (val & 0x3000) == 0x1000 ? "2048" :
+ (val & 0x3000) == 0x2000 ? "10240" :
+ (val & 0x3000) == 0x3000 ? "Reserved" : "?");
+ FIELD("802.1QMode", "%s",
+ (val & 0x0c00) == 0x0000 ? "Disabled" :
+ (val & 0x0c00) == 0x0400 ? "Fallback" :
+ (val & 0x0c00) == 0x0800 ? "Check" :
+ (val & 0x0c00) == 0x0c00 ? "Secure" : "?");
+ FIELD("Discard Tagged Frames", "%u", !!(val & 0x0200));
+ FIELD("Discard Untagged Frames", "%u", !!(val & 0x0100));
+ FIELD("Map using DA hits", "%u", !!(val & 0x0080));
+ FIELD("ARP Mirror enable", "%u", !!(val & 0x0040));
+ FIELD("Egress Monitor Source Port", "%u", !!(val & 0x0020));
+ FIELD("Ingress Monitor Source Port", "%u", !!(val & 0x0010));
+ FIELD("Allow VID of Zero", "%u", !!(val & 0x0008));
+ FIELD("Default Queue Priority", "0x%x", val & 0x0007);
+ break;
+ case 9:
+ REG(reg, "Egress Rate Control", val);
+ break;
+ case 10:
+ REG(reg, "Egress Rate Control 2", val);
+ break;
+ case 11:
+ REG(reg, "Port Association Vector", val);
+ break;
+ case 12:
+ REG(reg, "Port ATU Control", val);
+ break;
+ case 13:
+ REG(reg, "Override", val);
+ break;
+ case 14:
+ REG(reg, "Policy Control", val);
+ break;
+ case 15:
+ REG(reg, "Port Ether Type", val);
+ break;
+ case 22:
+ REG(reg, "LED Control", val);
+ break;
+ case 23:
+ REG(reg, "IP Priority Mapping Table", val);
+ break;
+ case 24:
+ REG(reg, "IEEE Priority Mapping Table", val);
+ break;
+ case 25:
+ REG(reg, "Port Control 3", val);
+ break;
+ case 27:
+ REG(reg, "Queue Counters", val);
+ break;
+ case 28:
+ REG(reg, "Queue Control", val);
+ break;
+ case 30:
+ REG(reg, "Cut Through Control", val);
+ break;
+ case 31:
+ REG(reg, "Debug Counters", val);
+ break;
+ default:
+ REG(reg, "Reserved", val);
+ break;
+ }
+};
+
+struct dsa_mv88e6xxx_switch {
+ void (*dump)(int reg, u16 val);
+ const char *name;
+ u16 id;
+};
+
+static const struct dsa_mv88e6xxx_switch dsa_mv88e6xxx_switches[] = {
+ { .id = 0x04a0, .name = "88E6085 ", .dump = NULL },
+ { .id = 0x0950, .name = "88E6095 ", .dump = NULL },
+ { .id = 0x0990, .name = "88E6097 ", .dump = NULL },
+ { .id = 0x0a00, .name = "88E6190X", .dump = dsa_mv88e6390 },
+ { .id = 0x0a10, .name = "88E6390X", .dump = dsa_mv88e6390 },
+ { .id = 0x1060, .name = "88E6131 ", .dump = NULL },
+ { .id = 0x1150, .name = "88E6320 ", .dump = NULL },
+ { .id = 0x1210, .name = "88E6123 ", .dump = dsa_mv88e6161 },
+ { .id = 0x1610, .name = "88E6161 ", .dump = dsa_mv88e6161 },
+ { .id = 0x1650, .name = "88E6165 ", .dump = NULL },
+ { .id = 0x1710, .name = "88E6171 ", .dump = NULL },
+ { .id = 0x1720, .name = "88E6172 ", .dump = dsa_mv88e6352 },
+ { .id = 0x1750, .name = "88E6175 ", .dump = NULL },
+ { .id = 0x1760, .name = "88E6176 ", .dump = dsa_mv88e6352 },
+ { .id = 0x1900, .name = "88E6190 ", .dump = dsa_mv88e6390 },
+ { .id = 0x1910, .name = "88E6191 ", .dump = NULL },
+ { .id = 0x1a70, .name = "88E6185 ", .dump = dsa_mv88e6185 },
+ { .id = 0x2400, .name = "88E6240 ", .dump = dsa_mv88e6352 },
+ { .id = 0x2900, .name = "88E6290 ", .dump = dsa_mv88e6390 },
+ { .id = 0x3100, .name = "88E6321 ", .dump = NULL },
+ { .id = 0x3400, .name = "88E6141 ", .dump = NULL },
+ { .id = 0x3410, .name = "88E6341 ", .dump = NULL },
+ { .id = 0x3520, .name = "88E6352 ", .dump = dsa_mv88e6352 },
+ { .id = 0x3710, .name = "88E6350 ", .dump = NULL },
+ { .id = 0x3750, .name = "88E6351 ", .dump = NULL },
+ { .id = 0x3900, .name = "88E6390 ", .dump = dsa_mv88e6390 },
+};
+
+static int dsa_mv88e6xxx_dump_regs(struct ethtool_regs *regs)
+{
+ const struct dsa_mv88e6xxx_switch *sw = NULL;
+ const u16 *data = (u16 *)regs->data;
+ unsigned int i;
+ u16 id;
+
+ /* Marvell chips have 32 per-port 16-bit registers */
+ if (regs->len < 32 * sizeof(u16))
+ return 1;
+
+ id = regs->version & 0xfff0;
+
+ for (i = 0; i < ARRAY_SIZE(dsa_mv88e6xxx_switches); i++) {
+ if (id == dsa_mv88e6xxx_switches[i].id) {
+ sw = &dsa_mv88e6xxx_switches[i];
+ break;
+ }
+ }
+
+ if (!sw)
+ return 1;
+
+ printf("%s Switch Port Registers\n", sw->name);
+ printf("------------------------------\n");
+
+ for (i = 0; i < 32; i++)
+ if (sw->dump)
+ sw->dump(i, data[i]);
+ else
+ REG(i, "", data[i]);
+
+ /* Dump the SERDES registers, if provided */
+ if (regs->len > SERDES_OFFSET * sizeof(u16)) {
+ printf("\n%s Switch Port SERDES Registers\n", sw->name);
+ printf("-------------------------------------\n");
+ for (i = SERDES_OFFSET; i < regs->len / 2; i++)
+ if (sw->dump)
+ sw->dump(i, data[i]);
+ else
+ REG(i - SERDES_OFFSET, "", data[i]);
+ }
+
+ return 0;
+}
+
+#undef FIELD_BITMAP
+#undef FIELD
+#undef REG
+
+int dsa_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ /* DSA per-driver register dump */
+ if (!dsa_mv88e6xxx_dump_regs(regs))
+ return 0;
+
+ /* Fallback to hexdump */
+ return 1;
+}
diff --git a/e100.c b/e100.c
new file mode 100644
index 0000000..fd4bd03
--- /dev/null
+++ b/e100.c
@@ -0,0 +1,238 @@
+/* Copyright (c) 2002 Intel Corporation */
+#include <stdio.h>
+#include "internal.h"
+
+#define D102_REV_ID 12
+
+#define MDI_MDIX_CONFIG_IS_OK 0x0010
+#define MDI_MDIX_STATUS 0x0020
+
+#define SOFT_INT 0x0200 /* Generate a S/W interrupt */
+
+/* Interrupt masks */
+#define ALL_INT_MASK 0x0100 /* Mask interrupts */
+#define FCP_INT_MASK 0x0400 /* Flow Control Pause */
+#define ER_INT_MASK 0x0800 /* Early Receive */
+#define RNR_INT_MASK 0x1000 /* RU Not Ready */
+#define CNA_INT_MASK 0x2000 /* CU Not Active */
+#define FR_INT_MASK 0x4000 /* Frame Received */
+#define CX_INT_MASK 0x8000 /* CU eXecution w/ I-bit done */
+
+/* Interrupts pending */
+#define FCP_INT_PENDING 0x0100 /* Flow Control Pause */
+#define ER_INT_PENDING 0x0200 /* Early Receive */
+#define SWI_INT_PENDING 0x0400 /* S/W generated interrupt */
+#define MDI_INT_PENDING 0x0800 /* MDI read or write done */
+#define RNR_INT_PENDING 0x1000 /* RU Became Not Ready */
+#define CNA_INT_PENDING 0x2000 /* CU Became Inactive (IDLE) */
+#define FR_INT_PENDING 0x4000 /* RU Received A Frame */
+#define CX_INT_PENDING 0x8000 /* CU Completed Action Cmd */
+
+/* Status */
+#define CU_STATUS 0x00C0
+#define RU_STATUS 0x003C
+
+/* Commands */
+#define CU_CMD 0x00F0
+#define RU_CMD 0x0007
+
+int e100_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *regs_buff = (u32 *)regs->data;
+ u8 version = (u8)(regs->version >> 24);
+ u8 rev_id = (u8)(regs->version);
+ u8 regs_len = regs->len / sizeof(u32);
+ u32 reg;
+ u16 scb_status, scb_cmd;
+
+ if (version != 1)
+ return -1;
+
+ reg = regs_buff[0];
+ scb_status = reg & 0x0000ffff;
+ scb_cmd = reg >> 16;
+ fprintf(stdout,
+ "SCB Status Word (Lower Word) 0x%04X\n",
+ scb_status);
+
+ switch ((scb_status & RU_STATUS) >> 2) {
+ case 0:
+ fprintf(stdout,
+ " RU Status: Idle\n");
+ break;
+ case 1:
+ fprintf(stdout,
+ " RU Status: Suspended\n");
+ break;
+ case 2:
+ fprintf(stdout,
+ " RU Status: No Resources\n");
+ break;
+ case 4:
+ fprintf(stdout,
+ " RU Status: Ready\n");
+ break;
+ case 9:
+ fprintf(stdout,
+ " RU Status: Suspended with no more RBDs\n");
+ break;
+ case 10:
+ fprintf(stdout,
+ " RU Status: No Resources due to no more RBDs\n");
+ break;
+ case 12:
+ fprintf(stdout,
+ " RU Status: Ready with no RBDs present\n");
+ break;
+ default:
+ fprintf(stdout,
+ " RU Status: Unknown State\n");
+ break;
+ }
+
+ switch ((scb_status & CU_STATUS) >> 6) {
+ case 0:
+ fprintf(stdout,
+ " CU Status: Idle\n");
+ break;
+ case 1:
+ fprintf(stdout,
+ " CU Status: Suspended\n");
+ break;
+ case 2:
+ fprintf(stdout,
+ " CU Status: Active\n");
+ break;
+ default:
+ fprintf(stdout,
+ " CU Status: Unknown State\n");
+ break;
+ }
+
+ fprintf(stdout,
+ " ---- Interrupts Pending ----\n"
+ " Flow Control Pause: %s\n"
+ " Early Receive: %s\n"
+ " Software Generated Interrupt: %s\n"
+ " MDI Done: %s\n"
+ " RU Not In Ready State: %s\n"
+ " CU Not in Active State: %s\n"
+ " RU Received Frame: %s\n"
+ " CU Completed Command: %s\n",
+ scb_status & FCP_INT_PENDING ? "yes" : "no",
+ scb_status & ER_INT_PENDING ? "yes" : "no",
+ scb_status & SWI_INT_PENDING ? "yes" : "no",
+ scb_status & MDI_INT_PENDING ? "yes" : "no",
+ scb_status & RNR_INT_PENDING ? "yes" : "no",
+ scb_status & CNA_INT_PENDING ? "yes" : "no",
+ scb_status & FR_INT_PENDING ? "yes" : "no",
+ scb_status & CX_INT_PENDING ? "yes" : "no");
+
+ fprintf(stdout,
+ "SCB Command Word (Upper Word) 0x%04X\n",
+ scb_cmd);
+
+ switch (scb_cmd & RU_CMD) {
+ case 0:
+ fprintf(stdout,
+ " RU Command: No Command\n");
+ break;
+ case 1:
+ fprintf(stdout,
+ " RU Command: RU Start\n");
+ break;
+ case 2:
+ fprintf(stdout,
+ " RU Command: RU Resume\n");
+ break;
+ case 4:
+ fprintf(stdout,
+ " RU Command: RU Abort\n");
+ break;
+ case 6:
+ fprintf(stdout,
+ " RU Command: Load RU Base\n");
+ break;
+ default:
+ fprintf(stdout,
+ " RU Command: Unknown\n");
+ break;
+ }
+
+ switch ((scb_cmd & CU_CMD) >> 4) {
+ case 0:
+ fprintf(stdout,
+ " CU Command: No Command\n");
+ break;
+ case 1:
+ fprintf(stdout,
+ " CU Command: CU Start\n");
+ break;
+ case 2:
+ fprintf(stdout,
+ " CU Command: CU Resume\n");
+ break;
+ case 4:
+ fprintf(stdout,
+ " CU Command: Load Dump Counters Address\n");
+ break;
+ case 5:
+ fprintf(stdout,
+ " CU Command: Dump Counters\n");
+ break;
+ case 6:
+ fprintf(stdout,
+ " CU Command: Load CU Base\n");
+ break;
+ case 7:
+ fprintf(stdout,
+ " CU Command: Dump & Reset Counters\n");
+ break;
+ default:
+ fprintf(stdout,
+ " CU Command: Unknown\n");
+ break;
+ }
+
+ fprintf(stdout,
+ " Software Generated Interrupt: %s\n",
+ scb_cmd & SOFT_INT ? "yes" : "no");
+
+ fprintf(stdout,
+ " ---- Interrupts Masked ----\n"
+ " ALL Interrupts: %s\n"
+ " Flow Control Pause: %s\n"
+ " Early Receive: %s\n"
+ " RU Not In Ready State: %s\n"
+ " CU Not in Active State: %s\n"
+ " RU Received Frame: %s\n"
+ " CU Completed Command: %s\n",
+ scb_cmd & ALL_INT_MASK ? "yes" : "no",
+ scb_cmd & FCP_INT_MASK ? "yes" : "no",
+ scb_cmd & ER_INT_MASK ? "yes" : "no",
+ scb_cmd & RNR_INT_MASK ? "yes" : "no",
+ scb_cmd & CNA_INT_MASK ? "yes" : "no",
+ scb_cmd & FR_INT_MASK ? "yes" : "no",
+ scb_cmd & CX_INT_MASK ? "yes" : "no");
+
+ if(regs_len > 1) {
+ fprintf(stdout, "MDI/MDI-X Status: ");
+ if(rev_id < D102_REV_ID)
+ fprintf(stdout, "MDI\n");
+ else {
+ u16 ctrl_reg = regs_buff[1];
+
+ if(ctrl_reg & MDI_MDIX_CONFIG_IS_OK) {
+ if(ctrl_reg & MDI_MDIX_STATUS)
+ fprintf(stdout, "MDI-X\n");
+ else
+ fprintf(stdout, "MDI\n");
+ } else
+ fprintf(stdout, "Unknown\n");
+ }
+ }
+
+ return 0;
+}
+
diff --git a/e1000.c b/e1000.c
new file mode 100644
index 0000000..dbd6eb5
--- /dev/null
+++ b/e1000.c
@@ -0,0 +1,640 @@
+/* Copyright (c) 2002 Intel Corporation */
+#include <stdio.h>
+#include "internal.h"
+
+/* Register Bit Masks */
+/* Device Control */
+#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */
+#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */
+#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */
+#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */
+#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */
+#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */
+#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */
+#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
+#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */
+#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */
+#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */
+#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */
+#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */
+#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */
+#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */
+#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */
+#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */
+#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */
+#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */
+#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */
+#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */
+#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */
+#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */
+#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */
+#define E1000_CTRL_RST 0x04000000 /* Global reset */
+#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */
+#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */
+#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */
+#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
+#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */
+
+/* Device Status */
+#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */
+#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */
+#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */
+#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */
+#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */
+#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */
+#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */
+#define E1000_STATUS_SPEED_MASK 0x000000C0
+#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */
+#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */
+#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */
+#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */
+#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */
+#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */
+#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */
+#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */
+#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */
+
+/* Constants used to intrepret the masked PCI-X bus speed. */
+#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */
+#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */
+#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */
+
+/* Receive Control */
+#define E1000_RCTL_RST 0x00000001 /* Software reset */
+#define E1000_RCTL_EN 0x00000002 /* enable */
+#define E1000_RCTL_SBP 0x00000004 /* store bad packet */
+#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */
+#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */
+#define E1000_RCTL_LPE 0x00000020 /* long packet enable */
+#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */
+#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */
+#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */
+#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */
+#define E1000_RCTL_RDMTS 0x00000300 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */
+#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */
+#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */
+#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */
+#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */
+#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */
+#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */
+#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */
+#define E1000_RCTL_SZ 0x00030000 /* rx buffer size */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
+#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */
+#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */
+#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */
+#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
+#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */
+#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */
+#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */
+#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */
+#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */
+#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */
+#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */
+#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */
+#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */
+
+/* Transmit Control */
+#define E1000_TCTL_RST 0x00000001 /* software reset */
+#define E1000_TCTL_EN 0x00000002 /* enable tx */
+#define E1000_TCTL_BCE 0x00000004 /* busy check enable */
+#define E1000_TCTL_PSP 0x00000008 /* pad short packets */
+#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */
+#define E1000_TCTL_COLD 0x003ff000 /* collision distance */
+#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */
+#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */
+#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
+#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
+
+/* M88E1000 PHY Specific Status Register */
+#define M88_PSSR_JABBER 0x0001 /* 1=Jabber */
+#define M88_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */
+#define M88_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */
+#define M88_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */
+#define M88_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M;
+ * 3=110-140M;4=>140M */
+#define M88_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */
+#define M88_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */
+#define M88_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */
+#define M88_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */
+#define M88_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */
+#define M88_PSSR_10MBS 0x0000 /* 00=10Mbs */
+#define M88_PSSR_100MBS 0x4000 /* 01=100Mbs */
+#define M88_PSSR_1000MBS 0x8000 /* 10=1000Mbs */
+
+#define M88_PSSR_CL_0_50 (0<<7)
+#define M88_PSSR_CL_50_80 (1<<7)
+#define M88_PSSR_CL_80_110 (2<<7)
+#define M88_PSSR_CL_110_140 (3<<7)
+#define M88_PSSR_CL_140_PLUS (4<<7)
+
+/* M88E1000 PHY Specific Control Register */
+#define M88_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */
+#define M88_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */
+#define M88_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */
+#define M88_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low,
+ * 0=CLK125 toggling
+ */
+#define M88_PSCR_MDI_MASK 0x0060
+#define M88_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */
+ /* Manual MDI configuration */
+#define M88_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */
+#define M88_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover,
+ * 100BASE-TX/10BASE-T:
+ * MDI Mode
+ */
+#define M88_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled
+ * all speeds.
+ */
+#define M88_PSCR_10BT_EXT_DIST_ENABLE 0x0080
+ /* 1=Enable Extended 10BASE-T distance
+ * (Lower 10BASE-T RX Threshold)
+ * 0=Normal 10BASE-T RX Threshold */
+#define M88_PSCR_MII_5BIT_ENABLE 0x0100
+ /* 1=5-Bit interface in 100BASE-TX
+ * 0=MII interface in 100BASE-TX */
+#define M88_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */
+#define M88_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */
+#define M88_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */
+
+#define M88_PSCR_POLARITY_REVERSAL_SHIFT 1
+#define M88_PSCR_AUTO_X_MODE_SHIFT 5
+#define M88_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7
+
+/* PCI Device IDs */
+#define E1000_DEV_ID_82542 0x1000
+#define E1000_DEV_ID_82543GC_FIBER 0x1001
+#define E1000_DEV_ID_82543GC_COPPER 0x1004
+#define E1000_DEV_ID_82544EI_COPPER 0x1008
+#define E1000_DEV_ID_82544EI_FIBER 0x1009
+#define E1000_DEV_ID_82544GC_COPPER 0x100C
+#define E1000_DEV_ID_82544GC_LOM 0x100D
+#define E1000_DEV_ID_82540EM 0x100E
+#define E1000_DEV_ID_82540EM_LOM 0x1015
+#define E1000_DEV_ID_82540EP_LOM 0x1016
+#define E1000_DEV_ID_82540EP 0x1017
+#define E1000_DEV_ID_82540EP_LP 0x101E
+#define E1000_DEV_ID_82545EM_COPPER 0x100F
+#define E1000_DEV_ID_82545EM_FIBER 0x1011
+#define E1000_DEV_ID_82545GM_COPPER 0x1026
+#define E1000_DEV_ID_82545GM_FIBER 0x1027
+#define E1000_DEV_ID_82545GM_SERDES 0x1028
+#define E1000_DEV_ID_82546EB_COPPER 0x1010
+#define E1000_DEV_ID_82546EB_FIBER 0x1012
+#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D
+#define E1000_DEV_ID_82546GB_COPPER 0x1079
+#define E1000_DEV_ID_82546GB_FIBER 0x107A
+#define E1000_DEV_ID_82546GB_SERDES 0x107B
+#define E1000_DEV_ID_82546GB_PCIE 0x108A
+#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099
+#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5
+#define E1000_DEV_ID_82541EI 0x1013
+#define E1000_DEV_ID_82541EI_MOBILE 0x1018
+#define E1000_DEV_ID_82541ER_LOM 0x1014
+#define E1000_DEV_ID_82541ER 0x1078
+#define E1000_DEV_ID_82541GI 0x1076
+#define E1000_DEV_ID_82541GI_LF 0x107C
+#define E1000_DEV_ID_82541GI_MOBILE 0x1077
+#define E1000_DEV_ID_82547EI 0x1019
+#define E1000_DEV_ID_82547EI_MOBILE 0x101A
+#define E1000_DEV_ID_82547GI 0x1075
+#define E1000_DEV_ID_82571EB_COPPER 0x105E
+#define E1000_DEV_ID_82571EB_FIBER 0x105F
+#define E1000_DEV_ID_82571EB_SERDES 0x1060
+#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4
+#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5
+#define E1000_DEV_ID_82571EB_QUAD_COPPER_LP 0x10BC
+#define E1000_DEV_ID_82572EI_COPPER 0x107D
+#define E1000_DEV_ID_82572EI_FIBER 0x107E
+#define E1000_DEV_ID_82572EI_SERDES 0x107F
+#define E1000_DEV_ID_82572EI 0x10B9
+#define E1000_DEV_ID_82573E 0x108B
+#define E1000_DEV_ID_82573E_IAMT 0x108C
+#define E1000_DEV_ID_82573L 0x109A
+#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096
+#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098
+#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA
+#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB
+#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049
+#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A
+#define E1000_DEV_ID_ICH8_IGP_C 0x104B
+#define E1000_DEV_ID_ICH8_IFE 0x104C
+#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4
+#define E1000_DEV_ID_ICH8_IFE_G 0x10C5
+#define E1000_DEV_ID_ICH8_IGP_M 0x104D
+
+#define E1000_82542_2_0_REV_ID 2
+#define E1000_82542_2_1_REV_ID 3
+
+/* Enumerated types specific to the e1000 hardware */
+/* Media Access Controlers */
+enum e1000_mac_type {
+ e1000_undefined = 0,
+ e1000_82542,
+ e1000_82543,
+ e1000_82544,
+ e1000_82540,
+ e1000_82545,
+ e1000_82545_rev_3,
+ e1000_82546,
+ e1000_82546_rev_3,
+ e1000_82541,
+ e1000_82541_rev_2,
+ e1000_82547,
+ e1000_82547_rev_2,
+ e1000_82571,
+ e1000_82572,
+ e1000_82573,
+ e1000_80003es2lan,
+ e1000_ich8lan,
+ e1000_num_macs
+};
+
+static enum e1000_mac_type e1000_get_mac_type(u16 device_id)
+{
+ enum e1000_mac_type mac_type = e1000_undefined;
+
+ switch (device_id) {
+ case E1000_DEV_ID_82542:
+ mac_type = e1000_82542;
+ break;
+ case E1000_DEV_ID_82543GC_FIBER:
+ case E1000_DEV_ID_82543GC_COPPER:
+ mac_type = e1000_82543;
+ break;
+ case E1000_DEV_ID_82544EI_COPPER:
+ case E1000_DEV_ID_82544EI_FIBER:
+ case E1000_DEV_ID_82544GC_COPPER:
+ case E1000_DEV_ID_82544GC_LOM:
+ mac_type = e1000_82544;
+ break;
+ case E1000_DEV_ID_82540EM:
+ case E1000_DEV_ID_82540EM_LOM:
+ case E1000_DEV_ID_82540EP:
+ case E1000_DEV_ID_82540EP_LOM:
+ case E1000_DEV_ID_82540EP_LP:
+ mac_type = e1000_82540;
+ break;
+ case E1000_DEV_ID_82545EM_COPPER:
+ case E1000_DEV_ID_82545EM_FIBER:
+ mac_type = e1000_82545;
+ break;
+ case E1000_DEV_ID_82545GM_COPPER:
+ case E1000_DEV_ID_82545GM_FIBER:
+ case E1000_DEV_ID_82545GM_SERDES:
+ mac_type = e1000_82545_rev_3;
+ break;
+ case E1000_DEV_ID_82546EB_COPPER:
+ case E1000_DEV_ID_82546EB_FIBER:
+ case E1000_DEV_ID_82546EB_QUAD_COPPER:
+ mac_type = e1000_82546;
+ break;
+ case E1000_DEV_ID_82546GB_COPPER:
+ case E1000_DEV_ID_82546GB_FIBER:
+ case E1000_DEV_ID_82546GB_SERDES:
+ case E1000_DEV_ID_82546GB_PCIE:
+ case E1000_DEV_ID_82546GB_QUAD_COPPER:
+ case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3:
+ mac_type = e1000_82546_rev_3;
+ break;
+ case E1000_DEV_ID_82541EI:
+ case E1000_DEV_ID_82541EI_MOBILE:
+ case E1000_DEV_ID_82541ER_LOM:
+ mac_type = e1000_82541;
+ break;
+ case E1000_DEV_ID_82541ER:
+ case E1000_DEV_ID_82541GI:
+ case E1000_DEV_ID_82541GI_LF:
+ case E1000_DEV_ID_82541GI_MOBILE:
+ mac_type = e1000_82541_rev_2;
+ break;
+ case E1000_DEV_ID_82547EI:
+ case E1000_DEV_ID_82547EI_MOBILE:
+ mac_type = e1000_82547;
+ break;
+ case E1000_DEV_ID_82547GI:
+ mac_type = e1000_82547_rev_2;
+ break;
+ case E1000_DEV_ID_82571EB_COPPER:
+ case E1000_DEV_ID_82571EB_FIBER:
+ case E1000_DEV_ID_82571EB_SERDES:
+ case E1000_DEV_ID_82571EB_QUAD_COPPER:
+ case E1000_DEV_ID_82571EB_QUAD_FIBER:
+ case E1000_DEV_ID_82571EB_QUAD_COPPER_LP:
+ mac_type = e1000_82571;
+ break;
+ case E1000_DEV_ID_82572EI:
+ case E1000_DEV_ID_82572EI_COPPER:
+ case E1000_DEV_ID_82572EI_FIBER:
+ case E1000_DEV_ID_82572EI_SERDES:
+ mac_type = e1000_82572;
+ break;
+ case E1000_DEV_ID_82573E:
+ case E1000_DEV_ID_82573E_IAMT:
+ case E1000_DEV_ID_82573L:
+ mac_type = e1000_82573;
+ break;
+ case E1000_DEV_ID_80003ES2LAN_COPPER_DPT:
+ case E1000_DEV_ID_80003ES2LAN_SERDES_DPT:
+ case E1000_DEV_ID_80003ES2LAN_COPPER_SPT:
+ case E1000_DEV_ID_80003ES2LAN_SERDES_SPT:
+ mac_type = e1000_80003es2lan;
+ break;
+ case E1000_DEV_ID_ICH8_IFE:
+ case E1000_DEV_ID_ICH8_IFE_GT:
+ case E1000_DEV_ID_ICH8_IFE_G:
+ case E1000_DEV_ID_ICH8_IGP_M:
+ case E1000_DEV_ID_ICH8_IGP_M_AMT:
+ case E1000_DEV_ID_ICH8_IGP_AMT:
+ case E1000_DEV_ID_ICH8_IGP_C:
+ mac_type = e1000_ich8lan;
+ break;
+ default:
+ /* assume old nic and attempt so user can get limited
+ * functionality */
+ mac_type = e1000_82543;
+ break;
+ }
+
+ return mac_type;
+}
+
+int e1000_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *regs_buff = (u32 *)regs->data;
+ u16 hw_device_id = (u16)regs->version;
+ /* u8 hw_revision_id = (u8)(regs->version >> 16); */
+ u8 version = (u8)(regs->version >> 24);
+ enum e1000_mac_type mac_type;
+ u32 reg;
+
+ if (version != 1)
+ return -1;
+
+ mac_type = e1000_get_mac_type(hw_device_id);
+
+ if(mac_type == e1000_undefined)
+ return -1;
+
+ fprintf(stdout, "MAC Registers\n");
+ fprintf(stdout, "-------------\n");
+
+ /* Device control register */
+ reg = regs_buff[0];
+ fprintf(stdout,
+ "0x00000: CTRL (Device control register) 0x%08X\n"
+ " Endian mode (buffers): %s\n"
+ " Link reset: %s\n"
+ " Set link up: %s\n"
+ " Invert Loss-Of-Signal: %s\n"
+ " Receive flow control: %s\n"
+ " Transmit flow control: %s\n"
+ " VLAN mode: %s\n",
+ reg,
+ reg & E1000_CTRL_BEM ? "big" : "little",
+ reg & E1000_CTRL_LRST ? "reset" : "normal",
+ reg & E1000_CTRL_SLU ? "1" : "0",
+ reg & E1000_CTRL_ILOS ? "yes" : "no",
+ reg & E1000_CTRL_RFCE ? "enabled" : "disabled",
+ reg & E1000_CTRL_TFCE ? "enabled" : "disabled",
+ reg & E1000_CTRL_VME ? "enabled" : "disabled");
+ if(mac_type >= e1000_82543) {
+ fprintf(stdout,
+ " Auto speed detect: %s\n"
+ " Speed select: %s\n"
+ " Force speed: %s\n"
+ " Force duplex: %s\n",
+ reg & E1000_CTRL_ASDE ? "enabled" : "disabled",
+ (reg & E1000_CTRL_SPD_SEL) == E1000_CTRL_SPD_10 ? "10Mb/s" :
+ (reg & E1000_CTRL_SPD_SEL) == E1000_CTRL_SPD_100 ? "100Mb/s" :
+ (reg & E1000_CTRL_SPD_SEL) == E1000_CTRL_SPD_1000 ? "1000Mb/s" :
+ "not used",
+ reg & E1000_CTRL_FRCSPD ? "yes" : "no",
+ reg & E1000_CTRL_FRCDPX ? "yes" : "no");
+ }
+
+ /* Device status register */
+ reg = regs_buff[1];
+ fprintf(stdout,
+ "0x00008: STATUS (Device status register) 0x%08X\n"
+ " Duplex: %s\n"
+ " Link up: %s\n",
+ reg,
+ reg & E1000_STATUS_FD ? "full" : "half",
+ reg & E1000_STATUS_LU ? "link config" : "no link config");
+ if (mac_type >= e1000_82571) {
+ fprintf(stdout,
+ " TBI mode: %s\n"
+ " Link speed: %s\n"
+ " Bus type: %s\n"
+ " Port number: %s\n",
+ reg & E1000_STATUS_TBIMODE ? "enabled" : "disabled",
+ (reg & E1000_STATUS_SPEED_MASK) == E1000_STATUS_SPEED_10 ?
+ "10Mb/s" :
+ (reg & E1000_STATUS_SPEED_MASK) == E1000_STATUS_SPEED_100 ?
+ "100Mb/s" :
+ (reg & E1000_STATUS_SPEED_MASK) == E1000_STATUS_SPEED_1000 ?
+ "1000Mb/s" : "not used",
+ "PCI Express",
+ (reg & E1000_STATUS_FUNC_MASK) == 0 ? "0" : "1");
+ }
+ else if (mac_type >= e1000_82543) {
+ fprintf(stdout,
+ " TBI mode: %s\n"
+ " Link speed: %s\n"
+ " Bus type: %s\n"
+ " Bus speed: %s\n"
+ " Bus width: %s\n",
+ reg & E1000_STATUS_TBIMODE ? "enabled" : "disabled",
+ (reg & E1000_STATUS_SPEED_MASK) == E1000_STATUS_SPEED_10 ?
+ "10Mb/s" :
+ (reg & E1000_STATUS_SPEED_MASK) == E1000_STATUS_SPEED_100 ?
+ "100Mb/s" :
+ (reg & E1000_STATUS_SPEED_MASK) == E1000_STATUS_SPEED_1000 ?
+ "1000Mb/s" : "not used",
+ (reg & E1000_STATUS_PCIX_MODE) ? "PCI-X" : "PCI",
+ (reg & E1000_STATUS_PCIX_MODE) ?
+ ((reg & E1000_STATUS_PCIX_SPEED_133) ? "133MHz" :
+ (reg & E1000_STATUS_PCIX_SPEED_100) ? "100MHz" :
+ "66MHz") :
+ ((reg & E1000_STATUS_PCI66) ? "66MHz" : "33MHz"),
+ (reg & E1000_STATUS_BUS64) ? "64-bit" : "32-bit");
+ }
+
+ /* Receive control register */
+ reg = regs_buff[2];
+ fprintf(stdout,
+ "0x00100: RCTL (Receive control register) 0x%08X\n"
+ " Receiver: %s\n"
+ " Store bad packets: %s\n"
+ " Unicast promiscuous: %s\n"
+ " Multicast promiscuous: %s\n"
+ " Long packet: %s\n"
+ " Descriptor minimum threshold size: %s\n"
+ " Broadcast accept mode: %s\n"
+ " VLAN filter: %s\n"
+ " Canonical form indicator: %s\n"
+ " Discard pause frames: %s\n"
+ " Pass MAC control frames: %s\n",
+ reg,
+ reg & E1000_RCTL_EN ? "enabled" : "disabled",
+ reg & E1000_RCTL_SBP ? "enabled" : "disabled",
+ reg & E1000_RCTL_UPE ? "enabled" : "disabled",
+ reg & E1000_RCTL_MPE ? "enabled" : "disabled",
+ reg & E1000_RCTL_LPE ? "enabled" : "disabled",
+ (reg & E1000_RCTL_RDMTS) == E1000_RCTL_RDMTS_HALF ? "1/2" :
+ (reg & E1000_RCTL_RDMTS) == E1000_RCTL_RDMTS_QUAT ? "1/4" :
+ (reg & E1000_RCTL_RDMTS) == E1000_RCTL_RDMTS_EIGTH ? "1/8" :
+ "reserved",
+ reg & E1000_RCTL_BAM ? "accept" : "ignore",
+ reg & E1000_RCTL_VFE ? "enabled" : "disabled",
+ reg & E1000_RCTL_CFIEN ? "enabled" : "disabled",
+ reg & E1000_RCTL_DPF ? "ignored" : "filtered",
+ reg & E1000_RCTL_PMCF ? "pass" : "don't pass");
+ if(mac_type >= e1000_82543) {
+ fprintf(stdout,
+ " Receive buffer size: %s\n",
+ reg & E1000_RCTL_BSEX ?
+ ((reg & E1000_RCTL_SZ)==E1000_RCTL_SZ_16384 ? "16384" :
+ (reg & E1000_RCTL_SZ)==E1000_RCTL_SZ_8192 ? "8192" :
+ (reg & E1000_RCTL_SZ)==E1000_RCTL_SZ_4096 ? "4096" :
+ "reserved") :
+ ((reg & E1000_RCTL_SZ)==E1000_RCTL_SZ_2048 ? "2048" :
+ (reg & E1000_RCTL_SZ)==E1000_RCTL_SZ_1024 ? "1024" :
+ (reg & E1000_RCTL_SZ)==E1000_RCTL_SZ_512 ? "512" :
+ "256"));
+ } else {
+ fprintf(stdout,
+ " Receive buffer size: %s\n",
+ (reg & E1000_RCTL_SZ) == E1000_RCTL_SZ_2048 ? "2048" :
+ (reg & E1000_RCTL_SZ) == E1000_RCTL_SZ_1024 ? "1024" :
+ (reg & E1000_RCTL_SZ) == E1000_RCTL_SZ_512 ? "512" :
+ "256");
+ }
+
+ /* Receive descriptor registers */
+ fprintf(stdout,
+ "0x02808: RDLEN (Receive desc length) 0x%08X\n",
+ regs_buff[3]);
+ fprintf(stdout,
+ "0x02810: RDH (Receive desc head) 0x%08X\n",
+ regs_buff[4]);
+ fprintf(stdout,
+ "0x02818: RDT (Receive desc tail) 0x%08X\n",
+ regs_buff[5]);
+ fprintf(stdout,
+ "0x02820: RDTR (Receive delay timer) 0x%08X\n",
+ regs_buff[6]);
+
+ /* Transmit control register */
+ reg = regs_buff[7];
+ fprintf(stdout,
+ "0x00400: TCTL (Transmit ctrl register) 0x%08X\n"
+ " Transmitter: %s\n"
+ " Pad short packets: %s\n"
+ " Software XOFF Transmission: %s\n",
+ reg,
+ reg & E1000_TCTL_EN ? "enabled" : "disabled",
+ reg & E1000_TCTL_PSP ? "enabled" : "disabled",
+ reg & E1000_TCTL_SWXOFF ? "enabled" : "disabled");
+ if(mac_type >= e1000_82543) {
+ fprintf(stdout,
+ " Re-transmit on late collision: %s\n",
+ reg & E1000_TCTL_RTLC ? "enabled" : "disabled");
+ }
+
+ /* Transmit descriptor registers */
+ fprintf(stdout,
+ "0x03808: TDLEN (Transmit desc length) 0x%08X\n",
+ regs_buff[8]);
+ fprintf(stdout,
+ "0x03810: TDH (Transmit desc head) 0x%08X\n",
+ regs_buff[9]);
+ fprintf(stdout,
+ "0x03818: TDT (Transmit desc tail) 0x%08X\n",
+ regs_buff[10]);
+ fprintf(stdout,
+ "0x03820: TIDV (Transmit delay timer) 0x%08X\n",
+ regs_buff[11]);
+
+ /* PHY type */
+ fprintf(stdout,
+ "PHY type: %s\n",
+ regs_buff[12] == 0 ? "M88" :
+ regs_buff[12] == 1 ? "IGP" :
+ regs_buff[12] == 2 ? "IGP2" : "unknown" );
+
+ if (0 == regs_buff[12]) {
+ reg = regs_buff[13];
+ fprintf(stdout,
+ "M88 PHY STATUS REGISTER: 0x%08X\n"
+ " Jabber: %s\n"
+ " Polarity: %s\n"
+ " Downshifted: %s\n"
+ " MDI/MDIX: %s\n"
+ " Cable Length Estimate: %s meters\n"
+ " Link State: %s\n"
+ " Speed & Duplex Resolved: %s\n"
+ " Page Received: %s\n"
+ " Duplex: %s\n"
+ " Speed: %s mbps\n",
+ reg,
+ reg & M88_PSSR_JABBER ? "yes" : "no",
+ reg & M88_PSSR_REV_POLARITY ? "reverse" : "normal",
+ reg & M88_PSSR_DOWNSHIFT ? "yes" : "no",
+ reg & M88_PSSR_MDIX ? "MDIX" : "MDI",
+ ((reg & M88_PSSR_CABLE_LENGTH)==M88_PSSR_CL_0_50 ? "0-50"
+ : (reg & M88_PSSR_CABLE_LENGTH)==M88_PSSR_CL_50_80 ? "50-80"
+ : (reg & M88_PSSR_CABLE_LENGTH)==M88_PSSR_CL_80_110 ? "80-110"
+ : (reg & M88_PSSR_CABLE_LENGTH)==M88_PSSR_CL_110_140? "110-140"
+ : (reg & M88_PSSR_CABLE_LENGTH)==M88_PSSR_CL_140_PLUS ? "140+"
+ : "unknown"),
+ reg & M88_PSSR_LINK ? "Up" : "Down",
+ reg & M88_PSSR_SPD_DPLX_RESOLVED ? "Yes" : "No",
+ reg & M88_PSSR_PAGE_RCVD ? "Yes" : "No",
+ reg & M88_PSSR_DPLX ? "Full" : "Half",
+ ((reg & M88_PSSR_SPEED)==M88_PSSR_10MBS ? "10"
+ : (reg & M88_PSSR_SPEED)==M88_PSSR_100MBS ? "100"
+ : (reg & M88_PSSR_SPEED)==M88_PSSR_1000MBS ? "1000"
+ : "unknown")
+ );
+
+ reg = regs_buff[17];
+ fprintf(stdout,
+ "M88 PHY CONTROL REGISTER: 0x%08X\n"
+ " Jabber function: %s\n"
+ " Auto-polarity: %s\n"
+ " SQE Test: %s\n"
+ " CLK125: %s\n"
+ " Auto-MDIX: %s\n"
+ " Extended 10Base-T Distance: %s\n"
+ " 100Base-TX Interface: %s\n"
+ " Scrambler: %s\n"
+ " Force Link Good: %s\n"
+ " Assert CRS on Transmit: %s\n",
+ reg,
+ reg & M88_PSCR_JABBER_DISABLE ? "disabled" : "enabled",
+ reg & M88_PSCR_POLARITY_REVERSAL ? "enabled" : "disabled",
+ reg & M88_PSCR_SQE_TEST ? "enabled" : "disabled",
+ reg & M88_PSCR_CLK125_DISABLE ? "disabled" : "enabled",
+ ((reg & M88_PSCR_MDI_MASK)==M88_PSCR_MDI_MANUAL_MODE ? "force MDI"
+ : (reg & M88_PSCR_MDI_MASK)==M88_PSCR_MDIX_MANUAL_MODE ? "force MDIX"
+ : (reg & M88_PSCR_MDI_MASK)==M88_PSCR_AUTO_X_1000T ? "1000 auto, 10/100 MDI"
+ : (reg & M88_PSCR_MDI_MASK)==M88_PSCR_AUTO_X_MODE ? "auto"
+ : "wtf"),
+ reg & M88_PSCR_10BT_EXT_DIST_ENABLE ? "enabled" : "disabled",
+ reg & M88_PSCR_MII_5BIT_ENABLE ? "5-bit" : "MII",
+ reg & M88_PSCR_SCRAMBLER_DISABLE ? "disabled" : "enabled",
+ reg & M88_PSCR_FORCE_LINK_GOOD ? "forced" : "disabled",
+ reg & M88_PSCR_ASSERT_CRS_ON_TX ? "enabled" : "disabled"
+ );
+ }
+
+ return 0;
+}
+
diff --git a/et131x.c b/et131x.c
new file mode 100644
index 0000000..a23f7a2
--- /dev/null
+++ b/et131x.c
@@ -0,0 +1,124 @@
+#include <stdio.h>
+#include <string.h>
+#include "internal.h"
+
+int et131x_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u8 version = (u8)(regs->version >> 24);
+ u32 *reg = (u32 *)regs->data;
+
+ if (version != 1)
+ return -1;
+
+ fprintf(stdout, "PHY Registers\n");
+ fprintf(stdout, "0x0, Basic Control Reg = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1, Basic Status Reg = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x2, PHY identifier 1 = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x3, PHY identifier 2 = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x4, Auto Neg Advertisement = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x5, Auto Neg L Partner Ability = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x6, Auto Neg Expansion = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x7, Reserved = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x8, Reserved = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x9, 1000T Control = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xA, 1000T Status = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xB, Reserved = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xC, Reserved = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xD, MMD Access Control = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xE, MMD access Data = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xF, Extended Status = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x10, Phy Index = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x11, Phy Data = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x12, MPhy Control = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x13, Phy Loopback Control1 = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x14, Phy Loopback Control2 = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x15, Register Management = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x16, Phy Config = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x17, Phy Phy Control = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x18, Phy Interrupt Mask = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x19, Phy Interrupt Status = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1A, Phy Phy Status = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1B, Phy LED1 = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1C, Phy LED2 = 0x%04X\n", *reg++);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "JAGCore Global Registers\n");
+ fprintf(stdout, "0x0, TXQ Start Address = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1, TXQ End Address = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x2, RXQ Start Address = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x3, RXQ End Address = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x4, Power Management Status = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x5, Interrupt Status = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x6, Interrupt Mask = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x7, Int Alias Clear Mask = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x8, Int Status Alias = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x9, Software Reset = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xA, SLV Timer = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xB, MSI Config = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xC, Loopback = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xD, Watchdog Timer = 0x%04X\n", *reg++);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "TXDMA Registers\n");
+ fprintf(stdout, "0x0, Control Status = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1, Packet Ring Base Addr (Hi) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x2, Packet Ring Base Addr (Lo) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x3, Packet Ring Num Descrs = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x4, TX Queue Write Address = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x5, TX Queue Write Address Ext = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x6, TX Queue Read Address = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x7, Status Writeback Addr (Hi) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x8, Status Writeback Addr (Lo) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x9, Service Request = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xA, Service Complete = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xB, Cache Read Index = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xC, Cache Write Index = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xD, TXDMA Error = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xE, Descriptor Abort Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xF, Payload Abort Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x10, Writeback Abort Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x11, Descriptor Timeout Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x12, Payload Timeout Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x13, Writeback Timeout Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x14, Descriptor Error Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x15, Payload Error Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x16, Writeback Error Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x17, Dropped TLP Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x18, New service Complete = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1A, Ethernet Packet Count = 0x%04X\n", *reg++);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "RXDMA Registers\n");
+ fprintf(stdout, "0x0, Control Status = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1, Writeback Addr (Hi) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x2, Writeback Addr (Lo) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x3, Num Packets Done = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x4, Max Packet Time = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x5, RX Queue Read Addr = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x6, RX Queue Read Address Ext = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x7, RX Queue Write Addr = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x8, Packet Ring Base Addr (Hi) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x9, Packet Ring Base Addr (Lo) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xA, Packet Ring Num Descrs = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xE, Packet Ring Avail Offset = 0x%04X\n", *reg++);
+ fprintf(stdout, "0xF, Packet Ring Full Offset = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x10, Packet Ring Access Index = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x11, Packet Ring Min Descrip = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x12, FBR0 Address (Lo) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x13, FBR0 Address (Hi) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x14, FBR0 Num Descriptors = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x15, FBR0 Available Offset = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x16, FBR0 Full Offset = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x17, FBR0 Read Index = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x18, FBR0 Minimum Descriptors = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x19, FBR1 Address (Lo) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1A, FBR1 Address (Hi) = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1B, FBR1 Num Descriptors = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1C, FBR1 Available Offset = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1D, FBR1 Full Offset = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1E, FBR1 Read Index = 0x%04X\n", *reg++);
+ fprintf(stdout, "0x1F, FBR1 Minimum Descriptors = 0x%04X\n", *reg++);
+ fprintf(stdout, "\n");
+ return 0;
+}
diff --git a/ethtool.8.in b/ethtool.8.in
new file mode 100644
index 0000000..7a3080f
--- /dev/null
+++ b/ethtool.8.in
@@ -0,0 +1,1780 @@
+.\" -*- nroff -*-
+.\" Copyright 1999 by David S. Miller. All Rights Reserved.
+.\" Portions Copyright 2001 Sun Microsystems
+.\" Portions Copyright 2007, 2009 Free Software Foundation, Inc.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.\" There must be no text lines before .TH. Use '.' for vertical spacing.
+.\"
+.\" .An - list of n alternative values as in "flav vanilla|strawberry"
+.\"
+.de A1
+\\fB\\$1\\fP|\\fB\\$2\\fP
+..
+.de A2
+\\fB\\$1\\fP\ \\fB\\$2\\fP|\\fB\\$3\\fP
+..
+.de A3
+\\fB\\$1\\fP\ \\fB\\$2\\fP|\\fB\\$3\\fP|\\fB\\$4\\fP
+..
+.de A4
+\\fB\\$1\\fP\ \\fB\\$2\\fP|\\fB\\$3\\fP|\\fB\\$4\\fP|\\fB\\$5\\fP
+..
+.\"
+.\" .Bn - same as above but framed by square brackets
+.\"
+.de B1
+[\\fB\\$1\\fP|\\fB\\$2\\fP]
+..
+.de B2
+[\\fB\\$1\\fP\ \\fB\\$2\\fP|\\fB\\$3\\fP]
+..
+.de B3
+[\\fB\\$1\\fP\ \\fB\\$2\\fP|\\fB\\$3\\fP|\\fB\\$4\\fP]
+..
+.de B4
+[\\fB\\$1\\fP\ \\fB\\$2\\fP|\\fB\\$3\\fP|\\fB\\$4\\fP|\\fB\\$5\\fP]
+..
+.\"
+.\" .BN - value with a numeric input as in "[value N]"
+.\"
+.de BN
+[\\fB\\$1\\fP\ \\fIN\\fP]
+..
+.\"
+.\" .BM - same as above but has a mask field for format "[value N [m N]]"
+.\"
+.de BM
+[\\fB\\$1\\fP\ \\fIN\\fP\ [\\fBm\\fP\ \\fIN\\fP]]
+..
+.\"
+.\" \(*MA - mac address
+.\"
+.ds MA \fIxx\fP\fB:\fP\fIyy\fP\fB:\fP\fIzz\fP\fB:\fP\fIaa\fP\fB:\fP\fIbb\fP\fB:\fP\fIcc\fP
+.\"
+.\" \(*MS - master-slave property
+.\"
+.ds MS \fBpreferred-master\fP|\fBpreferred-slave\fP|\fBforced-master\fP|\fBforced-slave\fP
+.\"
+.\" \(*PA - IP address
+.\"
+.ds PA \fIip-address\fP
+.\"
+.\" \(*WO - wol flags
+.\"
+.ds WO \fBp\fP|\fBu\fP|\fBm\fP|\fBb\fP|\fBa\fP|\fBg\fP|\fBs\fP|\fBf|\fBd\fP...
+.\"
+.\" \(*FL - flow type values
+.\"
+.ds FL \fBtcp4\fP|\fBudp4\fP|\fBah4\fP|\fBesp4\fP|\fBsctp4\fP|\fBtcp6\fP|\fBudp6\fP|\fBah6\fP|\fBesp6\fP|\fBsctp6\fP
+.\"
+.\" \(*HO - hash options
+.\"
+.ds HO \fBm\fP|\fBv\fP|\fBt\fP|\fBs\fP|\fBd\fP|\fBf\fP|\fBn\fP|\fBr\fP...
+.\"
+.\" \(*SD - Self-diag test values
+.\"
+.ds SD \fBoffline\fP|\fBonline\fP|\fBexternal_lb\fP
+.\"
+.\" \(*NC - Network Classifier type values
+.\"
+.ds NC \fBether\fP|\fBip4\fP|\fBtcp4\fP|\fBudp4\fP|\fBsctp4\fP|\fBah4\fP|\fBesp4\fP|\fBip6\fP|\fBtcp6\fP|\fBudp6\fP|\fBah6\fP|\fBesp6\fP|\fBsctp6\fP
+.
+.\"
+.\" Start URL.
+.de UR
+. ds m1 \\$1\"
+. nh
+. if \\n(mH \{\
+. \" Start diversion in a new environment.
+. do ev URL-div
+. do di URL-div
+. \}
+..
+.\" End URL.
+.de UE
+. ie \\n(mH \{\
+. br
+. di
+. ev
+.
+. \" Has there been one or more input lines for the link text?
+. ie \\n(dn \{\
+. do HTML-NS "<a href=""\\*(m1"">"
+. \" Yes, strip off final newline of diversion and emit it.
+. do chop URL-div
+. do URL-div
+\c
+. do HTML-NS </a>
+. \}
+. el \
+. do HTML-NS "<a href=""\\*(m1"">\\*(m1</a>"
+\&\\$*\"
+. \}
+. el \
+\\*(la\\*(m1\\*(ra\\$*\"
+.
+. hy \\n(HY
+..
+.
+.TH ETHTOOL 8 "January 2024" "Ethtool version @VERSION@"
+.SH NAME
+ethtool \- query or control network driver and hardware settings
+.
+.SH SYNOPSIS
+.\" Do not adjust lines (i.e. left justification) and do not hyphenate.
+.na
+.nh
+.HP
+.B ethtool
+.I devname
+.HP
+.B ethtool \-h|\-\-help
+.HP
+.B ethtool \-\-version
+.HP
+.B ethtool
+.BN --debug
+.I args
+.HP
+.B ethtool [--json]
+.I args
+.HP
+.B ethtool [-I | --include-statistics]
+.I args
+.HP
+.B ethtool \-\-monitor
+[
+.I command
+] [
+.I devname
+]
+.HP
+.B ethtool \-a|\-\-show\-pause
+.I devname
+.HP
+.B ethtool \-A|\-\-pause
+.I devname
+.B2 autoneg on off
+.B2 rx on off
+.B2 tx on off
+.HP
+.B ethtool \-c|\-\-show\-coalesce
+.I devname
+.HP
+.B ethtool \-C|\-\-coalesce
+.I devname
+.B2 adaptive\-rx on off
+.B2 adaptive\-tx on off
+.BN rx\-usecs
+.BN rx\-frames
+.BN rx\-usecs\-irq
+.BN rx\-frames\-irq
+.BN tx\-usecs
+.BN tx\-frames
+.BN tx\-usecs\-irq
+.BN tx\-frames\-irq
+.BN stats\-block\-usecs
+.BN pkt\-rate\-low
+.BN rx\-usecs\-low
+.BN rx\-frames\-low
+.BN tx\-usecs\-low
+.BN tx\-frames\-low
+.BN pkt\-rate\-high
+.BN rx\-usecs\-high
+.BN rx\-frames\-high
+.BN tx\-usecs\-high
+.BN tx\-frames\-high
+.BN sample\-interval
+.B2 cqe\-mode\-rx on off
+.B2 cqe\-mode\-tx on off
+.BN tx\-aggr\-max\-bytes
+.BN tx\-aggr\-max\-frames
+.BN tx\-aggr\-time\-usecs
+.HP
+.B ethtool \-g|\-\-show\-ring
+.I devname
+.HP
+.B ethtool \-G|\-\-set\-ring
+.I devname
+.BN rx
+.BN rx\-mini
+.BN rx\-jumbo
+.BN tx
+.BN rx\-buf\-len
+.B3 tcp\-data\-split auto on off
+.BN cqe\-size
+.BN tx\-push
+.BN rx\-push
+.BN tx\-push\-buf\-len
+.HP
+.B ethtool \-i|\-\-driver
+.I devname
+.HP
+.B ethtool \-d|\-\-register\-dump
+.I devname
+.B2 raw on off
+.B2 hex on off
+.RB [ file
+.IR name ]
+.HP
+.B ethtool \-e|\-\-eeprom\-dump
+.I devname
+.B2 raw on off
+.BN offset
+.BN length
+.HP
+.B ethtool \-E|\-\-change\-eeprom
+.I devname
+.BN magic
+.BN offset
+.BN length
+.BN value
+.HP
+.B ethtool \-k|\-\-show\-features|\-\-show\-offload
+.I devname
+.HP
+.B ethtool \-K|\-\-features|\-\-offload
+.I devname feature
+.A1 on off
+.RB ...
+.HP
+.B ethtool \-p|\-\-identify
+.I devname
+.RI [ N ]
+.HP
+.B ethtool \-P|\-\-show\-permaddr
+.I devname
+.HP
+.B ethtool \-r|\-\-negotiate
+.I devname
+.HP
+.B ethtool \-S|\-\-statistics
+.I devname
+.RB [\fB\-\-all\-groups\fP|\fB\-\-groups
+.RB [\fBeth\-phy\fP]
+.RB [\fBeth\-mac\fP]
+.RB [\fBeth\-ctrl\fP]
+.RB [\fBrmon\fP]
+.RB ]
+.HP
+.B ethtool \-\-phy\-statistics
+.I devname
+.HP
+.B ethtool \-t|\-\-test
+.I devname
+.RI [\*(SD]
+.HP
+.B ethtool \-s
+.I devname
+.BN speed
+.BN lanes
+.B2 duplex half full
+.B4 port tp aui bnc mii fibre da
+.B3 mdix auto on off
+.B2 autoneg on off
+.RB [ advertise \ \fIN\fP[\fB/\fP\fIM\fP]
+|
+.BI advertise \ mode
+.A1 on off
+.RB ...]
+.BN phyad
+.B2 xcvr internal external
+.RB [ wol \ \fIN\fP[\fB/\fP\fIM\fP]
+.RB | \ wol \ \*(WO]
+.RB [ sopass \ \*(MA]
+.RB [ master-slave \ \*(MS]
+.RB [ msglvl
+.IR N\fP[/\fIM\fP] \ |
+.BI msglvl \ type
+.A1 on off
+.RB ...]
+.HP
+.B ethtool \-n|\-u|\-\-show\-nfc|\-\-show\-ntuple
+.I devname
+.RB [\ rx\-flow\-hash \ \*(FL \ |
+.br
+.BI rule \ N
+.RB ]
+.HP
+.B ethtool \-N|\-U|\-\-config\-nfc|\-\-config\-ntuple
+.I devname
+.BR rx\-flow\-hash \ \*(FL \ \: \*(HO \ |
+.br
+.B flow\-type \*(NC
+.RB [ src \ \*(MA\ [ m \ \*(MA]]
+.RB [ dst \ \*(MA\ [ m \ \*(MA]]
+.BM proto
+.RB [ src\-ip \ \*(PA\ [ m \ \*(PA]]
+.RB [ dst\-ip \ \*(PA\ [ m \ \*(PA]]
+.BM tos
+.BM tclass
+.BM l4proto
+.BM src\-port
+.BM dst\-port
+.BM spi
+.BM l4data
+.BM vlan\-etype
+.BM vlan
+.BM user\-def
+.RB [ dst-mac \ \*(MA\ [ m \ \*(MA]]
+.BN action
+.BN context
+.BN loc
+.RB |
+.br
+.BI delete \ N
+.HP
+.B ethtool \-w|\-\-get\-dump
+.I devname
+.RB [ data
+.IR filename ]
+.HP
+.B ethtool\ \-W|\-\-set\-dump
+.I devname N
+.HP
+.B ethtool \-T|\-\-show\-time\-stamping
+.I devname
+.HP
+.B ethtool \-x|\-\-show\-rxfh\-indir|\-\-show\-rxfh
+.I devname
+.HP
+.B ethtool \-X|\-\-set\-rxfh\-indir|\-\-rxfh
+.I devname
+.RB [ hkey \ \*(MA:\...]
+.RB [ start
+.IR N ]
+.RB [\ equal
+.IR N \ |
+.BI weight\ W0
+.IR W1
+.RB ...\ | \ default \ ]
+.RB [ hfunc
+.IR FUNC ]
+.RB [ context
+.I CTX
+.RB |\ new ]
+.RB [ delete ]
+.HP
+.B ethtool \-f|\-\-flash
+.I devname file
+.RI [ N ]
+.HP
+.B ethtool \-l|\-\-show\-channels
+.I devname
+.HP
+.B ethtool \-L|\-\-set\-channels
+.I devname
+.BN rx
+.BN tx
+.BN other
+.BN combined
+.HP
+.B ethtool \-m|\-\-dump\-module\-eeprom|\-\-module\-info
+.I devname
+.B2 raw on off
+.B2 hex on off
+.BN offset
+.BN length
+.BN page
+.BN bank
+.BN i2c
+.HP
+.B ethtool \-\-show\-priv\-flags
+.I devname
+.HP
+.B ethtool \-\-set\-priv\-flags
+.I devname flag
+.A1 on off
+.RB ...
+.HP
+.B ethtool \-\-show\-eee
+.I devname
+.HP
+.B ethtool \-\-set\-eee
+.I devname
+.B2 eee on off
+.B2 tx-lpi on off
+.BN tx-timer
+.BN advertise
+.HP
+.B ethtool \-\-set\-phy\-tunable
+.I devname
+.RB [
+.B downshift
+.A1 on off
+.BN count
+.RB ]
+.RB [
+.B fast\-link\-down
+.A1 on off
+.BN msecs
+.RB ]
+.RB [
+.B energy\-detect\-power\-down
+.A1 on off
+.BN msecs
+.RB ]
+.HP
+.B ethtool \-\-get\-phy\-tunable
+.I devname
+.RB [ downshift ]
+.RB [ fast-link-down ]
+.RB [ energy-detect-power-down ]
+.HP
+.B ethtool \-\-get\-tunable
+.I devname
+.RB [ rx-copybreak ]
+.RB [ tx-copybreak ]
+.RB [ tx-buf-size ]
+.RB [ pfc-prevention-tout ]
+.HP
+.B ethtool \-\-set\-tunable
+.I devname
+.BN rx\-copybreak
+.BN tx\-copybreak
+.BN tx\-buf\-size
+.BN pfc\-prevention\-tout
+.HP
+.B ethtool \-\-reset
+.I devname
+.BN flags
+.RB [ mgmt ]
+.RB [ mgmt-shared ]
+.RB [ irq ]
+.RB [ irq-shared ]
+.RB [ dma ]
+.RB [ dma-shared ]
+.RB [ filter ]
+.RB [ filter-shared ]
+.RB [ offload ]
+.RB [ offload-shared ]
+.RB [ mac ]
+.RB [ mac-shared ]
+.RB [ phy ]
+.RB [ phy-shared ]
+.RB [ ram ]
+.RB [ ram-shared ]
+.RB [ ap ]
+.RB [ ap-shared ]
+.RB [ dedicated ]
+.RB [ all ]
+.HP
+.B ethtool \-\-show\-fec
+.I devname
+.HP
+.B ethtool \-\-set\-fec
+.I devname
+.B encoding
+.BR auto | off | rs | baser | llrs \ [...]
+.HP
+.B ethtool \-Q|\-\-per\-queue
+.I devname
+.RB [ queue_mask
+.IR %x ]
+.I sub_command
+.RB ...
+.HP
+.B ethtool \-\-cable\-test
+.I devname
+.HP
+.B ethtool \-\-cable\-test\-tdr
+.I devname
+.BN first N
+.BN last N
+.BN step N
+.BN pair N
+.HP
+.B ethtool \-\-show\-tunnels
+.I devname
+.HP
+.B ethtool \-\-show\-module
+.I devname
+.HP
+.B ethtool \-\-set\-module
+.I devname
+.RB [ power\-mode\-policy
+.BR high | auto ]
+.HP
+.B ethtool \-\-get\-plca\-cfg
+.I devname
+.HP
+.B ethtool \-\-set\-plca\-cfg
+.I devname
+.RB [ enable
+.BR on | off ]
+.BN node\-id N
+.BN node\-cnt N
+.BN to\-tmr N
+.BN burst\-cnt N
+.BN burst\-tmr N
+.HP
+.B ethtool \-\-get\-plca\-status
+.I devname
+.HP
+.B ethtool \-\-show\-mm
+.I devname
+.HP
+.B ethtool \-\-set\-mm
+.I devname
+.RB [ verify\-enabled
+.BR on | off ]
+.RB [ verify\-time
+.BR N ]
+.RB [ tx\-enabled
+.BR on | off ]
+.RB [ pmac\-enabled
+.BR on | off ]
+.RB [ tx\-min\-frag\-size
+.BR N ]
+.HP
+.B ethtool \-\-show\-pse
+.I devname
+.HP
+.B ethtool \-\-set\-pse
+.I devname
+.RB [ podl\-pse\-admin\-control
+.BR enable | disable ]
+.
+.\" Adjust lines (i.e. full justification) and hyphenate.
+.ad
+.hy
+
+.SH DESCRIPTION
+.BI ethtool
+is used to query and control network device driver and hardware
+settings, particularly for wired Ethernet devices.
+
+.I devname
+is the name of the network device on which ethtool should operate.
+
+.SH OPTIONS
+.B ethtool
+with a single argument specifying the device name prints current
+settings of the specified device.
+.TP
+.B \-h \-\-help
+Shows a short help message.
+.TP
+.B \-\-version
+Shows the ethtool version number.
+.TP
+.BI \-\-debug \ N
+Turns on debugging messages. Argument is interpreted as a mask:
+.TS
+nokeep;
+lB l.
+0x01 Parser information
+.TE
+.TP
+.BI \-\-json
+Output results in JavaScript Object Notation (JSON). Only a subset of
+options support this. Those which do not will continue to output
+plain text in the presence of this option.
+.TP
+.B \-I \-\-include\-statistics
+Include command-related statistics in the output. This option allows
+displaying relevant device statistics for selected get commands.
+.TP
+.B \-a \-\-show\-pause
+Queries the specified Ethernet device for pause parameter information.
+.RS 4
+.TP
+.A3 \fB\-\-src \fBaggregate\fP \fBemac\fP \fBpmac\fP
+If the MAC Merge layer is supported, request a particular source of device
+statistics (eMAC or pMAC, or their aggregate). Only valid if ethtool was
+invoked with the
+.B \-I \-\-include\-statistics
+argument.
+.RE
+.TP
+.B \-A \-\-pause
+Changes the pause parameters of the specified Ethernet device.
+.RS 4
+.TP
+.A2 autoneg on off
+Specifies whether pause autonegotiation should be enabled.
+.TP
+.A2 rx on off
+Specifies whether RX pause should be enabled.
+.TP
+.A2 tx on off
+Specifies whether TX pause should be enabled.
+.RE
+.TP
+.B \-c \-\-show\-coalesce
+Queries the specified network device for coalescing information.
+.TP
+.B \-C \-\-coalesce
+Changes the coalescing settings of the specified network device.
+.TP
+.B \-g \-\-show\-ring
+Queries the specified network device for rx/tx ring parameter information.
+.TP
+.B \-G \-\-set\-ring
+Changes the rx/tx ring parameters of the specified network device.
+.RS 4
+.TP
+.BI rx \ N
+Changes the number of ring entries for the Rx ring.
+.TP
+.BI rx\-mini \ N
+Changes the number of ring entries for the Rx Mini ring.
+.TP
+.BI rx\-jumbo \ N
+Changes the number of ring entries for the Rx Jumbo ring.
+.TP
+.BI tx \ N
+Changes the number of ring entries for the Tx ring.
+.TP
+.BI rx\-buf\-len \ N
+Changes the size of a buffer in the Rx ring.
+.TP
+.BI tcp\-data\-split \ auto|on|off
+Specifies the state of TCP data split.
+.TP
+.BI cqe\-size \ N
+Changes the size of completion queue event.
+.TP
+.BI tx\-push \ on|off
+Specifies whether TX push should be enabled.
+.TP
+.BI rx\-push \ on|off
+Specifies whether RX push should be enabled.
+.TP
+.BI tx\-push\-buf\-len \ N
+Specifies the maximum number of bytes of a transmitted packet a driver can push
+directly to the underlying device
+.RE
+.TP
+.B \-i \-\-driver
+Queries the specified network device for associated driver information.
+.TP
+.B \-d \-\-register\-dump
+Retrieves and prints a register dump for the specified network device.
+The register format for some devices is known and decoded others
+are printed in hex.
+When
+.I raw
+is enabled, then ethtool dumps the raw register data to stdout.
+If
+.I file
+is specified, then use contents of previous raw register dump, rather
+than reading from the device.
+.TP
+.B \-e \-\-eeprom\-dump
+Retrieves and prints an EEPROM dump for the specified network device.
+When raw is enabled, then it dumps the raw EEPROM data to stdout. The
+length and offset parameters allow dumping certain portions of the EEPROM.
+Default is to dump the entire EEPROM.
+.RS 4
+.TP
+.BI raw \ on|off
+.TP
+.BI offset \ N
+.TP
+.BI length \ N
+.RE
+.TP
+.B \-E \-\-change\-eeprom
+If value is specified, changes EEPROM byte for the specified network device.
+offset and value specify which byte and it's new value. If value is not
+specified, stdin is read and written to the EEPROM. The length and offset
+parameters allow writing to certain portions of the EEPROM.
+Because of the persistent nature of writing to the EEPROM, a device-specific
+magic key must be specified to prevent the accidental writing to the EEPROM.
+.TP
+.B \-k \-\-show\-features \-\-show\-offload
+Queries the specified network device for the state of protocol
+offload and other features.
+.TP
+.B \-K \-\-features \-\-offload
+Changes the offload parameters and other features of the specified
+network device. The following feature names are built-in and others
+may be defined by the kernel.
+.RS 4
+.TP
+.A2 rx on off
+Specifies whether RX checksumming should be enabled.
+.TP
+.A2 tx on off
+Specifies whether TX checksumming should be enabled.
+.TP
+.A2 sg on off
+Specifies whether scatter-gather should be enabled.
+.TP
+.A2 tso on off
+Specifies whether TCP segmentation offload should be enabled.
+.TP
+.A2 ufo on off
+Specifies whether UDP fragmentation offload should be enabled
+.TP
+.A2 gso on off
+Specifies whether generic segmentation offload should be enabled
+.TP
+.A2 gro on off
+Specifies whether generic receive offload should be enabled
+.TP
+.A2 lro on off
+Specifies whether large receive offload should be enabled
+.TP
+.A2 rxvlan on off
+Specifies whether RX VLAN acceleration should be enabled
+.TP
+.A2 txvlan on off
+Specifies whether TX VLAN acceleration should be enabled
+.TP
+.A2 ntuple on off
+Specifies whether Rx ntuple filters and actions should be enabled
+.TP
+.A2 rxhash on off
+Specifies whether receive hashing offload should be enabled
+.RE
+.TP
+.B \-p \-\-identify
+Initiates adapter-specific action intended to enable an operator to
+easily identify the adapter by sight. Typically this involves
+blinking one or more LEDs on the specific network port.
+.RS 4
+.TP
+.BN
+Length of time to perform phys-id, in seconds.
+.RE
+.TP
+.B \-P \-\-show\-permaddr
+Queries the specified network device for permanent hardware address.
+.TP
+.B \-r \-\-negotiate
+Restarts auto-negotiation on the specified Ethernet device, if
+auto-negotiation is enabled.
+.TP
+.B \-S \-\-statistics
+Queries the specified network device for standard (IEEE, IETF, etc.), or NIC-
+and driver-specific statistics. NIC- and driver-specific statistics are
+requested when no group of statistics is specified.
+
+NIC- and driver-specific statistics and standard statistics are independent,
+devices may implement either, both or none. There is little commonality between
+naming of NIC- and driver-specific statistics across vendors.
+.RS 4
+.TP
+.B \fB\-\-all\-groups
+.TP
+.B \fB\-\-groups [\fBeth\-phy\fP] [\fBeth\-mac\fP] [\fBeth\-ctrl\fP] [\fBrmon\fP]
+Request groups of standard device statistics.
+.TP
+.A3 \fB\-\-src \fBaggregate\fP \fBemac\fP \fBpmac\fP
+If the MAC Merge layer is supported, request a particular source of device
+statistics (eMAC or pMAC, or their aggregate).
+.RE
+.TP
+.B \-\-phy\-statistics
+Queries the specified network device for PHY specific statistics.
+.TP
+.B \-t \-\-test
+Executes adapter selftest on the specified network device. Possible test modes are:
+.RS 4
+.TP
+.B offline
+Perform full set of tests, possibly interrupting normal operation
+during the tests,
+.TP
+.B online
+Perform limited set of tests, not interrupting normal operation,
+.TP
+.B external_lb
+Perform full set of tests, as for \fBoffline\fR, and additionally an
+external-loopback test.
+.RE
+.TP
+.B \-s \-\-change
+Allows changing some or all settings of the specified network device.
+All following options only apply if
+.B \-s
+was specified.
+.RS 4
+.TP
+.BI speed \ N
+Set speed in Mb/s.
+.B ethtool
+with just the device name as an argument will show you the supported device speeds.
+.TP
+.BI lanes \ N
+Set number of lanes.
+.TP
+.A2 duplex half full
+Sets full or half duplex mode.
+.TP
+.A4 port tp aui bnc mii fibre da
+Selects device port.
+.TP
+.BR master-slave \ \*(MS
+Configure MASTER/SLAVE role of the PHY. When the PHY is configured as MASTER,
+the PMA Transmit function shall source TX_TCLK from a local clock source. When
+configured as SLAVE, the PMA Transmit function shall source TX_TCLK from the
+clock recovered from data stream provided by MASTER. Not all devices support this.
+.TS
+nokeep;
+lB l.
+preferred-master Prefer MASTER role on autonegotiation
+preferred-slave Prefer SLAVE role on autonegotiation
+forced-master Force the PHY in MASTER role. Can be used without autonegotiation
+forced-slave Force the PHY in SLAVE role. Can be used without autonegotiation
+.TE
+.TP
+.A3 mdix auto on off
+Selects MDI-X mode for port. May be used to override the automatic
+detection feature of most adapters. An argument of \fBauto\fR means
+automatic detection of MDI status, \fBon\fR forces MDI-X (crossover)
+mode, while \fBoff\fR means MDI (straight through) mode. The driver
+should guarantee that this command takes effect immediately, and if
+necessary may reset the link to cause the change to take effect.
+.TP
+.A2 autoneg on off
+Specifies whether autonegotiation should be enabled. Autonegotiation
+is enabled by default, but in some network devices may have trouble
+with it, so you can disable it if really necessary.
+.TP
+.BI advertise \ N
+Sets the speed and duplex advertised by autonegotiation. The argument is
+a hexadecimal value using one or a combination of the following values:
+.TS
+nokeep;
+lB l lB.
+0x001 10baseT Half
+0x002 10baseT Full
+0x100000000000000000000000 10baseT1L Full
+0x8000000000000000000000000 10baseT1S Full
+0x10000000000000000000000000 10baseT1S Half
+0x20000000000000000000000000 10baseT1S_P2MP Half
+0x004 100baseT Half
+0x008 100baseT Full
+0x80000000000000000 100baseT1 Full
+0x40000000000000000000000 100baseFX Half
+0x80000000000000000000000 100baseFX Full
+0x010 1000baseT Half (not supported by IEEE standards)
+0x020 1000baseT Full
+0x20000 1000baseKX Full
+0x20000000000 1000baseX Full
+0x100000000000000000 1000baseT1 Full
+0x8000 2500baseX Full (not supported by IEEE standards)
+0x800000000000 2500baseT Full
+0x1000000000000 5000baseT Full
+0x1000 10000baseT Full
+0x40000 10000baseKX4 Full
+0x80000 10000baseKR Full
+0x100000 10000baseR_FEC
+0x40000000000 10000baseCR Full
+0x80000000000 10000baseSR Full
+0x100000000000 10000baseLR Full
+0x200000000000 10000baseLRM Full
+0x400000000000 10000baseER Full
+0x200000 20000baseMLD2 Full (not supported by IEEE standards)
+0x400000 20000baseKR2 Full (not supported by IEEE standards)
+0x80000000 25000baseCR Full
+0x100000000 25000baseKR Full
+0x200000000 25000baseSR Full
+0x800000 40000baseKR4 Full
+0x1000000 40000baseCR4 Full
+0x2000000 40000baseSR4 Full
+0x4000000 40000baseLR4 Full
+0x400000000 50000baseCR2 Full
+0x800000000 50000baseKR2 Full
+0x10000000000 50000baseSR2 Full
+0x10000000000000 50000baseKR Full
+0x20000000000000 50000baseSR Full
+0x40000000000000 50000baseCR Full
+0x80000000000000 50000baseLR_ER_FR Full
+0x100000000000000 50000baseDR Full
+0x8000000 56000baseKR4 Full
+0x10000000 56000baseCR4 Full
+0x20000000 56000baseSR4 Full
+0x40000000 56000baseLR4 Full
+0x1000000000 100000baseKR4 Full
+0x2000000000 100000baseSR4 Full
+0x4000000000 100000baseCR4 Full
+0x8000000000 100000baseLR4_ER4 Full
+0x200000000000000 100000baseKR2 Full
+0x400000000000000 100000baseSR2 Full
+0x800000000000000 100000baseCR2 Full
+0x1000000000000000 100000baseLR2_ER2_FR2 Full
+0x2000000000000000 100000baseDR2 Full
+0x8000000000000000000 100000baseKR Full
+0x10000000000000000000 100000baseSR Full
+0x20000000000000000000 100000baseLR_ER_FR Full
+0x40000000000000000000 100000baseCR Full
+0x80000000000000000000 100000baseDR Full
+0x4000000000000000 200000baseKR4 Full
+0x8000000000000000 200000baseSR4 Full
+0x10000000000000000 200000baseLR4_ER4_FR4 Full
+0x20000000000000000 200000baseDR4 Full
+0x40000000000000000 200000baseCR4 Full
+0x100000000000000000000 200000baseKR2 Full
+0x200000000000000000000 200000baseSR2 Full
+0x400000000000000000000 200000baseLR2_ER2_FR2 Full
+0x800000000000000000000 200000baseDR2 Full
+0x1000000000000000000000 200000baseCR2 Full
+0x200000000000000000 400000baseKR8 Full
+0x400000000000000000 400000baseSR8 Full
+0x800000000000000000 400000baseLR8_ER8_FR8 Full
+0x1000000000000000000 400000baseDR8 Full
+0x2000000000000000000 400000baseCR8 Full
+0x2000000000000000000000 400000baseKR4 Full
+0x4000000000000000000000 400000baseSR4 Full
+0x8000000000000000000000 400000baseLR4_ER4_FR4 Full
+0x10000000000000000000000 400000baseDR4 Full
+0x20000000000000000000000 400000baseCR4 Full
+0x200000000000000000000000 800000baseCR8 Full
+0x400000000000000000000000 800000baseKR8 Full
+0x800000000000000000000000 800000baseDR8 Full
+0x1000000000000000000000000 800000baseDR8_2 Full
+0x2000000000000000000000000 800000baseSR8 Full
+0x4000000000000000000000000 800000baseVR8 Full
+.TE
+.TP
+.BI phyad \ N
+PHY address.
+.TP
+.A2 xcvr internal external
+Selects transceiver type. Currently only internal and external can be
+specified, in the future further types might be added.
+.TP
+.BR wol \ \*(WO
+Sets Wake-on-LAN options. Not all devices support this. The argument to
+this option is a string of characters specifying which options to enable.
+.TS
+nokeep;
+lB l.
+p Wake on PHY activity
+u Wake on unicast messages
+m Wake on multicast messages
+b Wake on broadcast messages
+a Wake on ARP
+g Wake on MagicPacket\[tm]
+s Enable SecureOn\[tm] password for MagicPacket\[tm]
+f Wake on filter(s)
+d T{
+Disable (wake on nothing). This option clears all previous options.
+T}
+.TE
+.TP
+.B sopass \*(MA
+Sets the SecureOn\[tm] password. The argument to this option must be 6
+bytes in Ethernet MAC hex format (\*(MA).
+.PP
+.BI msglvl \ N
+.br
+.BI msglvl \ type
+.A1 on off
+.RB ...
+.RS
+Sets the driver message type flags by name or number. \fItype\fR
+names the type of message to enable or disable; \fIN\fR specifies the
+new flags numerically. The defined type names and numbers are:
+.TS
+nokeep;
+lB l l.
+drv 0x0001 General driver status
+probe 0x0002 Hardware probing
+link 0x0004 Link state
+timer 0x0008 Periodic status check
+ifdown 0x0010 Interface being brought down
+ifup 0x0020 Interface being brought up
+rx_err 0x0040 Receive error
+tx_err 0x0080 Transmit error
+tx_queued 0x0100 Transmit queueing
+intr 0x0200 Interrupt handling
+tx_done 0x0400 Transmit completion
+rx_status 0x0800 Receive completion
+pktdata 0x1000 Packet contents
+hw 0x2000 Hardware status
+wol 0x4000 Wake-on-LAN status
+.TE
+.PP
+The precise meanings of these type flags differ between drivers.
+.RE
+.PD
+.RE
+.TP
+.B \-n \-u \-\-show\-nfc \-\-show\-ntuple
+Retrieves receive network flow classification options or rules.
+.RS 4
+.TP
+.BR rx\-flow\-hash \ \*(FL
+Retrieves the hash options for the specified flow type.
+.TS
+nokeep;
+lB l.
+tcp4 TCP over IPv4
+udp4 UDP over IPv4
+ah4 IPSEC AH over IPv4
+esp4 IPSEC ESP over IPv4
+sctp4 SCTP over IPv4
+tcp6 TCP over IPv6
+udp6 UDP over IPv6
+ah6 IPSEC AH over IPv6
+esp6 IPSEC ESP over IPv6
+sctp6 SCTP over IPv6
+.TE
+.TP
+.BI rule \ N
+Retrieves the RX classification rule with the given ID.
+.RE
+.PD
+.RE
+.TP
+.B \-N \-U \-\-config\-nfc \-\-config\-ntuple
+Configures receive network flow classification options or rules.
+.RS 4
+.TP
+.BR rx\-flow\-hash \ \*(FL \: \*(HO
+Configures the hash options for the specified flow type.
+.TS
+nokeep;
+lB l.
+m Hash on the Layer 2 destination address of the rx packet.
+v Hash on the VLAN tag of the rx packet.
+t Hash on the Layer 3 protocol field of the rx packet.
+s Hash on the IP source address of the rx packet.
+d Hash on the IP destination address of the rx packet.
+f Hash on bytes 0 and 1 of the Layer 4 header of the rx packet.
+n Hash on bytes 2 and 3 of the Layer 4 header of the rx packet.
+r T{
+Discard all packets of this flow type. When this option is set, all
+other options are ignored.
+T}
+.TE
+.TP
+.B flow\-type \*(NC
+Inserts or updates a classification rule for the specified flow type.
+.TS
+nokeep;
+lB l.
+ether Ethernet
+ip4 Raw IPv4
+tcp4 TCP over IPv4
+udp4 UDP over IPv4
+sctp4 SCTP over IPv4
+ah4 IPSEC AH over IPv4
+esp4 IPSEC ESP over IPv4
+ip6 Raw IPv6
+tcp6 TCP over IPv6
+udp6 UDP over IPv6
+sctp6 SCTP over IPv6
+ah6 IPSEC AH over IPv6
+esp6 IPSEC ESP over IPv6
+.TE
+.PP
+For all fields that allow both a value and a mask to be specified, the
+mask may be specified immediately after the value using the \fBm\fR
+keyword, or separately using the field name keyword with \fB-mask\fR
+appended, e.g. \fBsrc-mask\fR.
+.PD
+.TP
+.BR src \ \*(MA\ [ m \ \*(MA]
+Includes the source MAC address, specified as 6 bytes in hexadecimal
+separated by colons, along with an optional mask. Valid only for
+flow-type ether.
+.TP
+.BR dst \ \*(MA\ [ m \ \*(MA]
+Includes the destination MAC address, specified as 6 bytes in hexadecimal
+separated by colons, along with an optional mask. Valid only for
+flow-type ether.
+.TP
+.BI proto \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Includes the Ethernet protocol number (ethertype) and an optional mask.
+Valid only for flow-type ether.
+.TP
+.BR src\-ip \ \*(PA\ [ m \ \*(PA]
+Specify the source IP address of the incoming packet to match along with
+an optional mask. Valid for all IP based flow-types.
+.TP
+.BR dst\-ip \ \*(PA\ [ m \ \*(PA]
+Specify the destination IP address of the incoming packet to match along
+with an optional mask. Valid for all IP based flow-types.
+.TP
+.BI tos \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the Type of Service field in the incoming packet to
+match along with an optional mask. Applies to all IPv4 based flow-types.
+.TP
+.BI tclass \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the Traffic Class field in the incoming packet to
+match along with an optional mask. Applies to all IPv6 based flow-types.
+.TP
+.BI l4proto \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Includes the layer 4 protocol number and optional mask. Valid only for
+flow-types ip4 and ip6.
+.TP
+.BI src\-port \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the source port field (applicable to TCP/UDP packets)
+in the incoming packet to match along with an optional mask. Valid for
+flow-types ip4, tcp4, udp4, and sctp4 and their IPv6 equivalents.
+.TP
+.BI dst\-port \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the destination port field (applicable to TCP/UDP
+packets)in the incoming packet to match along with an optional mask.
+Valid for flow-types ip4, tcp4, udp4, and sctp4 and their IPv6 equivalents.
+.TP
+.BI spi \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the security parameter index field (applicable to
+AH/ESP packets)in the incoming packet to match along with an optional
+mask. Valid for flow-types ip4, ah4, and esp4 and their IPv6 equivalents.
+.TP
+.BI l4data \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the first 4 Bytes of Layer 4 in the incoming packet to
+match along with an optional mask. Valid for ip4 and ip6 flow-types.
+.TP
+.BI vlan\-etype \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Includes the VLAN tag Ethertype and an optional mask.
+.TP
+.BI vlan \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Includes the VLAN tag and an optional mask.
+.TP
+.BI user\-def \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Includes 64-bits of user-specific data and an optional mask.
+.TP
+.BR dst-mac \ \*(MA\ [ m \ \*(MA]
+Includes the destination MAC address, specified as 6 bytes in hexadecimal
+separated by colons, along with an optional mask.
+Valid for all IP based flow-types.
+.TP
+.BI action \ N
+Specifies the Rx queue to send packets to, or some other action.
+.TS
+nokeep;
+lB l.
+-1 Drop the matched flow
+-2 Use the matched flow as a Wake-on-LAN filter
+0 or higher Rx queue to route the flow
+.TE
+.TP
+.BI context \ N
+Specifies the RSS context to spread packets over multiple queues; either
+.B 0
+for the default RSS context, or a value returned by
+.BI ethtool\ -X\ ... \ context
+.BR new .
+.TP
+.BI vf \ N
+Specifies the Virtual Function the filter applies to. Not compatible with action.
+.TP
+.BI queue \ N
+Specifies the Rx queue to send packets to. Not compatible with action.
+.TP
+.BI loc \ N
+Specify the location/ID to insert the rule. This will overwrite
+any rule present in that location and will not go through any
+of the rule ordering process.
+.TP
+.BI delete \ N
+Deletes the RX classification rule with the given ID.
+.RE
+.TP
+.B \-w \-\-get\-dump
+Retrieves and prints firmware dump for the specified network device.
+By default, it prints out the dump flag, version and length of the dump data.
+When
+.I data
+is indicated, then ethtool fetches the dump data and directs it to a
+.I file.
+.TP
+.B \-W \-\-set\-dump
+Sets the dump flag for the device.
+.TP
+.B \-T \-\-show\-time\-stamping
+Show the device's time stamping capabilities and associated PTP
+hardware clock.
+.TP
+.B \-x \-\-show\-rxfh\-indir \-\-show\-rxfh
+Retrieves the receive flow hash indirection table and/or RSS hash key.
+.TP
+.B \-X \-\-set\-rxfh\-indir \-\-rxfh
+Configures the receive flow hash indirection table and/or RSS hash key.
+.RS 4
+.TP
+.BI hkey
+Sets RSS hash key of the specified network device. RSS hash key should be of device supported length.
+Hash key format must be in xx:yy:zz:aa:bb:cc format meaning both the nibbles of a byte should be mentioned
+even if a nibble is zero.
+.TP
+.BI hfunc
+Sets RSS hash function of the specified network device.
+List of RSS hash functions which kernel supports is shown as a part of the --show-rxfh command output.
+.TP
+.BI start\ N
+For the \fBequal\fR and \fBweight\fR options, sets the starting receive queue
+for spreading flows to \fIN\fR.
+.TP
+.BI equal\ N
+Sets the receive flow hash indirection table to spread flows evenly
+between the first \fIN\fR receive queues.
+.TP
+\fBweight\fR \fIW0 W1\fR ...
+Sets the receive flow hash indirection table to spread flows between
+receive queues according to the given weights. The sum of the weights
+must be non-zero and must not exceed the size of the indirection table.
+.TP
+.BI default
+Sets the receive flow hash indirection table to its default value.
+.TP
+\fBcontext \fICTX\fR | \fBnew\fR
+Specifies an RSS context to act on; either
+.B new
+to allocate a new RSS context, or
+.IR CTX ,
+a value returned by a previous
+.IB ... \ context
+.BR new .
+.TP
+.B delete
+Delete the specified RSS context. May only be used in conjunction with
+.B context
+and a non-zero
+.I CTX
+value.
+.RE
+.TP
+.B \-f \-\-flash
+Write a firmware image to flash or other non-volatile memory on the
+device.
+.RS 4
+.TP
+.I file
+Specifies the filename of the firmware image. The firmware must first
+be installed in one of the directories where the kernel firmware
+loader or firmware agent will look, such as /lib/firmware.
+.TP
+.I N
+If the device stores multiple firmware images in separate regions of
+non-volatile memory, this parameter may be used to specify which
+region is to be written. The default is 0, requesting that all
+regions are written. All other values are driver-dependent.
+.RE
+.PD
+.TP
+.B \-l \-\-show\-channels
+Queries the specified network device for the numbers of channels it has.
+A channel is an IRQ and the set of queues that can trigger that IRQ.
+.TP
+.B \-L \-\-set\-channels
+Changes the numbers of channels of the specified network device.
+.RS 4
+.TP
+.BI rx \ N
+Changes the number of channels with only receive queues.
+.TP
+.BI tx \ N
+Changes the number of channels with only transmit queues.
+.TP
+.BI other \ N
+Changes the number of channels used only for other purposes e.g. link interrupts or SR-IOV co-ordination.
+.TP
+.BI combined \ N
+Changes the number of multi-purpose channels.
+.RE
+.TP
+.B \-m \-\-dump\-module\-eeprom \-\-module\-info
+Retrieves and if possible decodes the EEPROM from plugin modules, e.g SFP+, QSFP.
+If the driver and module support it, the optical diagnostic information is also
+read and decoded.
+When either one of
+.I page,
+.I bank
+or
+.I i2c
+parameters is specified, dumps only of a single page or its portion is
+allowed. In such a case
+.I offset
+and
+.I length
+parameters are treated relatively to EEPROM page boundaries.
+.TP
+.B \-\-show\-priv\-flags
+Queries the specified network device for its private flags. The
+names and meanings of private flags (if any) are defined by each
+network device driver.
+.TP
+.B \-\-set\-priv\-flags
+Sets the device's private flags as specified.
+.RS 4
+.PP
+.I flag
+.A1 on off
+Sets the state of the named private flag.
+.RE
+.TP
+.B \-\-show\-eee
+Queries the specified network device for its support of Energy-Efficient
+Ethernet (according to the IEEE 802.3az specifications)
+.TP
+.B \-\-set\-eee
+Sets the device EEE behaviour.
+.RS 4
+.TP
+.A2 eee on off
+Enables/disables the device support of EEE.
+.TP
+.A2 tx-lpi on off
+Determines whether the device should assert its Tx LPI.
+.TP
+.BI advertise \ N
+Sets the speeds for which the device should advertise EEE capabilities.
+Values are as for
+.B \-\-change advertise
+.TP
+.BI tx-timer \ N
+Sets the amount of time the device should stay in idle mode prior to asserting
+its Tx LPI (in microseconds). This has meaning only when Tx LPI is enabled.
+.RE
+.TP
+.B \-\-set\-phy\-tunable
+Sets the PHY tunable parameters.
+.RS 4
+.TP
+.A2 downshift on off
+Specifies whether downshift should be enabled.
+.TS
+nokeep;
+lB l.
+.BI count \ N
+ Sets the PHY downshift re-tries count.
+.TE
+.TP
+.A2 fast-link-down on off
+Specifies whether Fast Link Down should be enabled and time until link down (if supported).
+.TS
+nokeep;
+lB l.
+.BI msecs \ N
+ Sets the period after which the link is reported as down. Note that the PHY may choose
+ the closest supported value. Only on reading back the tunable do you get the actual value.
+.TE
+.TP
+.A2 energy-detect-power-down on off
+Specifies whether Energy Detect Power Down (EDPD) should be enabled (if supported).
+This will put the RX and TX circuit blocks into a low power mode, and the PHY will
+wake up periodically to send link pulses to avoid any lock-up situation with a peer
+PHY that may also have EDPD enabled. By default, this setting will also enable the
+periodic transmission of TX pulses.
+.TS
+nokeep;
+lB l.
+.BI msecs \ N
+ Some PHYs support configuration of the wake-up interval to send TX pulses.
+ This setting allows the control of this interval, and 0 disables TX pulses
+ if the PHY supports this. Disabling TX pulses can create a lock-up situation
+ where neither of the PHYs wakes the other one. If unspecified the default
+ value (in milliseconds) will be used by the PHY.
+.TE
+.TP
+.PD
+.RE
+.TP
+.B \-\-get\-phy\-tunable
+Gets the PHY tunable parameters.
+.RS 4
+.TP
+.B downshift
+For operation in cabling environments that are incompatible with 1000BASE-T,
+PHY device provides an automatic link speed downshift operation.
+Link speed downshift after N failed 1000BASE-T auto-negotiation attempts.
+Downshift is useful where cable does not have the 4 pairs instance.
+
+Gets the PHY downshift count/status.
+.TP
+.B fast\-link\-down
+Depending on the mode it may take 0.5s - 1s until a broken link is reported as down.
+In certain use cases a link-down event needs to be reported as soon as possible.
+Some PHYs support a Fast Link Down Feature and may allow configuration of the delay
+before a broken link is reported as being down.
+
+Gets the PHY Fast Link Down status / period.
+.TP
+.B energy\-detect\-power\-down
+Gets the current configured setting for Energy Detect Power Down (if supported).
+
+.RE
+.TP
+.B \-\-get\-tunable
+Get the tunable parameters.
+.RS 4
+.TP
+.B rx\-copybreak
+Get the current rx copybreak value in bytes.
+.TP
+.B tx\-copybreak
+Get the current tx copybreak value in bytes.
+.TP
+.B tx\-buf\-size
+Get the current tx copybreak buffer size in bytes.
+.TP
+.B pfc\-prevention\-tout
+Get the current pfc prevention timeout value in msecs.
+.RE
+.TP
+.B \-\-set\-tunable
+Set driver's tunable parameters.
+.RS 4
+.TP
+.BI rx\-copybreak \ N
+Set the rx copybreak value in bytes.
+.TP
+.BI tx\-copybreak \ N
+Set the tx copybreak value in bytes.
+.TP
+.BI tx\-buf\-size \ N
+Set the tx copybreak buffer size in bytes.
+.TP
+.BI pfc\-prevention\-tout \ N
+Set pfc prevention timeout in msecs. Value of 0 means disable and 65535 means auto.
+.RE
+.TP
+.B \-\-reset
+Reset hardware components specified by flags and components listed below
+.RS 4
+.TP
+.BI flags \ N
+Resets the components based on direct flags mask
+.TP
+.B mgmt
+Management processor
+.TP
+.B irq
+Interrupt requester
+.TP
+.B dma
+DMA engine
+.TP
+.B filter
+Filtering/flow direction
+.TP
+.B offload
+Protocol offload
+.TP
+.B mac
+Media access controller
+.TP
+.B phy
+Transceiver/PHY
+.TP
+.B ram
+RAM shared between multiple components
+.B ap
+Application Processor
+.TP
+.B dedicated
+All components dedicated to this interface
+.TP
+.B all
+All components used by this interface, even if shared
+.RE
+.TP
+.B \-\-show\-fec
+Queries the specified network device for its support of Forward Error Correction.
+.TP
+.B \-\-set\-fec
+Configures Forward Error Correction for the specified network device.
+
+Forward Error Correction modes selected by a user are expected to be persisted
+after any hotplug events. If a module is swapped that does not support the
+current FEC mode, the driver or firmware must take the link down
+administratively and report the problem in the system logs for users to correct.
+.RS 4
+.TP
+.BR encoding\ auto | off | rs | baser | llrs \ [...]
+
+Sets the FEC encoding for the device. Combinations of options are specified as
+e.g.
+.B encoding auto rs
+; the semantics of such combinations vary between drivers.
+.TS
+nokeep;
+lB l.
+auto Use the driver's default encoding
+off Turn off FEC
+RS Force RS-FEC encoding
+BaseR Force BaseR encoding
+LLRS Force LLRS-FEC encoding
+.TE
+.RE
+.TP
+.B \-Q|\-\-per\-queue
+Applies provided sub command to specific queues.
+.RS 4
+.TP
+.B queue_mask %x
+Sets the specific queues which the sub command is applied to.
+If queue_mask is not set, the sub command will be applied to all queues.
+.TP
+.B sub_command
+Sub command to apply. The supported sub commands include --show-coalesce and
+--coalesce.
+.RE
+.TP
+.B \-\-cable\-test
+Perform a cable test and report the results. What results are returned depends
+on the capabilities of the network interface. Typically open pairs and shorted
+pairs can be reported, along with pairs being O.K. When a fault is detected
+the approximate distance to the fault may be reported.
+.TP
+.B \-\-cable\-test\-tdr
+Perform a cable test and report the raw Time Domain Reflectometer
+data. A pulse is sent down a cable pair and the amplitude of the
+reflection, for a given distance, is reported. A break in the cable
+returns a big reflection. Minor damage to the cable returns a small
+reflection. If the cable is shorted, the amplitude of the reflection
+can be negative. By default, data is returned for lengths between 0
+and 150m at 1m steps, for all pairs. However parameters can be passed
+to restrict the collection of data. It should be noted, that the
+interface will round the distances to whatever granularity is actually
+implemented. This is often 0.8 of a meter. The results should include
+the actual rounded first and last distance and step size.
+.RS 4
+.TP
+.B first \ N
+Distance along the cable, in meters, where the first measurement
+should be made.
+.TP
+.B last \ N
+Distance along the cable, in meters, where the last measurement should
+be made.
+.TP
+.B step \ N
+Distance, in meters, between each measurement.
+.TP
+.B pair \ N
+Which pair should be measured. Typically a cable has 4 pairs. 0 = Pair A, 1 = Pair B, ...
+.RE
+.TP
+.B \-\-monitor
+Listens to netlink notification and displays them.
+.RS 4
+.TP
+.I command
+If argument matching a command is used, ethtool only shows notifications of
+this type. Without such argument or with --all, all notification types are
+shown.
+.TP
+.I devname
+If a device name is used as argument, only notification for this device are
+shown. Default is to show notifications for all devices.
+.RE
+.TP
+.B \-\-show\-tunnels
+Show tunnel-related device capabilities and state.
+List UDP ports kernel has programmed the device to parse as VxLAN,
+or GENEVE tunnels.
+.RE
+.TP
+.B \-\-show\-module
+Show the transceiver module's parameters.
+.RE
+.TP
+.B \-\-set\-module
+Set the transceiver module's parameters.
+.RS 4
+.TP
+.A2 power-mode-policy high auto
+Set the power mode policy for the module. When set to \fBhigh\fR, the module
+always operates at high power mode. When set to \fBauto\fR, the module is
+transitioned by the host to high power mode when the first port using it is put
+administratively up and to low power mode when the last port using it is put
+administratively down. The power mode policy can be set before a module is
+plugged-in.
+.RE
+.TP
+.B \-\-get\-plca\-cfg
+Show the current PLCA parameters for the given interface.
+.RE
+.TP
+.B \-\-set\-plca\-cfg
+Change the PLCA settings for the given interface.
+.RS 4
+.TP
+.A2 enable on off
+Enables or disables the PLCA function. When the PLCA RS is disabled (default),
+the PHY operates in plain CSMA/CD mode. To enable PLCA, the PHY must be assigned
+a unique \fBplca\-id\fR other than 255. This one can be configured concurrently
+with the enable parameter. The \fBenable\fR parameter maps to IEEE 802.3cg-2019
+clause 30.16.1.1.1 (aPLCAAdminState) and clause 30.16.1.2.1 (acPLCAAdminControl).
+.TP
+.BI node\-id \ N
+The unique node identifier in the range [0 .. 255]. Node ID 0 is reserved for
+the coordinator node, the one generating the BEACON signal. There must be
+exactly one coordinator on a PLCA network. Setting the node ID to 255 (default)
+disables the node. This parameter maps to IEEE 802.3cg-2019 clause 30.16.1.1.4
+(aPLCALocalNodeID).
+.TP
+.BI node\-cnt \ N
+The node-cnt [1 .. 255] should be set after the maximum number of nodes that
+can be plugged to the multi-drop network. This parameter regulates the minimum
+length of the PLCA cycle. Therefore, it is only meaningful for the coordinator
+node (\fBnod-id\fR = 0). Setting this parameter on a follower node has no
+effect. The \fBnode\-cnt\fR parameter maps to IEEE 802.3cg-2019 clause
+30.16.1.1.3 (aPLCANodeCount).
+.TP
+.BI to\-tmr \ N
+The TO timer parameter sets the value of the transmit opportunity timer in
+bit-times, and shall be set equal across all the nodes sharing the same
+medium for PLCA to work. The default value of 32 is enough to cover a link of
+roughly 50 mt. This parameter maps to IEEE 802.3cg-2019 clause 30.16.1.1.5
+(aPLCATransmitOpportunityTimer).
+.TP
+.BI burst\-cnt \ N
+The \fBburst\-cnt\fR parameter [0 .. 255] indicates the extra number of packets
+that the node is allowed to send during a single transmit opportunity.
+By default, this attribute is 0, meaning that the node can send a sigle frame
+per TO. When greater than 0, the PLCA RS keeps the TO after any transmission,
+waiting for the MAC to send a new frame for up to \fBburst\-tmr\fR BTs. This can
+only happen a number of times per PLCA cycle up to the value of this parameter.
+After that, the burst is over and the normal counting of TOs resumes.
+This parameter maps to IEEE 802.3cg-2019 clause 30.16.1.1.6 (aPLCAMaxBurstCount).
+.TP
+.BI burst\-tmr \ N
+The \fBburst\-tmr\fR parameter [0 .. 255] sets how many bit-times the PLCA RS
+waits for the MAC to initiate a new transmission when \fBburst\-cnt\fR is
+greater than 0. If the MAC fails to send a new frame within this time, the burst
+ends and the counting of TOs resumes. Otherwise, the new frame is sent as part
+of the current burst. This parameter maps to IEEE 802.3cg-2019 clause
+30.16.1.1.7 (aPLCABurstTimer). The value of \fBburst\-tmr\fR should be set
+greater than the Inter-Frame-Gap (IFG) time of the MAC (plus some margin)
+for PLCA burst mode to work as intended.
+.RE
+.TP
+.B \-\-get\-plca\-status
+Show the current PLCA status for the given interface. If \fBon\fR, the PHY is
+successfully receiving or generating the BEACON signal. If \fBoff\fR, the PLCA
+function is temporarily disabled and the PHY is operating in plain CSMA/CD mode.
+.RE
+.TP
+.B \-\-show\-mm
+Show the MAC Merge layer state. The ethtool argument
+.B \-I \-\-include\-statistics
+can be used with this command, and MAC Merge layer statistics counters will
+also be retrieved.
+.RS 4
+.TP
+.B pmac-enabled
+Shows whether the pMAC is enabled and capable of receiving traffic and SMD-V
+frames (and responding to them with SMD-R replies).
+.TP
+.B tx-enabled
+Shows whether transmission on the pMAC is administratively enabled.
+.TP
+.B tx-active
+Shows whether transmission on the pMAC is active (verification is either
+successful, or was disabled).
+.TP
+.B tx-min-frag-size
+Shows the minimum size (in octets) of transmitted non-final fragments which
+can be received by the link partner. Corresponds to the standard addFragSize
+variable using the formula:
+
+tx-min-frag-size = 64 * (1 + addFragSize) - 4
+.TP
+.B rx-min-frag-size
+Shows the minimum size (in octets) of non-final fragments which the local
+device supports receiving.
+.TP
+.B verify-enabled
+Shows whether the verification state machine is enabled. This process, if
+successful, ensures that preemptible frames transmitted by the local device
+will not be dropped as error frames by the link partner.
+.TP
+.B verify-time
+Shows the interval in ms between verification attempts, represented as an
+integer between 1 and 128 ms. The standard defines a fixed number of
+verification attempts (verifyLimit) before failing the verification process.
+.TP
+.B max-verify-time
+Shows the maximum value for verify-time accepted by the local device, which
+may be less than 128 ms.
+.TP
+.B verify-status
+Shows the current state of the verification state machine of the local device.
+Values can be
+.B INITIAL,
+.B VERIFYING,
+.B SUCCEEDED,
+.B FAILED
+or
+.B DISABLED.
+
+.RE
+.TP
+.B \-\-set\-mm
+Set the MAC Merge layer parameters.
+.RS 4
+.TP
+.A2 pmac-enabled \ on off
+Enable reception for the pMAC.
+.TP
+.A2 tx-enabled \ on off
+Administatively enable transmission for the pMAC.
+.TP
+.B tx-min-frag-size \ N
+Set the minimum size (in octets) of transmitted non-final fragments which can
+be received by the link partner.
+.TP
+.A2 verify-enabled \ on off
+Enable or disable the verification state machine.
+.TP
+.B verify-time \ N
+Set the interval in ms between verification attempts.
+
+.RE
+.TP
+.B \-\-show\-pse
+Show the current Power Sourcing Equipment (PSE) status for the given interface.
+.RS 4
+.TP
+.B podl-pse-admin-state
+This attribute indicates the operational status of PoDL PSE functions, which
+can be modified using the
+.B podl-pse-admin-control
+parameter. It corresponds to IEEE 802.3-2018 30.15.1.1.2 (aPoDLPSEAdminState),
+with potential values being
+.B enabled, disabled
+.TP
+.B podl-pse-power-detection-status
+This attribute indicates the power detection status of the PoDL PSE. The
+status depend on internal PSE state machine and automatic PD classification
+support. It corresponds to IEEE 802.3-2018 30.15.1.1.3
+(aPoDLPSEPowerDetectionStatus) with potential values being
+.B disabled, searching, delivering power, sleep, idle, error
+.RE
+
+.RE
+.TP
+.B \-\-set\-pse
+Set Power Sourcing Equipment (PSE) parameters.
+.RS 4
+.TP
+.A2 podl-pse-admin-control \ enable disable
+This parameter manages PoDL PSE Admin operations in accordance with the IEEE
+802.3-2018 30.15.1.2.1 (acPoDLPSEAdminControl) specification.
+
+.SH BUGS
+Not supported (in part or whole) on all network drivers.
+.SH AUTHOR
+.B ethtool
+was written by David Miller.
+
+Modifications by
+Jeff Garzik,
+Tim Hockin,
+Jakub Jelinek,
+Andre Majorel,
+Eli Kupermann,
+Scott Feldman,
+Andi Kleen,
+Alexander Duyck,
+Sucheta Chakraborty,
+Jesse Brandeburg,
+Ben Hutchings,
+Scott Branden.
+.SH AVAILABILITY
+.B ethtool
+is available from
+.UR http://www.kernel.org/pub/software/network/ethtool/
+.UE
diff --git a/ethtool.c b/ethtool.c
new file mode 100644
index 0000000..3ac15a7
--- /dev/null
+++ b/ethtool.c
@@ -0,0 +1,6488 @@
+/*
+ * ethtool.c: Linux ethernet device configuration tool.
+ *
+ * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Portions Copyright 2001 Sun Microsystems
+ * Kernel 2.4 update Copyright 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+ * Wake-on-LAN,natsemi,misc support by Tim Hockin <thockin@sun.com>
+ * Portions Copyright 2002 Intel
+ * Portions Copyright (C) Sun Microsystems 2008
+ * do_test support by Eli Kupermann <eli.kupermann@intel.com>
+ * ETHTOOL_PHYS_ID support by Chris Leech <christopher.leech@intel.com>
+ * e1000 support by Scott Feldman <scott.feldman@intel.com>
+ * e100 support by Wen Tao <wen-hwa.tao@intel.com>
+ * ixgb support by Nicholas Nunley <Nicholas.d.nunley@intel.com>
+ * amd8111e support by Reeja John <reeja.john@amd.com>
+ * long arguments by Andi Kleen.
+ * SMSC LAN911x support by Steve Glendinning <steve.glendinning@smsc.com>
+ * Rx Network Flow Control configuration support <santwona.behera@sun.com>
+ * Various features by Ben Hutchings <bhutchings@solarflare.com>;
+ * Copyright 2009, 2010 Solarflare Communications
+ * MDI-X set support by Jesse Brandeburg <jesse.brandeburg@intel.com>
+ * Copyright 2012 Intel Corporation
+ * vmxnet3 support by Shrikrishna Khare <skhare@vmware.com>
+ * Various features by Ben Hutchings <ben@decadent.org.uk>;
+ * Copyright 2008-2010, 2013-2016 Ben Hutchings
+ * QSFP+/QSFP28 DOM support by Vidya Sagar Ravipati <vidya@cumulusnetworks.com>
+ *
+ * TODO:
+ * * show settings for all devices
+ */
+
+#include "internal.h"
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <sys/utsname.h>
+#include <limits.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/netlink.h>
+
+#include "common.h"
+#include "netlink/extapi.h"
+
+#ifndef MAX_ADDR_LEN
+#define MAX_ADDR_LEN 32
+#endif
+
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+static void exit_bad_args(void) __attribute__((noreturn));
+
+static void exit_bad_args(void)
+{
+ fprintf(stderr,
+ "ethtool: bad command line argument(s)\n"
+ "For more information run ethtool -h\n");
+ exit(1);
+}
+
+static void exit_nlonly_param(const char *name) __attribute__((noreturn));
+
+static void exit_nlonly_param(const char *name)
+{
+ fprintf(stderr,
+ "ethtool: parameter '%s' can be used only with netlink\n",
+ name);
+ exit(1);
+}
+
+typedef enum {
+ CMDL_NONE,
+ CMDL_BOOL,
+ CMDL_S32,
+ CMDL_U8,
+ CMDL_U16,
+ CMDL_U32,
+ CMDL_U64,
+ CMDL_BE16,
+ CMDL_IP4,
+ CMDL_STR,
+ CMDL_FLAG,
+ CMDL_MAC,
+} cmdline_type_t;
+
+struct cmdline_info {
+ const char *name;
+ cmdline_type_t type;
+ /* Points to int (BOOL), s32, u16, u32 (U32/FLAG/IP4), u64,
+ * char * (STR) or u8[6] (MAC). For FLAG, the value accumulates
+ * all flags to be set. */
+ void *wanted_val;
+ void *ioctl_val;
+ /* For FLAG, the flag value to be set/cleared */
+ u32 flag_val;
+ /* For FLAG, points to u32 and accumulates all flags seen.
+ * For anything else, points to int and is set if the option is
+ * seen. */
+ void *seen_val;
+};
+
+struct feature_def {
+ char name[ETH_GSTRING_LEN];
+ int off_flag_index; /* index in off_flag_def; negative if none match */
+};
+
+struct feature_defs {
+ size_t n_features;
+ /* Number of features each offload flag is associated with */
+ unsigned int off_flag_matched[OFF_FLAG_DEF_SIZE];
+ /* Name and offload flag index for each feature */
+ struct feature_def def[0];
+};
+
+#define FEATURE_BITS_TO_BLOCKS(n_bits) DIV_ROUND_UP(n_bits, 32U)
+#define FEATURE_WORD(blocks, index, field) ((blocks)[(index) / 32U].field)
+#define FEATURE_FIELD_FLAG(index) (1U << (index) % 32U)
+#define FEATURE_BIT_SET(blocks, index, field) \
+ (FEATURE_WORD(blocks, index, field) |= FEATURE_FIELD_FLAG(index))
+#define FEATURE_BIT_CLEAR(blocks, index, field) \
+ (FEATURE_WORD(blocks, index, filed) &= ~FEATURE_FIELD_FLAG(index))
+#define FEATURE_BIT_IS_SET(blocks, index, field) \
+ (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
+
+static long long
+get_int_range(char *str, int base, long long min, long long max)
+{
+ long long v;
+ char *endp;
+
+ if (!str)
+ exit_bad_args();
+ errno = 0;
+ v = strtoll(str, &endp, base);
+ if (errno || *endp || v < min || v > max)
+ exit_bad_args();
+ return v;
+}
+
+static unsigned long long
+get_uint_range(char *str, int base, unsigned long long max)
+{
+ unsigned long long v;
+ char *endp;
+
+ if (!str)
+ exit_bad_args();
+ errno = 0;
+ v = strtoull(str, &endp, base);
+ if (errno || *endp || v > max)
+ exit_bad_args();
+ return v;
+}
+
+static int get_int(char *str, int base)
+{
+ return get_int_range(str, base, INT_MIN, INT_MAX);
+}
+
+static u32 get_u32(char *str, int base)
+{
+ return get_uint_range(str, base, 0xffffffff);
+}
+
+static void get_mac_addr(char *src, unsigned char *dest)
+{
+ int count;
+ int i;
+ int buf[ETH_ALEN];
+
+ count = sscanf(src, "%2x:%2x:%2x:%2x:%2x:%2x",
+ &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]);
+ if (count != ETH_ALEN)
+ exit_bad_args();
+
+ for (i = 0; i < count; i++)
+ dest[i] = buf[i];
+}
+
+static int parse_hex_u32_bitmap(const char *s,
+ unsigned int nbits, u32 *result)
+{
+ const unsigned int nwords = __KERNEL_DIV_ROUND_UP(nbits, 32);
+ size_t slen = strlen(s);
+ size_t i;
+
+ /* ignore optional '0x' prefix */
+ if ((slen > 2) && (strncasecmp(s, "0x", 2) == 0)) {
+ slen -= 2;
+ s += 2;
+ }
+
+ if (slen > 8 * nwords) /* up to 2 digits per byte */
+ return -1;
+
+ memset(result, 0, 4 * nwords);
+ for (i = 0; i < slen; ++i) {
+ const unsigned int shift = (slen - 1 - i) * 4;
+ u32 *dest = &result[shift / 32];
+ u32 nibble;
+
+ if ('a' <= s[i] && s[i] <= 'f')
+ nibble = 0xa + (s[i] - 'a');
+ else if ('A' <= s[i] && s[i] <= 'F')
+ nibble = 0xa + (s[i] - 'A');
+ else if ('0' <= s[i] && s[i] <= '9')
+ nibble = (s[i] - '0');
+ else
+ return -1;
+
+ *dest |= (nibble << (shift % 32));
+ }
+
+ return 0;
+}
+
+static void parse_generic_cmdline(struct cmd_context *ctx,
+ int *changed,
+ struct cmdline_info *info,
+ unsigned int n_info)
+{
+ unsigned int argc = ctx->argc;
+ char **argp = ctx->argp;
+ unsigned int i, idx;
+ int found;
+
+ for (i = 0; i < argc; i++) {
+ found = 0;
+ for (idx = 0; idx < n_info; idx++) {
+ if (!strcmp(info[idx].name, argp[i])) {
+ found = 1;
+ *changed = 1;
+ if (info[idx].type != CMDL_FLAG &&
+ info[idx].seen_val)
+ *(int *)info[idx].seen_val = 1;
+ i += 1;
+ if (i >= argc)
+ exit_bad_args();
+ switch (info[idx].type) {
+ case CMDL_BOOL: {
+ int *p = info[idx].wanted_val;
+ if (!strcmp(argp[i], "on"))
+ *p = 1;
+ else if (!strcmp(argp[i], "off"))
+ *p = 0;
+ else
+ exit_bad_args();
+ break;
+ }
+ case CMDL_S32: {
+ s32 *p = info[idx].wanted_val;
+ *p = get_int_range(argp[i], 0,
+ -0x80000000LL,
+ 0x7fffffff);
+ break;
+ }
+ case CMDL_U8: {
+ u8 *p = info[idx].wanted_val;
+ *p = get_uint_range(argp[i], 0, 0xff);
+ break;
+ }
+ case CMDL_U16: {
+ u16 *p = info[idx].wanted_val;
+ *p = get_uint_range(argp[i], 0, 0xffff);
+ break;
+ }
+ case CMDL_U32: {
+ u32 *p = info[idx].wanted_val;
+ *p = get_uint_range(argp[i], 0,
+ 0xffffffff);
+ break;
+ }
+ case CMDL_U64: {
+ u64 *p = info[idx].wanted_val;
+ *p = get_uint_range(
+ argp[i], 0,
+ 0xffffffffffffffffLL);
+ break;
+ }
+ case CMDL_BE16: {
+ u16 *p = info[idx].wanted_val;
+ *p = cpu_to_be16(
+ get_uint_range(argp[i], 0,
+ 0xffff));
+ break;
+ }
+ case CMDL_IP4: {
+ u32 *p = info[idx].wanted_val;
+ struct in_addr in;
+ if (!inet_pton(AF_INET, argp[i], &in))
+ exit_bad_args();
+ *p = in.s_addr;
+ break;
+ }
+ case CMDL_MAC:
+ get_mac_addr(argp[i],
+ info[idx].wanted_val);
+ break;
+ case CMDL_FLAG: {
+ u32 *p;
+ p = info[idx].seen_val;
+ *p |= info[idx].flag_val;
+ if (!strcmp(argp[i], "on")) {
+ p = info[idx].wanted_val;
+ *p |= info[idx].flag_val;
+ } else if (strcmp(argp[i], "off")) {
+ exit_bad_args();
+ }
+ break;
+ }
+ case CMDL_STR: {
+ char **s = info[idx].wanted_val;
+ *s = strdup(argp[i]);
+ break;
+ }
+ default:
+ exit_bad_args();
+ }
+ break;
+ }
+ }
+ if (!found)
+ exit_bad_args();
+ }
+}
+
+static void flag_to_cmdline_info(const char *name, u32 value,
+ u32 *wanted, u32 *mask,
+ struct cmdline_info *cli)
+{
+ memset(cli, 0, sizeof(*cli));
+ cli->name = name;
+ cli->type = CMDL_FLAG;
+ cli->flag_val = value;
+ cli->wanted_val = wanted;
+ cli->seen_val = mask;
+}
+
+static int rxflow_str_to_type(const char *str)
+{
+ int flow_type = 0;
+
+ if (!strcmp(str, "tcp4"))
+ flow_type = TCP_V4_FLOW;
+ else if (!strcmp(str, "udp4"))
+ flow_type = UDP_V4_FLOW;
+ else if (!strcmp(str, "ah4") || !strcmp(str, "esp4"))
+ flow_type = AH_ESP_V4_FLOW;
+ else if (!strcmp(str, "sctp4"))
+ flow_type = SCTP_V4_FLOW;
+ else if (!strcmp(str, "tcp6"))
+ flow_type = TCP_V6_FLOW;
+ else if (!strcmp(str, "udp6"))
+ flow_type = UDP_V6_FLOW;
+ else if (!strcmp(str, "ah6") || !strcmp(str, "esp6"))
+ flow_type = AH_ESP_V6_FLOW;
+ else if (!strcmp(str, "sctp6"))
+ flow_type = SCTP_V6_FLOW;
+ else if (!strcmp(str, "ether"))
+ flow_type = ETHER_FLOW;
+
+ return flow_type;
+}
+
+static int do_version(struct cmd_context *ctx __maybe_unused)
+{
+ fprintf(stdout,
+ PACKAGE " version " VERSION
+#ifndef ETHTOOL_ENABLE_PRETTY_DUMP
+ " (pretty dumps disabled)"
+#endif
+ "\n");
+ return 0;
+}
+
+/* link mode routines */
+
+static ETHTOOL_DECLARE_LINK_MODE_MASK(all_advertised_modes);
+static ETHTOOL_DECLARE_LINK_MODE_MASK(all_advertised_flags);
+
+static void init_global_link_mode_masks(void)
+{
+ static const enum ethtool_link_mode_bit_indices
+ all_advertised_modes_bits[] = {
+ ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+ ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+ ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+ ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+ ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT,
+ ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
+ ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
+ ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseDR_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
+ ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
+ ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
+ ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
+ ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT,
+ ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT,
+ ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT,
+ ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT,
+ ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT,
+ ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT,
+ ETHTOOL_LINK_MODE_10baseT1S_Full_BIT,
+ ETHTOOL_LINK_MODE_10baseT1S_Half_BIT,
+ ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
+ };
+ static const enum ethtool_link_mode_bit_indices
+ additional_advertised_flags_bits[] = {
+ ETHTOOL_LINK_MODE_Autoneg_BIT,
+ ETHTOOL_LINK_MODE_TP_BIT,
+ ETHTOOL_LINK_MODE_AUI_BIT,
+ ETHTOOL_LINK_MODE_MII_BIT,
+ ETHTOOL_LINK_MODE_FIBRE_BIT,
+ ETHTOOL_LINK_MODE_BNC_BIT,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_Backplane_BIT,
+ ETHTOOL_LINK_MODE_FEC_NONE_BIT,
+ ETHTOOL_LINK_MODE_FEC_RS_BIT,
+ ETHTOOL_LINK_MODE_FEC_BASER_BIT,
+ ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
+ };
+ unsigned int i;
+
+ ethtool_link_mode_zero(all_advertised_modes);
+ ethtool_link_mode_zero(all_advertised_flags);
+ for (i = 0; i < ARRAY_SIZE(all_advertised_modes_bits); ++i) {
+ ethtool_link_mode_set_bit(all_advertised_modes_bits[i],
+ all_advertised_modes);
+ ethtool_link_mode_set_bit(all_advertised_modes_bits[i],
+ all_advertised_flags);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(additional_advertised_flags_bits); ++i) {
+ ethtool_link_mode_set_bit(
+ additional_advertised_flags_bits[i],
+ all_advertised_flags);
+ }
+}
+
+static void dump_link_caps(const char *prefix, const char *an_prefix,
+ const u32 *mask, int link_mode_only);
+
+static void dump_supported(const struct ethtool_link_usettings *link_usettings)
+{
+ fprintf(stdout, " Supported ports: [ ");
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_TP_BIT,
+ link_usettings->link_modes.supported))
+ fprintf(stdout, "TP ");
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_AUI_BIT,
+ link_usettings->link_modes.supported))
+ fprintf(stdout, "AUI ");
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_BNC_BIT,
+ link_usettings->link_modes.supported))
+ fprintf(stdout, "BNC ");
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_MII_BIT,
+ link_usettings->link_modes.supported))
+ fprintf(stdout, "MII ");
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_FIBRE_BIT,
+ link_usettings->link_modes.supported))
+ fprintf(stdout, "FIBRE ");
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_Backplane_BIT,
+ link_usettings->link_modes.supported))
+ fprintf(stdout, "Backplane ");
+ fprintf(stdout, "]\n");
+
+ dump_link_caps("Supported", "Supports",
+ link_usettings->link_modes.supported, 0);
+}
+
+/* Print link capability flags (supported, advertised or lp_advertised).
+ * Assumes that the corresponding SUPPORTED and ADVERTISED flags are equal.
+ */
+static void dump_link_caps(const char *prefix, const char *an_prefix,
+ const u32 *mask, int link_mode_only)
+{
+ static const struct {
+ int same_line; /* print on same line as previous */
+ unsigned int bit_index;
+ const char *name;
+ } mode_defs[] = {
+ { 0, ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+ "10baseT/Half" },
+ { 1, ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+ "10baseT/Full" },
+ { 0, ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+ "100baseT/Half" },
+ { 1, ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+ "100baseT/Full" },
+ { 0, ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+ "1000baseT/Half" },
+ { 1, ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ "1000baseT/Full" },
+ { 0, ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ "10000baseT/Full" },
+ { 0, ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+ "2500baseX/Full" },
+ { 0, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ "1000baseKX/Full" },
+ { 0, ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ "10000baseKX4/Full" },
+ { 0, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ "10000baseKR/Full" },
+ { 0, ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+ "10000baseR_FEC" },
+ { 0, ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT,
+ "20000baseMLD2/Full" },
+ { 0, ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
+ "20000baseKR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+ "40000baseKR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+ "40000baseCR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
+ "40000baseSR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
+ "40000baseLR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT,
+ "56000baseKR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT,
+ "56000baseCR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT,
+ "56000baseSR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT,
+ "56000baseLR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+ "25000baseCR/Full" },
+ { 0, ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ "25000baseKR/Full" },
+ { 0, ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+ "25000baseSR/Full" },
+ { 0, ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
+ "50000baseCR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
+ "50000baseKR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+ "100000baseKR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
+ "100000baseSR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+ "100000baseCR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
+ "100000baseLR4_ER4/Full" },
+ { 0, ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
+ "50000baseSR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+ "1000baseX/Full" },
+ { 0, ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
+ "10000baseCR/Full" },
+ { 0, ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+ "10000baseSR/Full" },
+ { 0, ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
+ "10000baseLR/Full" },
+ { 0, ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
+ "10000baseLRM/Full" },
+ { 0, ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
+ "10000baseER/Full" },
+ { 0, ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ "2500baseT/Full" },
+ { 0, ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+ "5000baseT/Full" },
+ { 0, ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
+ "50000baseKR/Full" },
+ { 0, ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
+ "50000baseSR/Full" },
+ { 0, ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
+ "50000baseCR/Full" },
+ { 0, ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
+ "50000baseLR_ER_FR/Full" },
+ { 0, ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
+ "50000baseDR/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
+ "100000baseKR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
+ "100000baseSR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
+ "100000baseCR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
+ "100000baseLR2_ER2_FR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
+ "100000baseDR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
+ "200000baseKR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
+ "200000baseSR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
+ "200000baseLR4_ER4_FR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
+ "200000baseDR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
+ "200000baseCR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+ "100baseT1/Full" },
+ { 0, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
+ "1000baseT1/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
+ "400000baseKR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
+ "400000baseSR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
+ "400000baseLR8_ER8_FR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
+ "400000baseDR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
+ "400000baseCR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
+ "100000baseKR/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
+ "100000baseSR/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
+ "100000baseLR_ER_FR/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseDR_Full_BIT,
+ "100000baseDR/Full" },
+ { 0, ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
+ "100000baseCR/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
+ "200000baseKR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
+ "200000baseSR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
+ "200000baseLR2_ER2_FR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
+ "200000baseDR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
+ "200000baseCR2/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT,
+ "400000baseKR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
+ "400000baseSR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
+ "400000baseLR4_ER4_FR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
+ "400000baseDR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT,
+ "400000baseCR4/Full" },
+ { 0, ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
+ "100baseFX/Half" },
+ { 1, ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
+ "100baseFX/Full" },
+ { 0, ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
+ "10baseT1L/Full" },
+ { 0, ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT,
+ "800000baseCR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT,
+ "800000baseKR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT,
+ "800000baseDR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT,
+ "800000baseDR8_2/Full" },
+ { 0, ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT,
+ "800000baseSR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT,
+ "800000baseVR8/Full" },
+ { 0, ETHTOOL_LINK_MODE_10baseT1S_Full_BIT,
+ "10baseT1S/Full" },
+ { 1, ETHTOOL_LINK_MODE_10baseT1S_Half_BIT,
+ "10baseT1S/Half" },
+ { 0, ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
+ "10baseT1S/Half" },
+ };
+ int indent;
+ int did1, new_line_pend;
+ int fecreported = 0;
+ unsigned int i;
+
+ /* Indent just like the separate functions used to */
+ indent = strlen(prefix) + 14;
+ if (indent < 24)
+ indent = 24;
+
+ fprintf(stdout, " %s link modes:%*s", prefix,
+ indent - (int)strlen(prefix) - 12, "");
+ did1 = 0;
+ new_line_pend = 0;
+ for (i = 0; i < ARRAY_SIZE(mode_defs); i++) {
+ if (did1 && !mode_defs[i].same_line)
+ new_line_pend = 1;
+ if (ethtool_link_mode_test_bit(mode_defs[i].bit_index,
+ mask)) {
+ if (new_line_pend) {
+ fprintf(stdout, "\n");
+ fprintf(stdout, " %*s", indent, "");
+ new_line_pend = 0;
+ }
+ did1++;
+ fprintf(stdout, "%s ", mode_defs[i].name);
+ }
+ }
+ if (did1 == 0)
+ fprintf(stdout, "Not reported");
+ fprintf(stdout, "\n");
+
+ if (!link_mode_only) {
+ fprintf(stdout, " %s pause frame use: ", prefix);
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_Pause_BIT, mask)) {
+ fprintf(stdout, "Symmetric");
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask))
+ fprintf(stdout, " Receive-only");
+ fprintf(stdout, "\n");
+ } else {
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask))
+ fprintf(stdout, "Transmit-only\n");
+ else
+ fprintf(stdout, "No\n");
+ }
+
+ fprintf(stdout, " %s auto-negotiation: ", an_prefix);
+ if (ethtool_link_mode_test_bit(
+ ETHTOOL_LINK_MODE_Autoneg_BIT, mask))
+ fprintf(stdout, "Yes\n");
+ else
+ fprintf(stdout, "No\n");
+
+ fprintf(stdout, " %s FEC modes:", prefix);
+ if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT,
+ mask)) {
+ fprintf(stdout, " None");
+ fecreported = 1;
+ }
+ if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT,
+ mask)) {
+ fprintf(stdout, " BaseR");
+ fecreported = 1;
+ }
+ if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT,
+ mask)) {
+ fprintf(stdout, " RS");
+ fecreported = 1;
+ }
+ if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
+ mask)) {
+ fprintf(stdout, " LLRS");
+ fecreported = 1;
+ }
+
+ if (!fecreported)
+ fprintf(stdout, " Not reported");
+ fprintf(stdout, "\n");
+ }
+}
+
+static int
+dump_link_usettings(const struct ethtool_link_usettings *link_usettings)
+{
+ dump_supported(link_usettings);
+ dump_link_caps("Advertised", "Advertised",
+ link_usettings->link_modes.advertising, 0);
+ if (!ethtool_link_mode_is_empty(
+ link_usettings->link_modes.lp_advertising))
+ dump_link_caps("Link partner advertised",
+ "Link partner advertised",
+ link_usettings->link_modes.lp_advertising, 0);
+
+ fprintf(stdout, " Speed: ");
+ if (link_usettings->base.speed == 0
+ || link_usettings->base.speed == (u16)(-1)
+ || link_usettings->base.speed == (u32)(-1))
+ fprintf(stdout, "Unknown!\n");
+ else
+ fprintf(stdout, "%uMb/s\n", link_usettings->base.speed);
+
+ fprintf(stdout, " Duplex: ");
+ switch (link_usettings->base.duplex) {
+ case DUPLEX_HALF:
+ fprintf(stdout, "Half\n");
+ break;
+ case DUPLEX_FULL:
+ fprintf(stdout, "Full\n");
+ break;
+ default:
+ fprintf(stdout, "Unknown! (%i)\n", link_usettings->base.duplex);
+ break;
+ };
+
+ fprintf(stdout, " Port: ");
+ switch (link_usettings->base.port) {
+ case PORT_TP:
+ fprintf(stdout, "Twisted Pair\n");
+ break;
+ case PORT_AUI:
+ fprintf(stdout, "AUI\n");
+ break;
+ case PORT_BNC:
+ fprintf(stdout, "BNC\n");
+ break;
+ case PORT_MII:
+ fprintf(stdout, "MII\n");
+ break;
+ case PORT_FIBRE:
+ fprintf(stdout, "FIBRE\n");
+ break;
+ case PORT_DA:
+ fprintf(stdout, "Direct Attach Copper\n");
+ break;
+ case PORT_NONE:
+ fprintf(stdout, "None\n");
+ break;
+ case PORT_OTHER:
+ fprintf(stdout, "Other\n");
+ break;
+ default:
+ fprintf(stdout, "Unknown! (%i)\n", link_usettings->base.port);
+ break;
+ };
+
+ fprintf(stdout, " PHYAD: %d\n", link_usettings->base.phy_address);
+ fprintf(stdout, " Transceiver: ");
+ switch (link_usettings->deprecated.transceiver) {
+ case XCVR_INTERNAL:
+ fprintf(stdout, "internal\n");
+ break;
+ case XCVR_EXTERNAL:
+ fprintf(stdout, "external\n");
+ break;
+ default:
+ fprintf(stdout, "Unknown!\n");
+ break;
+ };
+
+ fprintf(stdout, " Auto-negotiation: %s\n",
+ (link_usettings->base.autoneg == AUTONEG_DISABLE) ?
+ "off" : "on");
+
+ if (link_usettings->base.port == PORT_TP)
+ dump_mdix(link_usettings->base.eth_tp_mdix,
+ link_usettings->base.eth_tp_mdix_ctrl);
+
+ return 0;
+}
+
+static int dump_drvinfo(struct ethtool_drvinfo *info)
+{
+ fprintf(stdout,
+ "driver: %.*s\n"
+ "version: %.*s\n"
+ "firmware-version: %.*s\n"
+ "expansion-rom-version: %.*s\n"
+ "bus-info: %.*s\n"
+ "supports-statistics: %s\n"
+ "supports-test: %s\n"
+ "supports-eeprom-access: %s\n"
+ "supports-register-dump: %s\n"
+ "supports-priv-flags: %s\n",
+ (int)sizeof(info->driver), info->driver,
+ (int)sizeof(info->version), info->version,
+ (int)sizeof(info->fw_version), info->fw_version,
+ (int)sizeof(info->erom_version), info->erom_version,
+ (int)sizeof(info->bus_info), info->bus_info,
+ info->n_stats ? "yes" : "no",
+ info->testinfo_len ? "yes" : "no",
+ info->eedump_len ? "yes" : "no",
+ info->regdump_len ? "yes" : "no",
+ info->n_priv_flags ? "yes" : "no");
+
+ return 0;
+}
+
+static int parse_wolopts(char *optstr, u32 *data)
+{
+ *data = 0;
+ while (*optstr) {
+ switch (*optstr) {
+ case 'p':
+ *data |= WAKE_PHY;
+ break;
+ case 'u':
+ *data |= WAKE_UCAST;
+ break;
+ case 'm':
+ *data |= WAKE_MCAST;
+ break;
+ case 'b':
+ *data |= WAKE_BCAST;
+ break;
+ case 'a':
+ *data |= WAKE_ARP;
+ break;
+ case 'g':
+ *data |= WAKE_MAGIC;
+ break;
+ case 's':
+ *data |= WAKE_MAGICSECURE;
+ break;
+ case 'f':
+ *data |= WAKE_FILTER;
+ break;
+ case 'd':
+ *data = 0;
+ break;
+ default:
+ return -1;
+ }
+ optstr++;
+ }
+ return 0;
+}
+
+static int parse_rxfhashopts(char *optstr, u32 *data)
+{
+ *data = 0;
+ while (*optstr) {
+ switch (*optstr) {
+ case 'm':
+ *data |= RXH_L2DA;
+ break;
+ case 'v':
+ *data |= RXH_VLAN;
+ break;
+ case 't':
+ *data |= RXH_L3_PROTO;
+ break;
+ case 's':
+ *data |= RXH_IP_SRC;
+ break;
+ case 'd':
+ *data |= RXH_IP_DST;
+ break;
+ case 'f':
+ *data |= RXH_L4_B_0_1;
+ break;
+ case 'n':
+ *data |= RXH_L4_B_2_3;
+ break;
+ case 'r':
+ *data |= RXH_DISCARD;
+ break;
+ default:
+ return -1;
+ }
+ optstr++;
+ }
+ return 0;
+}
+
+static char *unparse_rxfhashopts(u64 opts)
+{
+ static char buf[300];
+
+ memset(buf, 0, sizeof(buf));
+
+ if (opts) {
+ if (opts & RXH_L2DA)
+ strcat(buf, "L2DA\n");
+ if (opts & RXH_VLAN)
+ strcat(buf, "VLAN tag\n");
+ if (opts & RXH_L3_PROTO)
+ strcat(buf, "L3 proto\n");
+ if (opts & RXH_IP_SRC)
+ strcat(buf, "IP SA\n");
+ if (opts & RXH_IP_DST)
+ strcat(buf, "IP DA\n");
+ if (opts & RXH_L4_B_0_1)
+ strcat(buf, "L4 bytes 0 & 1 [TCP/UDP src port]\n");
+ if (opts & RXH_L4_B_2_3)
+ strcat(buf, "L4 bytes 2 & 3 [TCP/UDP dst port]\n");
+ } else {
+ sprintf(buf, "None");
+ }
+
+ return buf;
+}
+
+static int convert_string_to_hashkey(char *rss_hkey, u32 key_size,
+ const char *rss_hkey_string)
+{
+ u32 i = 0;
+ int hex_byte, len;
+
+ do {
+ if (i > (key_size - 1)) {
+ fprintf(stderr,
+ "Key is too long for device (%u > %u)\n",
+ i + 1, key_size);
+ goto err;
+ }
+
+ if (sscanf(rss_hkey_string, "%2x%n", &hex_byte, &len) < 1 ||
+ len != 2) {
+ fprintf(stderr, "Invalid RSS hash key format\n");
+ goto err;
+ }
+
+ rss_hkey[i++] = hex_byte;
+ rss_hkey_string += 2;
+
+ if (*rss_hkey_string == ':') {
+ rss_hkey_string++;
+ } else if (*rss_hkey_string != '\0') {
+ fprintf(stderr, "Invalid RSS hash key format\n");
+ goto err;
+ }
+
+ } while (*rss_hkey_string);
+
+ if (i != key_size) {
+ fprintf(stderr, "Key is too short for device (%u < %u)\n",
+ i, key_size);
+ goto err;
+ }
+
+ return 0;
+err:
+ return 2;
+}
+
+static int parse_hkey(char **rss_hkey, u32 key_size,
+ const char *rss_hkey_string)
+{
+ if (!key_size) {
+ fprintf(stderr,
+ "Cannot set RX flow hash configuration:\n"
+ " Hash key setting not supported\n");
+ return 1;
+ }
+
+ *rss_hkey = malloc(key_size);
+ if (!(*rss_hkey)) {
+ perror("Cannot allocate memory for RSS hash key");
+ return 1;
+ }
+
+ if (convert_string_to_hashkey(*rss_hkey, key_size,
+ rss_hkey_string)) {
+ free(*rss_hkey);
+ *rss_hkey = NULL;
+ return 2;
+ }
+ return 0;
+}
+
+#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+static const struct {
+ const char *name;
+ int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+} driver_list[] = {
+ { "8139cp", realtek_dump_regs },
+ { "8139too", realtek_dump_regs },
+ { "r8169", realtek_dump_regs },
+ { "de2104x", de2104x_dump_regs },
+ { "e1000", e1000_dump_regs },
+ { "e1000e", e1000_dump_regs },
+ { "igb", igb_dump_regs },
+ { "ixgb", ixgb_dump_regs },
+ { "ixgbe", ixgbe_dump_regs },
+ { "ixgbevf", ixgbevf_dump_regs },
+ { "natsemi", natsemi_dump_regs },
+ { "e100", e100_dump_regs },
+ { "amd8111e", amd8111e_dump_regs },
+ { "pcnet32", pcnet32_dump_regs },
+ { "fec_8xx", fec_8xx_dump_regs },
+ { "ibm_emac", ibm_emac_dump_regs },
+ { "tg3", tg3_dump_regs },
+ { "skge", skge_dump_regs },
+ { "sky2", sky2_dump_regs },
+ { "vioc", vioc_dump_regs },
+ { "smsc911x", smsc911x_dump_regs },
+ { "at76c50x-usb", at76c50x_usb_dump_regs },
+ { "sfc", sfc_dump_regs },
+ { "st_mac100", st_mac100_dump_regs },
+ { "st_gmac", st_gmac_dump_regs },
+ { "et131x", et131x_dump_regs },
+ { "altera_tse", altera_tse_dump_regs },
+ { "vmxnet3", vmxnet3_dump_regs },
+ { "fjes", fjes_dump_regs },
+ { "lan78xx", lan78xx_dump_regs },
+ { "dsa", dsa_dump_regs },
+ { "fec", fec_dump_regs },
+ { "igc", igc_dump_regs },
+ { "bnxt_en", bnxt_dump_regs },
+ { "cpsw-switch", cpsw_dump_regs },
+ { "lan743x", lan743x_dump_regs },
+ { "fsl_enetc", fsl_enetc_dump_regs },
+ { "fsl_enetc_vf", fsl_enetc_dump_regs },
+ { "hns3", hns3_dump_regs },
+};
+#endif
+
+void dump_hex(FILE *file, const u8 *data, int len, int offset)
+{
+ int i;
+
+ fprintf(file, "Offset\t\tValues\n");
+ fprintf(file, "------\t\t------");
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0)
+ fprintf(file, "\n0x%04x:\t\t", i + offset);
+ fprintf(file, "%02x ", data[i]);
+ }
+ fprintf(file, "\n");
+}
+
+static int dump_regs(int gregs_dump_raw, int gregs_dump_hex,
+ struct ethtool_drvinfo *info, struct ethtool_regs *regs)
+{
+ if (gregs_dump_raw) {
+ fwrite(regs->data, regs->len, 1, stdout);
+ goto nested;
+ }
+
+#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ if (!gregs_dump_hex) {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(driver_list); i++)
+ if (!strncmp(driver_list[i].name, info->driver,
+ ETHTOOL_BUSINFO_LEN)) {
+ if (driver_list[i].func(info, regs) == 0)
+ goto nested;
+ /* This version (or some other
+ * variation in the dump format) is
+ * not handled; fall back to hex
+ */
+ break;
+ }
+ }
+#endif
+
+ dump_hex(stdout, regs->data, regs->len, 0);
+
+nested:
+ /* Recurse dump if some drvinfo and regs structures are nested */
+ if (info->regdump_len > regs->len + sizeof(*info) + sizeof(*regs)) {
+ info = (struct ethtool_drvinfo *)(&regs->data[0] + regs->len);
+ regs = (struct ethtool_regs *)(&regs->data[0] + regs->len + sizeof(*info));
+
+ return dump_regs(gregs_dump_raw, gregs_dump_hex, info, regs);
+ }
+
+ return 0;
+}
+
+static int dump_eeprom(int geeprom_dump_raw,
+ struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_eeprom *ee)
+{
+ if (geeprom_dump_raw) {
+ fwrite(ee->data, 1, ee->len, stdout);
+ return 0;
+ }
+#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ if (!strncmp("natsemi", info->driver, ETHTOOL_BUSINFO_LEN)) {
+ return natsemi_dump_eeprom(info, ee);
+ } else if (!strncmp("tg3", info->driver, ETHTOOL_BUSINFO_LEN)) {
+ return tg3_dump_eeprom(info, ee);
+ }
+#endif
+ dump_hex(stdout, ee->data, ee->len, ee->offset);
+
+ return 0;
+}
+
+static int dump_test(struct ethtool_test *test,
+ struct ethtool_gstrings *strings)
+{
+ unsigned int i;
+ int rc;
+
+ rc = test->flags & ETH_TEST_FL_FAILED;
+ fprintf(stdout, "The test result is %s\n", rc ? "FAIL" : "PASS");
+
+ if (test->flags & ETH_TEST_FL_EXTERNAL_LB)
+ fprintf(stdout, "External loopback test was %sexecuted\n",
+ (test->flags & ETH_TEST_FL_EXTERNAL_LB_DONE) ?
+ "" : "not ");
+
+ if (strings->len)
+ fprintf(stdout, "The test extra info:\n");
+
+ for (i = 0; i < strings->len; i++) {
+ fprintf(stdout, "%s\t %d\n",
+ (char *)(strings->data + i * ETH_GSTRING_LEN),
+ (u32) test->data[i]);
+ }
+
+ fprintf(stdout, "\n");
+ return rc;
+}
+
+static int dump_pause(const struct ethtool_pauseparam *epause,
+ u32 advertising, u32 lp_advertising)
+{
+ fprintf(stdout,
+ "Autonegotiate: %s\n"
+ "RX: %s\n"
+ "TX: %s\n",
+ epause->autoneg ? "on" : "off",
+ epause->rx_pause ? "on" : "off",
+ epause->tx_pause ? "on" : "off");
+
+ if (lp_advertising) {
+ int an_rx = 0, an_tx = 0;
+
+ /* Work out negotiated pause frame usage per
+ * IEEE 802.3-2005 table 28B-3.
+ */
+ if (advertising & lp_advertising & ADVERTISED_Pause) {
+ an_tx = 1;
+ an_rx = 1;
+ } else if (advertising & lp_advertising &
+ ADVERTISED_Asym_Pause) {
+ if (advertising & ADVERTISED_Pause)
+ an_rx = 1;
+ else if (lp_advertising & ADVERTISED_Pause)
+ an_tx = 1;
+ }
+
+ fprintf(stdout,
+ "RX negotiated: %s\n"
+ "TX negotiated: %s\n",
+ an_rx ? "on" : "off",
+ an_tx ? "on" : "off");
+ }
+
+ fprintf(stdout, "\n");
+ return 0;
+}
+
+static int dump_ring(const struct ethtool_ringparam *ering)
+{
+ fprintf(stdout,
+ "Pre-set maximums:\n"
+ "RX: %u\n"
+ "RX Mini: %u\n"
+ "RX Jumbo: %u\n"
+ "TX: %u\n",
+ ering->rx_max_pending,
+ ering->rx_mini_max_pending,
+ ering->rx_jumbo_max_pending,
+ ering->tx_max_pending);
+
+ fprintf(stdout,
+ "Current hardware settings:\n"
+ "RX: %u\n"
+ "RX Mini: %u\n"
+ "RX Jumbo: %u\n"
+ "TX: %u\n",
+ ering->rx_pending,
+ ering->rx_mini_pending,
+ ering->rx_jumbo_pending,
+ ering->tx_pending);
+
+ fprintf(stdout, "\n");
+ return 0;
+}
+
+static int dump_channels(const struct ethtool_channels *echannels)
+{
+ fprintf(stdout,
+ "Pre-set maximums:\n"
+ "RX: %u\n"
+ "TX: %u\n"
+ "Other: %u\n"
+ "Combined: %u\n",
+ echannels->max_rx, echannels->max_tx,
+ echannels->max_other,
+ echannels->max_combined);
+
+ fprintf(stdout,
+ "Current hardware settings:\n"
+ "RX: %u\n"
+ "TX: %u\n"
+ "Other: %u\n"
+ "Combined: %u\n",
+ echannels->rx_count, echannels->tx_count,
+ echannels->other_count,
+ echannels->combined_count);
+
+ fprintf(stdout, "\n");
+ return 0;
+}
+
+static int dump_coalesce(const struct ethtool_coalesce *ecoal)
+{
+ fprintf(stdout, "Adaptive RX: %s TX: %s\n",
+ ecoal->use_adaptive_rx_coalesce ? "on" : "off",
+ ecoal->use_adaptive_tx_coalesce ? "on" : "off");
+
+ fprintf(stdout,
+ "stats-block-usecs: %u\n"
+ "sample-interval: %u\n"
+ "pkt-rate-low: %u\n"
+ "pkt-rate-high: %u\n"
+ "\n"
+ "rx-usecs: %u\n"
+ "rx-frames: %u\n"
+ "rx-usecs-irq: %u\n"
+ "rx-frames-irq: %u\n"
+ "\n"
+ "tx-usecs: %u\n"
+ "tx-frames: %u\n"
+ "tx-usecs-irq: %u\n"
+ "tx-frames-irq: %u\n"
+ "\n"
+ "rx-usecs-low: %u\n"
+ "rx-frames-low: %u\n"
+ "tx-usecs-low: %u\n"
+ "tx-frames-low: %u\n"
+ "\n"
+ "rx-usecs-high: %u\n"
+ "rx-frames-high: %u\n"
+ "tx-usecs-high: %u\n"
+ "tx-frames-high: %u\n"
+ "\n",
+ ecoal->stats_block_coalesce_usecs,
+ ecoal->rate_sample_interval,
+ ecoal->pkt_rate_low,
+ ecoal->pkt_rate_high,
+
+ ecoal->rx_coalesce_usecs,
+ ecoal->rx_max_coalesced_frames,
+ ecoal->rx_coalesce_usecs_irq,
+ ecoal->rx_max_coalesced_frames_irq,
+
+ ecoal->tx_coalesce_usecs,
+ ecoal->tx_max_coalesced_frames,
+ ecoal->tx_coalesce_usecs_irq,
+ ecoal->tx_max_coalesced_frames_irq,
+
+ ecoal->rx_coalesce_usecs_low,
+ ecoal->rx_max_coalesced_frames_low,
+ ecoal->tx_coalesce_usecs_low,
+ ecoal->tx_max_coalesced_frames_low,
+
+ ecoal->rx_coalesce_usecs_high,
+ ecoal->rx_max_coalesced_frames_high,
+ ecoal->tx_coalesce_usecs_high,
+ ecoal->tx_max_coalesced_frames_high);
+
+ return 0;
+}
+
+void dump_per_queue_coalesce(struct ethtool_per_queue_op *per_queue_opt,
+ __u32 *queue_mask, int n_queues)
+{
+ struct ethtool_coalesce *ecoal;
+ int i, idx = 0;
+
+ ecoal = (struct ethtool_coalesce *)(per_queue_opt + 1);
+ for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
+ int queue = i * 32;
+ __u32 mask = queue_mask[i];
+
+ while (mask > 0) {
+ if (mask & 0x1) {
+ fprintf(stdout, "Queue: %d\n", queue);
+ dump_coalesce(ecoal + idx);
+ idx++;
+ }
+ mask = mask >> 1;
+ queue++;
+ }
+ if (idx == n_queues)
+ break;
+ }
+}
+
+struct feature_state {
+ u32 off_flags;
+ struct ethtool_gfeatures features;
+};
+
+static void dump_one_feature(const char *indent, const char *name,
+ const struct feature_state *state,
+ const struct feature_state *ref_state,
+ u32 index)
+{
+ if (ref_state &&
+ !(FEATURE_BIT_IS_SET(state->features.features, index, active) ^
+ FEATURE_BIT_IS_SET(ref_state->features.features, index, active)))
+ return;
+
+ printf("%s%s: %s%s\n",
+ indent, name,
+ FEATURE_BIT_IS_SET(state->features.features, index, active) ?
+ "on" : "off",
+ (!FEATURE_BIT_IS_SET(state->features.features, index, available)
+ || FEATURE_BIT_IS_SET(state->features.features, index,
+ never_changed))
+ ? " [fixed]"
+ : (FEATURE_BIT_IS_SET(state->features.features, index, requested)
+ ^ FEATURE_BIT_IS_SET(state->features.features, index, active))
+ ? (FEATURE_BIT_IS_SET(state->features.features, index, requested)
+ ? " [requested on]" : " [requested off]")
+ : "");
+}
+
+static unsigned int linux_version_code(void)
+{
+ struct utsname utsname;
+ unsigned version, patchlevel, sublevel = 0;
+
+ if (uname(&utsname))
+ return -1;
+ if (sscanf(utsname.release, "%u.%u.%u", &version, &patchlevel, &sublevel) < 2)
+ return -1;
+ return KERNEL_VERSION(version, patchlevel, sublevel);
+}
+
+static void dump_features(const struct feature_defs *defs,
+ const struct feature_state *state,
+ const struct feature_state *ref_state)
+{
+ unsigned int kernel_ver = linux_version_code();
+ unsigned int i, j;
+ int indent;
+ u32 value;
+
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ /* Don't show features whose state is unknown on this
+ * kernel version
+ */
+ if (defs->off_flag_matched[i] == 0 &&
+ ((off_flag_def[i].get_cmd == 0 &&
+ kernel_ver < off_flag_def[i].min_kernel_ver) ||
+ (off_flag_def[i].get_cmd == ETHTOOL_GUFO &&
+ kernel_ver >= KERNEL_VERSION(4, 14, 0))))
+ continue;
+
+ value = off_flag_def[i].value;
+
+ /* If this offload flag matches exactly one generic
+ * feature then it's redundant to show the flag and
+ * feature states separately. Otherwise, show the
+ * flag state first.
+ */
+ if (defs->off_flag_matched[i] != 1 &&
+ (!ref_state ||
+ (state->off_flags ^ ref_state->off_flags) & value)) {
+ printf("%s: %s\n",
+ off_flag_def[i].long_name,
+ (state->off_flags & value) ? "on" : "off");
+ indent = 1;
+ } else {
+ indent = 0;
+ }
+
+ /* Show matching features */
+ for (j = 0; j < defs->n_features; j++) {
+ if (defs->def[j].off_flag_index != (int)i)
+ continue;
+ if (defs->off_flag_matched[i] != 1)
+ /* Show all matching feature states */
+ dump_one_feature(indent ? "\t" : "",
+ defs->def[j].name,
+ state, ref_state, j);
+ else
+ /* Show full state with the old flag name */
+ dump_one_feature("", off_flag_def[i].long_name,
+ state, ref_state, j);
+ }
+ }
+
+ /* Show all unmatched features that have non-null names */
+ for (j = 0; j < defs->n_features; j++)
+ if (defs->def[j].off_flag_index < 0 && defs->def[j].name[0])
+ dump_one_feature("", defs->def[j].name,
+ state, ref_state, j);
+}
+
+static int dump_rxfhash(int fhash, u64 val)
+{
+ switch (fhash & ~FLOW_RSS) {
+ case TCP_V4_FLOW:
+ fprintf(stdout, "TCP over IPV4 flows");
+ break;
+ case UDP_V4_FLOW:
+ fprintf(stdout, "UDP over IPV4 flows");
+ break;
+ case SCTP_V4_FLOW:
+ fprintf(stdout, "SCTP over IPV4 flows");
+ break;
+ case AH_ESP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ fprintf(stdout, "IPSEC AH/ESP over IPV4 flows");
+ break;
+ case TCP_V6_FLOW:
+ fprintf(stdout, "TCP over IPV6 flows");
+ break;
+ case UDP_V6_FLOW:
+ fprintf(stdout, "UDP over IPV6 flows");
+ break;
+ case SCTP_V6_FLOW:
+ fprintf(stdout, "SCTP over IPV6 flows");
+ break;
+ case AH_ESP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ fprintf(stdout, "IPSEC AH/ESP over IPV6 flows");
+ break;
+ default:
+ break;
+ }
+
+ if (val & RXH_DISCARD) {
+ fprintf(stdout, " - All matching flows discarded on RX\n");
+ return 0;
+ }
+ fprintf(stdout, " use these fields for computing Hash flow key:\n");
+
+ fprintf(stdout, "%s\n", unparse_rxfhashopts(val));
+
+ return 0;
+}
+
+static void dump_eeecmd(struct ethtool_eee *ep)
+{
+ ETHTOOL_DECLARE_LINK_MODE_MASK(link_mode);
+
+ fprintf(stdout, " EEE status: ");
+ if (!ep->supported) {
+ fprintf(stdout, "not supported\n");
+ return;
+ } else if (!ep->eee_enabled) {
+ fprintf(stdout, "disabled\n");
+ } else {
+ fprintf(stdout, "enabled - ");
+ if (ep->eee_active)
+ fprintf(stdout, "active\n");
+ else
+ fprintf(stdout, "inactive\n");
+ }
+
+ fprintf(stdout, " Tx LPI:");
+ if (ep->tx_lpi_enabled)
+ fprintf(stdout, " %d (us)\n", ep->tx_lpi_timer);
+ else
+ fprintf(stdout, " disabled\n");
+
+ ethtool_link_mode_zero(link_mode);
+
+ link_mode[0] = ep->supported;
+ dump_link_caps("Supported EEE", "", link_mode, 1);
+
+ link_mode[0] = ep->advertised;
+ dump_link_caps("Advertised EEE", "", link_mode, 1);
+
+ link_mode[0] = ep->lp_advertised;
+ dump_link_caps("Link partner advertised EEE", "", link_mode, 1);
+}
+
+static void dump_fec(u32 fec)
+{
+ if (fec & ETHTOOL_FEC_NONE)
+ fprintf(stdout, " None");
+ if (fec & ETHTOOL_FEC_AUTO)
+ fprintf(stdout, " Auto");
+ if (fec & ETHTOOL_FEC_OFF)
+ fprintf(stdout, " Off");
+ if (fec & ETHTOOL_FEC_BASER)
+ fprintf(stdout, " BaseR");
+ if (fec & ETHTOOL_FEC_RS)
+ fprintf(stdout, " RS");
+ if (fec & ETHTOOL_FEC_LLRS)
+ fprintf(stdout, " LLRS");
+}
+
+#define N_SOTS 7
+
+static char *so_timestamping_labels[N_SOTS] = {
+ "hardware-transmit (SOF_TIMESTAMPING_TX_HARDWARE)",
+ "software-transmit (SOF_TIMESTAMPING_TX_SOFTWARE)",
+ "hardware-receive (SOF_TIMESTAMPING_RX_HARDWARE)",
+ "software-receive (SOF_TIMESTAMPING_RX_SOFTWARE)",
+ "software-system-clock (SOF_TIMESTAMPING_SOFTWARE)",
+ "hardware-legacy-clock (SOF_TIMESTAMPING_SYS_HARDWARE)",
+ "hardware-raw-clock (SOF_TIMESTAMPING_RAW_HARDWARE)",
+};
+
+#define N_TX_TYPES (HWTSTAMP_TX_ONESTEP_SYNC + 1)
+
+static char *tx_type_labels[N_TX_TYPES] = {
+ "off (HWTSTAMP_TX_OFF)",
+ "on (HWTSTAMP_TX_ON)",
+ "one-step-sync (HWTSTAMP_TX_ONESTEP_SYNC)",
+};
+
+#define N_RX_FILTERS (HWTSTAMP_FILTER_NTP_ALL + 1)
+
+static char *rx_filter_labels[N_RX_FILTERS] = {
+ "none (HWTSTAMP_FILTER_NONE)",
+ "all (HWTSTAMP_FILTER_ALL)",
+ "some (HWTSTAMP_FILTER_SOME)",
+ "ptpv1-l4-event (HWTSTAMP_FILTER_PTP_V1_L4_EVENT)",
+ "ptpv1-l4-sync (HWTSTAMP_FILTER_PTP_V1_L4_SYNC)",
+ "ptpv1-l4-delay-req (HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ)",
+ "ptpv2-l4-event (HWTSTAMP_FILTER_PTP_V2_L4_EVENT)",
+ "ptpv2-l4-sync (HWTSTAMP_FILTER_PTP_V2_L4_SYNC)",
+ "ptpv2-l4-delay-req (HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ)",
+ "ptpv2-l2-event (HWTSTAMP_FILTER_PTP_V2_L2_EVENT)",
+ "ptpv2-l2-sync (HWTSTAMP_FILTER_PTP_V2_L2_SYNC)",
+ "ptpv2-l2-delay-req (HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ)",
+ "ptpv2-event (HWTSTAMP_FILTER_PTP_V2_EVENT)",
+ "ptpv2-sync (HWTSTAMP_FILTER_PTP_V2_SYNC)",
+ "ptpv2-delay-req (HWTSTAMP_FILTER_PTP_V2_DELAY_REQ)",
+ "ntp-all (HWTSTAMP_FILTER_NTP_ALL)",
+};
+
+static int dump_tsinfo(const struct ethtool_ts_info *info)
+{
+ int i;
+
+ fprintf(stdout, "Capabilities:\n");
+
+ for (i = 0; i < N_SOTS; i++) {
+ if (info->so_timestamping & (1 << i))
+ fprintf(stdout, "\t%s\n", so_timestamping_labels[i]);
+ }
+
+ fprintf(stdout, "PTP Hardware Clock: ");
+
+ if (info->phc_index < 0)
+ fprintf(stdout, "none\n");
+ else
+ fprintf(stdout, "%d\n", info->phc_index);
+
+ fprintf(stdout, "Hardware Transmit Timestamp Modes:");
+
+ if (!info->tx_types)
+ fprintf(stdout, " none\n");
+ else
+ fprintf(stdout, "\n");
+
+ for (i = 0; i < N_TX_TYPES; i++) {
+ if (info->tx_types & (1 << i))
+ fprintf(stdout, "\t%s\n", tx_type_labels[i]);
+ }
+
+ fprintf(stdout, "Hardware Receive Filter Modes:");
+
+ if (!info->rx_filters)
+ fprintf(stdout, " none\n");
+ else
+ fprintf(stdout, "\n");
+
+ for (i = 0; i < N_RX_FILTERS; i++) {
+ if (info->rx_filters & (1 << i))
+ fprintf(stdout, "\t%s\n", rx_filter_labels[i]);
+ }
+
+ return 0;
+}
+
+static struct ethtool_gstrings *
+get_stringset(struct cmd_context *ctx, enum ethtool_stringset set_id,
+ ptrdiff_t drvinfo_offset, int null_terminate)
+{
+ struct {
+ struct ethtool_sset_info hdr;
+ u32 buf[1];
+ } sset_info;
+ struct ethtool_drvinfo drvinfo;
+ u32 len, i;
+ struct ethtool_gstrings *strings;
+
+ sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
+ sset_info.hdr.reserved = 0;
+ sset_info.hdr.sset_mask = 1ULL << set_id;
+ if (send_ioctl(ctx, &sset_info) == 0) {
+ const u32 *sset_lengths = sset_info.hdr.data;
+
+ len = sset_info.hdr.sset_mask ? sset_lengths[0] : 0;
+ } else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
+ /* Fallback for old kernel versions */
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ if (send_ioctl(ctx, &drvinfo))
+ return NULL;
+ len = *(u32 *)((char *)&drvinfo + drvinfo_offset);
+ } else {
+ return NULL;
+ }
+
+ strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
+ if (!strings)
+ return NULL;
+
+ strings->cmd = ETHTOOL_GSTRINGS;
+ strings->string_set = set_id;
+ strings->len = len;
+ if (len != 0 && send_ioctl(ctx, strings)) {
+ free(strings);
+ return NULL;
+ }
+
+ if (null_terminate)
+ for (i = 0; i < len; i++)
+ strings->data[(i + 1) * ETH_GSTRING_LEN - 1] = 0;
+
+ return strings;
+}
+
+static struct feature_defs *get_feature_defs(struct cmd_context *ctx)
+{
+ struct ethtool_gstrings *names;
+ struct feature_defs *defs;
+ unsigned int i, j;
+ u32 n_features;
+
+ names = get_stringset(ctx, ETH_SS_FEATURES, 0, 1);
+ if (names) {
+ n_features = names->len;
+ } else if (errno == EOPNOTSUPP || errno == EINVAL) {
+ /* Kernel doesn't support named features; not an error */
+ n_features = 0;
+ } else if (errno == EPERM) {
+ /* Kernel bug: ETHTOOL_GSSET_INFO was privileged.
+ * Work around it. */
+ n_features = 0;
+ } else {
+ return NULL;
+ }
+
+ defs = malloc(sizeof(*defs) + sizeof(defs->def[0]) * n_features);
+ if (!defs) {
+ free(names);
+ return NULL;
+ }
+
+ defs->n_features = n_features;
+ memset(defs->off_flag_matched, 0, sizeof(defs->off_flag_matched));
+
+ /* Copy out feature names and find those associated with legacy flags */
+ for (i = 0; i < defs->n_features; i++) {
+ memcpy(defs->def[i].name, names->data + i * ETH_GSTRING_LEN,
+ ETH_GSTRING_LEN);
+ defs->def[i].off_flag_index = -1;
+
+ for (j = 0;
+ j < OFF_FLAG_DEF_SIZE &&
+ defs->def[i].off_flag_index < 0;
+ j++) {
+ const char *pattern =
+ off_flag_def[j].kernel_name;
+ const char *name = defs->def[i].name;
+ for (;;) {
+ if (*pattern == '*') {
+ /* There is only one wildcard; so
+ * switch to a suffix comparison */
+ size_t pattern_len =
+ strlen(pattern + 1);
+ size_t name_len = strlen(name);
+ if (name_len < pattern_len)
+ break; /* name is too short */
+ name += name_len - pattern_len;
+ ++pattern;
+ } else if (*pattern != *name) {
+ break; /* mismatch */
+ } else if (*pattern == 0) {
+ defs->def[i].off_flag_index = j;
+ defs->off_flag_matched[j]++;
+ break;
+ } else {
+ ++name;
+ ++pattern;
+ }
+ }
+ }
+ }
+
+ free(names);
+ return defs;
+}
+
+static int do_gdrv(struct cmd_context *ctx)
+{
+ int err;
+ struct ethtool_drvinfo drvinfo;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ err = send_ioctl(ctx, &drvinfo);
+ if (err < 0) {
+ perror("Cannot get driver information");
+ return 71;
+ }
+ return dump_drvinfo(&drvinfo);
+}
+
+static int do_gpause(struct cmd_context *ctx)
+{
+ struct ethtool_pauseparam epause;
+ struct ethtool_cmd ecmd;
+ int err;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ fprintf(stdout, "Pause parameters for %s:\n", ctx->devname);
+
+ epause.cmd = ETHTOOL_GPAUSEPARAM;
+ err = send_ioctl(ctx, &epause);
+ if (err) {
+ perror("Cannot get device pause settings");
+ return 76;
+ }
+
+ if (epause.autoneg) {
+ ecmd.cmd = ETHTOOL_GSET;
+ err = send_ioctl(ctx, &ecmd);
+ if (err) {
+ perror("Cannot get device settings");
+ return 1;
+ }
+ dump_pause(&epause, ecmd.advertising, ecmd.lp_advertising);
+ } else {
+ dump_pause(&epause, 0, 0);
+ }
+
+ return 0;
+}
+
+static void do_generic_set1(struct cmdline_info *info, int *changed_out)
+{
+ int wanted, *v1, *v2;
+
+ v1 = info->wanted_val;
+ wanted = *v1;
+
+ if (wanted < 0)
+ return;
+
+ v2 = info->ioctl_val;
+ if (wanted == *v2) {
+ fprintf(stderr, "%s unmodified, ignoring\n", info->name);
+ } else {
+ *v2 = wanted;
+ *changed_out = 1;
+ }
+}
+
+static void do_generic_set(struct cmdline_info *info,
+ unsigned int n_info,
+ int *changed_out)
+{
+ unsigned int i;
+
+ for (i = 0; i < n_info; i++)
+ do_generic_set1(&info[i], changed_out);
+}
+
+static int do_spause(struct cmd_context *ctx)
+{
+ struct ethtool_pauseparam epause;
+ int gpause_changed = 0;
+ int pause_autoneg_wanted = -1;
+ int pause_rx_wanted = -1;
+ int pause_tx_wanted = -1;
+ struct cmdline_info cmdline_pause[] = {
+ {
+ .name = "autoneg",
+ .type = CMDL_BOOL,
+ .wanted_val = &pause_autoneg_wanted,
+ .ioctl_val = &epause.autoneg,
+ },
+ {
+ .name = "rx",
+ .type = CMDL_BOOL,
+ .wanted_val = &pause_rx_wanted,
+ .ioctl_val = &epause.rx_pause,
+ },
+ {
+ .name = "tx",
+ .type = CMDL_BOOL,
+ .wanted_val = &pause_tx_wanted,
+ .ioctl_val = &epause.tx_pause,
+ },
+ };
+ int err, changed = 0;
+
+ parse_generic_cmdline(ctx, &gpause_changed,
+ cmdline_pause, ARRAY_SIZE(cmdline_pause));
+
+ epause.cmd = ETHTOOL_GPAUSEPARAM;
+ err = send_ioctl(ctx, &epause);
+ if (err) {
+ perror("Cannot get device pause settings");
+ return 77;
+ }
+
+ do_generic_set(cmdline_pause, ARRAY_SIZE(cmdline_pause), &changed);
+
+ if (!changed) {
+ fprintf(stderr, "no pause parameters changed, aborting\n");
+ return 78;
+ }
+
+ epause.cmd = ETHTOOL_SPAUSEPARAM;
+ err = send_ioctl(ctx, &epause);
+ if (err) {
+ perror("Cannot set device pause parameters");
+ return 79;
+ }
+
+ return 0;
+}
+
+static int do_sring(struct cmd_context *ctx)
+{
+ struct ethtool_ringparam ering;
+ int gring_changed = 0;
+ s32 ring_rx_wanted = -1;
+ s32 ring_rx_mini_wanted = -1;
+ s32 ring_rx_jumbo_wanted = -1;
+ s32 ring_tx_wanted = -1;
+ struct cmdline_info cmdline_ring[] = {
+ {
+ .name = "rx",
+ .type = CMDL_S32,
+ .wanted_val = &ring_rx_wanted,
+ .ioctl_val = &ering.rx_pending,
+ },
+ {
+ .name = "rx-mini",
+ .type = CMDL_S32,
+ .wanted_val = &ring_rx_mini_wanted,
+ .ioctl_val = &ering.rx_mini_pending,
+ },
+ {
+ .name = "rx-jumbo",
+ .type = CMDL_S32,
+ .wanted_val = &ring_rx_jumbo_wanted,
+ .ioctl_val = &ering.rx_jumbo_pending,
+ },
+ {
+ .name = "tx",
+ .type = CMDL_S32,
+ .wanted_val = &ring_tx_wanted,
+ .ioctl_val = &ering.tx_pending,
+ },
+ };
+ int err, changed = 0;
+
+ parse_generic_cmdline(ctx, &gring_changed,
+ cmdline_ring, ARRAY_SIZE(cmdline_ring));
+
+ ering.cmd = ETHTOOL_GRINGPARAM;
+ err = send_ioctl(ctx, &ering);
+ if (err) {
+ perror("Cannot get device ring settings");
+ return 76;
+ }
+
+ do_generic_set(cmdline_ring, ARRAY_SIZE(cmdline_ring), &changed);
+
+ if (!changed) {
+ fprintf(stderr, "no ring parameters changed, aborting\n");
+ return 80;
+ }
+
+ ering.cmd = ETHTOOL_SRINGPARAM;
+ err = send_ioctl(ctx, &ering);
+ if (err) {
+ perror("Cannot set device ring parameters");
+ return 81;
+ }
+
+ return 0;
+}
+
+static int do_gring(struct cmd_context *ctx)
+{
+ struct ethtool_ringparam ering;
+ int err;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ fprintf(stdout, "Ring parameters for %s:\n", ctx->devname);
+
+ ering.cmd = ETHTOOL_GRINGPARAM;
+ err = send_ioctl(ctx, &ering);
+ if (err == 0) {
+ err = dump_ring(&ering);
+ if (err)
+ return err;
+ } else {
+ perror("Cannot get device ring settings");
+ return 76;
+ }
+
+ return 0;
+}
+
+static int do_schannels(struct cmd_context *ctx)
+{
+ struct ethtool_channels echannels;
+ int gchannels_changed;
+ s32 channels_rx_wanted = -1;
+ s32 channels_tx_wanted = -1;
+ s32 channels_other_wanted = -1;
+ s32 channels_combined_wanted = -1;
+ struct cmdline_info cmdline_channels[] = {
+ {
+ .name = "rx",
+ .type = CMDL_S32,
+ .wanted_val = &channels_rx_wanted,
+ .ioctl_val = &echannels.rx_count,
+ },
+ {
+ .name = "tx",
+ .type = CMDL_S32,
+ .wanted_val = &channels_tx_wanted,
+ .ioctl_val = &echannels.tx_count,
+ },
+ {
+ .name = "other",
+ .type = CMDL_S32,
+ .wanted_val = &channels_other_wanted,
+ .ioctl_val = &echannels.other_count,
+ },
+ {
+ .name = "combined",
+ .type = CMDL_S32,
+ .wanted_val = &channels_combined_wanted,
+ .ioctl_val = &echannels.combined_count,
+ },
+ };
+ int err, changed = 0;
+
+ parse_generic_cmdline(ctx, &gchannels_changed,
+ cmdline_channels, ARRAY_SIZE(cmdline_channels));
+
+ echannels.cmd = ETHTOOL_GCHANNELS;
+ err = send_ioctl(ctx, &echannels);
+ if (err) {
+ perror("Cannot get device channel parameters");
+ return 1;
+ }
+
+ do_generic_set(cmdline_channels, ARRAY_SIZE(cmdline_channels),
+ &changed);
+
+ if (!changed) {
+ fprintf(stderr, "no channel parameters changed.\n");
+ fprintf(stderr, "current values: rx %u tx %u other %u"
+ " combined %u\n", echannels.rx_count,
+ echannels.tx_count, echannels.other_count,
+ echannels.combined_count);
+ return 0;
+ }
+
+ echannels.cmd = ETHTOOL_SCHANNELS;
+ err = send_ioctl(ctx, &echannels);
+ if (err) {
+ perror("Cannot set device channel parameters");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int do_gchannels(struct cmd_context *ctx)
+{
+ struct ethtool_channels echannels;
+ int err;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ fprintf(stdout, "Channel parameters for %s:\n", ctx->devname);
+
+ echannels.cmd = ETHTOOL_GCHANNELS;
+ err = send_ioctl(ctx, &echannels);
+ if (err == 0) {
+ err = dump_channels(&echannels);
+ if (err)
+ return err;
+ } else {
+ perror("Cannot get device channel parameters");
+ return 1;
+ }
+ return 0;
+
+}
+
+static int do_gcoalesce(struct cmd_context *ctx)
+{
+ struct ethtool_coalesce ecoal = {};
+ int err;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ fprintf(stdout, "Coalesce parameters for %s:\n", ctx->devname);
+
+ ecoal.cmd = ETHTOOL_GCOALESCE;
+ err = send_ioctl(ctx, &ecoal);
+ if (err == 0) {
+ err = dump_coalesce(&ecoal);
+ if (err)
+ return err;
+ } else {
+ perror("Cannot get device coalesce settings");
+ return 82;
+ }
+
+ return 0;
+}
+
+#define DECLARE_COALESCE_OPTION_VARS() \
+ s32 coal_stats_wanted = -1; \
+ int coal_adaptive_rx_wanted = -1; \
+ int coal_adaptive_tx_wanted = -1; \
+ s32 coal_sample_rate_wanted = -1; \
+ s32 coal_pkt_rate_low_wanted = -1; \
+ s32 coal_pkt_rate_high_wanted = -1; \
+ s32 coal_rx_usec_wanted = -1; \
+ s32 coal_rx_frames_wanted = -1; \
+ s32 coal_rx_usec_irq_wanted = -1; \
+ s32 coal_rx_frames_irq_wanted = -1; \
+ s32 coal_tx_usec_wanted = -1; \
+ s32 coal_tx_frames_wanted = -1; \
+ s32 coal_tx_usec_irq_wanted = -1; \
+ s32 coal_tx_frames_irq_wanted = -1; \
+ s32 coal_rx_usec_low_wanted = -1; \
+ s32 coal_rx_frames_low_wanted = -1; \
+ s32 coal_tx_usec_low_wanted = -1; \
+ s32 coal_tx_frames_low_wanted = -1; \
+ s32 coal_rx_usec_high_wanted = -1; \
+ s32 coal_rx_frames_high_wanted = -1; \
+ s32 coal_tx_usec_high_wanted = -1; \
+ s32 coal_tx_frames_high_wanted = -1
+
+#define COALESCE_CMDLINE_INFO(__ecoal) \
+{ \
+ { \
+ .name = "adaptive-rx", \
+ .type = CMDL_BOOL, \
+ .wanted_val = &coal_adaptive_rx_wanted, \
+ .ioctl_val = &__ecoal.use_adaptive_rx_coalesce, \
+ }, \
+ { \
+ .name = "adaptive-tx", \
+ .type = CMDL_BOOL, \
+ .wanted_val = &coal_adaptive_tx_wanted, \
+ .ioctl_val = &__ecoal.use_adaptive_tx_coalesce, \
+ }, \
+ { \
+ .name = "sample-interval", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_sample_rate_wanted, \
+ .ioctl_val = &__ecoal.rate_sample_interval, \
+ }, \
+ { \
+ .name = "stats-block-usecs", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_stats_wanted, \
+ .ioctl_val = &__ecoal.stats_block_coalesce_usecs, \
+ }, \
+ { \
+ .name = "pkt-rate-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_pkt_rate_low_wanted, \
+ .ioctl_val = &__ecoal.pkt_rate_low, \
+ }, \
+ { \
+ .name = "pkt-rate-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_pkt_rate_high_wanted, \
+ .ioctl_val = &__ecoal.pkt_rate_high, \
+ }, \
+ { \
+ .name = "rx-usecs", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_usec_wanted, \
+ .ioctl_val = &__ecoal.rx_coalesce_usecs, \
+ }, \
+ { \
+ .name = "rx-frames", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_frames_wanted, \
+ .ioctl_val = &__ecoal.rx_max_coalesced_frames, \
+ }, \
+ { \
+ .name = "rx-usecs-irq", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_usec_irq_wanted, \
+ .ioctl_val = &__ecoal.rx_coalesce_usecs_irq, \
+ }, \
+ { \
+ .name = "rx-frames-irq", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_frames_irq_wanted, \
+ .ioctl_val = &__ecoal.rx_max_coalesced_frames_irq, \
+ }, \
+ { \
+ .name = "tx-usecs", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_usec_wanted, \
+ .ioctl_val = &__ecoal.tx_coalesce_usecs, \
+ }, \
+ { \
+ .name = "tx-frames", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_frames_wanted, \
+ .ioctl_val = &__ecoal.tx_max_coalesced_frames, \
+ }, \
+ { \
+ .name = "tx-usecs-irq", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_usec_irq_wanted, \
+ .ioctl_val = &__ecoal.tx_coalesce_usecs_irq, \
+ }, \
+ { \
+ .name = "tx-frames-irq", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_frames_irq_wanted, \
+ .ioctl_val = &__ecoal.tx_max_coalesced_frames_irq, \
+ }, \
+ { \
+ .name = "rx-usecs-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_usec_low_wanted, \
+ .ioctl_val = &__ecoal.rx_coalesce_usecs_low, \
+ }, \
+ { \
+ .name = "rx-frames-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_frames_low_wanted, \
+ .ioctl_val = &__ecoal.rx_max_coalesced_frames_low, \
+ }, \
+ { \
+ .name = "tx-usecs-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_usec_low_wanted, \
+ .ioctl_val = &__ecoal.tx_coalesce_usecs_low, \
+ }, \
+ { \
+ .name = "tx-frames-low", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_frames_low_wanted, \
+ .ioctl_val = &__ecoal.tx_max_coalesced_frames_low, \
+ }, \
+ { \
+ .name = "rx-usecs-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_usec_high_wanted, \
+ .ioctl_val = &__ecoal.rx_coalesce_usecs_high, \
+ }, \
+ { \
+ .name = "rx-frames-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_rx_frames_high_wanted, \
+ .ioctl_val = &__ecoal.rx_max_coalesced_frames_high,\
+ }, \
+ { \
+ .name = "tx-usecs-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_usec_high_wanted, \
+ .ioctl_val = &__ecoal.tx_coalesce_usecs_high, \
+ }, \
+ { \
+ .name = "tx-frames-high", \
+ .type = CMDL_S32, \
+ .wanted_val = &coal_tx_frames_high_wanted, \
+ .ioctl_val = &__ecoal.tx_max_coalesced_frames_high,\
+ }, \
+}
+
+static int do_scoalesce(struct cmd_context *ctx)
+{
+ struct ethtool_coalesce ecoal;
+ int gcoalesce_changed = 0;
+ DECLARE_COALESCE_OPTION_VARS();
+ struct cmdline_info cmdline_coalesce[] = COALESCE_CMDLINE_INFO(ecoal);
+ int err, changed = 0;
+
+ parse_generic_cmdline(ctx, &gcoalesce_changed,
+ cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce));
+
+ ecoal.cmd = ETHTOOL_GCOALESCE;
+ err = send_ioctl(ctx, &ecoal);
+ if (err) {
+ perror("Cannot get device coalesce settings");
+ return 76;
+ }
+
+ do_generic_set(cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce),
+ &changed);
+
+ if (!changed) {
+ fprintf(stderr, "no coalesce parameters changed, aborting\n");
+ return 80;
+ }
+
+ ecoal.cmd = ETHTOOL_SCOALESCE;
+ err = send_ioctl(ctx, &ecoal);
+ if (err) {
+ perror("Cannot set device coalesce parameters");
+ return 81;
+ }
+
+ return 0;
+}
+
+static struct feature_state *
+get_features(struct cmd_context *ctx, const struct feature_defs *defs)
+{
+ struct feature_state *state;
+ struct ethtool_value eval;
+ int err, allfail = 1;
+ u32 value;
+ int i;
+
+ state = malloc(sizeof(*state) +
+ FEATURE_BITS_TO_BLOCKS(defs->n_features) *
+ sizeof(state->features.features[0]));
+ if (!state)
+ return NULL;
+
+ state->off_flags = 0;
+
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ value = off_flag_def[i].value;
+ if (!off_flag_def[i].get_cmd)
+ continue;
+ eval.cmd = off_flag_def[i].get_cmd;
+ err = send_ioctl(ctx, &eval);
+ if (err) {
+ if (errno == EOPNOTSUPP &&
+ off_flag_def[i].get_cmd == ETHTOOL_GUFO)
+ continue;
+
+ fprintf(stderr,
+ "Cannot get device %s settings: %m\n",
+ off_flag_def[i].long_name);
+ } else {
+ if (eval.data)
+ state->off_flags |= value;
+ allfail = 0;
+ }
+ }
+
+ eval.cmd = ETHTOOL_GFLAGS;
+ err = send_ioctl(ctx, &eval);
+ if (err) {
+ perror("Cannot get device flags");
+ } else {
+ state->off_flags |= eval.data & ETH_FLAG_EXT_MASK;
+ allfail = 0;
+ }
+
+ if (defs->n_features) {
+ state->features.cmd = ETHTOOL_GFEATURES;
+ state->features.size = FEATURE_BITS_TO_BLOCKS(defs->n_features);
+ err = send_ioctl(ctx, &state->features);
+ if (err)
+ perror("Cannot get device generic features");
+ else
+ allfail = 0;
+ }
+
+ if (allfail) {
+ free(state);
+ return NULL;
+ }
+
+ return state;
+}
+
+static int do_gfeatures(struct cmd_context *ctx)
+{
+ struct feature_defs *defs;
+ struct feature_state *features;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ defs = get_feature_defs(ctx);
+ if (!defs) {
+ perror("Cannot get device feature names");
+ return 1;
+ }
+
+ fprintf(stdout, "Features for %s:\n", ctx->devname);
+
+ features = get_features(ctx, defs);
+ if (!features) {
+ fprintf(stdout, "no feature info available\n");
+ free(defs);
+ return 1;
+ }
+
+ dump_features(defs, features, NULL);
+ free(features);
+ free(defs);
+ return 0;
+}
+
+static int do_sfeatures(struct cmd_context *ctx)
+{
+ struct feature_defs *defs;
+ int any_changed = 0, any_mismatch = 0;
+ u32 off_flags_wanted = 0;
+ u32 off_flags_mask = 0;
+ struct ethtool_sfeatures *efeatures = NULL;
+ struct feature_state *old_state = NULL;
+ struct feature_state *new_state = NULL;
+ struct cmdline_info *cmdline_features;
+ struct ethtool_value eval;
+ unsigned int i, j;
+ int err, rc;
+
+ defs = get_feature_defs(ctx);
+ if (!defs) {
+ perror("Cannot get device feature names");
+ return 1;
+ }
+ if (defs->n_features) {
+ efeatures = malloc(sizeof(*efeatures) +
+ FEATURE_BITS_TO_BLOCKS(defs->n_features) *
+ sizeof(efeatures->features[0]));
+ if (!efeatures) {
+ perror("Cannot parse arguments");
+ rc = 1;
+ goto err;
+ }
+ efeatures->cmd = ETHTOOL_SFEATURES;
+ efeatures->size = FEATURE_BITS_TO_BLOCKS(defs->n_features);
+ memset(efeatures->features, 0,
+ FEATURE_BITS_TO_BLOCKS(defs->n_features) *
+ sizeof(efeatures->features[0]));
+ }
+
+ /* Generate cmdline_info for legacy flags and kernel-named
+ * features, and parse our arguments.
+ */
+ cmdline_features = calloc(2 * OFF_FLAG_DEF_SIZE + defs->n_features,
+ sizeof(cmdline_features[0]));
+ if (!cmdline_features) {
+ perror("Cannot parse arguments");
+ rc = 1;
+ goto err;
+ }
+ j = 0;
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ flag_to_cmdline_info(off_flag_def[i].short_name,
+ off_flag_def[i].value,
+ &off_flags_wanted, &off_flags_mask,
+ &cmdline_features[j++]);
+ flag_to_cmdline_info(off_flag_def[i].long_name,
+ off_flag_def[i].value,
+ &off_flags_wanted, &off_flags_mask,
+ &cmdline_features[j++]);
+ }
+ for (i = 0; i < defs->n_features; i++)
+ flag_to_cmdline_info(
+ defs->def[i].name, FEATURE_FIELD_FLAG(i),
+ &FEATURE_WORD(efeatures->features, i, requested),
+ &FEATURE_WORD(efeatures->features, i, valid),
+ &cmdline_features[j++]);
+ parse_generic_cmdline(ctx, &any_changed, cmdline_features,
+ 2 * OFF_FLAG_DEF_SIZE + defs->n_features);
+ free(cmdline_features);
+
+ if (!any_changed) {
+ fprintf(stdout, "no features changed\n");
+ rc = 0;
+ goto err;
+ }
+
+ old_state = get_features(ctx, defs);
+ if (!old_state) {
+ rc = 1;
+ goto err;
+ }
+
+ if (efeatures) {
+ /* For each offload that the user specified, update any
+ * related features that the user did not specify and that
+ * are not fixed. Warn if all related features are fixed.
+ */
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ int fixed = 1;
+
+ if (!(off_flags_mask & off_flag_def[i].value))
+ continue;
+
+ for (j = 0; j < defs->n_features; j++) {
+ if (defs->def[j].off_flag_index != (int)i ||
+ !FEATURE_BIT_IS_SET(
+ old_state->features.features,
+ j, available) ||
+ FEATURE_BIT_IS_SET(
+ old_state->features.features,
+ j, never_changed))
+ continue;
+
+ fixed = 0;
+ if (!FEATURE_BIT_IS_SET(efeatures->features,
+ j, valid)) {
+ FEATURE_BIT_SET(efeatures->features,
+ j, valid);
+ if (off_flags_wanted &
+ off_flag_def[i].value)
+ FEATURE_BIT_SET(
+ efeatures->features,
+ j, requested);
+ }
+ }
+
+ if (fixed)
+ fprintf(stderr, "Cannot change %s\n",
+ off_flag_def[i].long_name);
+ }
+
+ err = send_ioctl(ctx, efeatures);
+ if (err < 0) {
+ perror("Cannot set device feature settings");
+ rc = 1;
+ goto err;
+ }
+ } else {
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ if (!off_flag_def[i].set_cmd)
+ continue;
+ if (off_flags_mask & off_flag_def[i].value) {
+ eval.cmd = off_flag_def[i].set_cmd;
+ eval.data = !!(off_flags_wanted &
+ off_flag_def[i].value);
+ err = send_ioctl(ctx, &eval);
+ if (err) {
+ fprintf(stderr,
+ "Cannot set device %s settings: %m\n",
+ off_flag_def[i].long_name);
+ rc = 1;
+ goto err;
+ }
+ }
+ }
+
+ if (off_flags_mask & ETH_FLAG_EXT_MASK) {
+ eval.cmd = ETHTOOL_SFLAGS;
+ eval.data = (old_state->off_flags & ~off_flags_mask &
+ ETH_FLAG_EXT_MASK);
+ eval.data |= off_flags_wanted & ETH_FLAG_EXT_MASK;
+
+ err = send_ioctl(ctx, &eval);
+ if (err) {
+ perror("Cannot set device flag settings");
+ rc = 92;
+ goto err;
+ }
+ }
+ }
+
+ /* Compare new state with requested state */
+ new_state = get_features(ctx, defs);
+ if (!new_state) {
+ rc = 1;
+ goto err;
+ }
+ any_changed = new_state->off_flags != old_state->off_flags;
+ any_mismatch = (new_state->off_flags !=
+ ((old_state->off_flags & ~off_flags_mask) |
+ off_flags_wanted));
+ for (i = 0; i < FEATURE_BITS_TO_BLOCKS(defs->n_features); i++) {
+ if (new_state->features.features[i].active !=
+ old_state->features.features[i].active)
+ any_changed = 1;
+ if (new_state->features.features[i].active !=
+ ((old_state->features.features[i].active &
+ ~efeatures->features[i].valid) |
+ efeatures->features[i].requested))
+ any_mismatch = 1;
+ }
+ if (any_mismatch) {
+ if (!any_changed) {
+ fprintf(stderr,
+ "Could not change any device features\n");
+ rc = 1;
+ goto err;
+ }
+ printf("Actual changes:\n");
+ dump_features(defs, new_state, old_state);
+ }
+
+ rc = 0;
+
+err:
+ free(new_state);
+ free(old_state);
+ free(defs);
+ free(efeatures);
+
+ return rc;
+}
+
+static struct ethtool_link_usettings *
+do_ioctl_glinksettings(struct cmd_context *ctx)
+{
+ int err;
+ struct {
+ struct ethtool_link_settings req;
+ __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ } ecmd;
+ struct ethtool_link_usettings *link_usettings;
+ unsigned int u32_offs;
+
+ /* Handshake with kernel to determine number of words for link
+ * mode bitmaps. When requested number of bitmap words is not
+ * the one expected by kernel, the latter returns the integer
+ * opposite of what it is expecting. We request length 0 below
+ * (aka. invalid bitmap length) to get this info.
+ */
+ memset(&ecmd, 0, sizeof(ecmd));
+ ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
+ err = send_ioctl(ctx, &ecmd);
+ if (err < 0)
+ return NULL;
+
+ /* see above: we expect a strictly negative value from kernel.
+ */
+ if (ecmd.req.link_mode_masks_nwords >= 0
+ || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
+ return NULL;
+
+ /* got the real ecmd.req.link_mode_masks_nwords,
+ * now send the real request
+ */
+ ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
+ ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
+ err = send_ioctl(ctx, &ecmd);
+ if (err < 0)
+ return NULL;
+
+ if (ecmd.req.link_mode_masks_nwords <= 0
+ || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
+ return NULL;
+
+ /* Convert to usettings struct */
+ link_usettings = calloc(1, sizeof(*link_usettings));
+ if (link_usettings == NULL)
+ return NULL;
+
+ memcpy(&link_usettings->base, &ecmd.req, sizeof(link_usettings->base));
+ link_usettings->deprecated.transceiver = ecmd.req.transceiver;
+
+ /* copy link mode bitmaps */
+ u32_offs = 0;
+ memcpy(link_usettings->link_modes.supported,
+ &ecmd.link_mode_data[u32_offs],
+ 4 * ecmd.req.link_mode_masks_nwords);
+
+ u32_offs += ecmd.req.link_mode_masks_nwords;
+ memcpy(link_usettings->link_modes.advertising,
+ &ecmd.link_mode_data[u32_offs],
+ 4 * ecmd.req.link_mode_masks_nwords);
+
+ u32_offs += ecmd.req.link_mode_masks_nwords;
+ memcpy(link_usettings->link_modes.lp_advertising,
+ &ecmd.link_mode_data[u32_offs],
+ 4 * ecmd.req.link_mode_masks_nwords);
+
+ return link_usettings;
+}
+
+static int
+do_ioctl_slinksettings(struct cmd_context *ctx,
+ const struct ethtool_link_usettings *link_usettings)
+{
+ struct {
+ struct ethtool_link_settings req;
+ __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
+ } ecmd;
+ unsigned int u32_offs;
+
+ /* refuse to send ETHTOOL_SLINKSETTINGS ioctl if
+ * link_usettings was retrieved with ETHTOOL_GSET
+ */
+ if (link_usettings->base.cmd != ETHTOOL_GLINKSETTINGS)
+ return -1;
+
+ /* refuse to send ETHTOOL_SLINKSETTINGS ioctl if deprecated fields
+ * were set
+ */
+ if (link_usettings->deprecated.transceiver)
+ return -1;
+
+ if (link_usettings->base.link_mode_masks_nwords <= 0)
+ return -1;
+
+ memcpy(&ecmd.req, &link_usettings->base, sizeof(ecmd.req));
+ ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
+
+ /* copy link mode bitmaps */
+ u32_offs = 0;
+ memcpy(&ecmd.link_mode_data[u32_offs],
+ link_usettings->link_modes.supported,
+ 4 * ecmd.req.link_mode_masks_nwords);
+
+ u32_offs += ecmd.req.link_mode_masks_nwords;
+ memcpy(&ecmd.link_mode_data[u32_offs],
+ link_usettings->link_modes.advertising,
+ 4 * ecmd.req.link_mode_masks_nwords);
+
+ u32_offs += ecmd.req.link_mode_masks_nwords;
+ memcpy(&ecmd.link_mode_data[u32_offs],
+ link_usettings->link_modes.lp_advertising,
+ 4 * ecmd.req.link_mode_masks_nwords);
+
+ return send_ioctl(ctx, &ecmd);
+}
+
+static struct ethtool_link_usettings *
+do_ioctl_gset(struct cmd_context *ctx)
+{
+ int err;
+ struct ethtool_cmd ecmd;
+ struct ethtool_link_usettings *link_usettings;
+
+ memset(&ecmd, 0, sizeof(ecmd));
+ ecmd.cmd = ETHTOOL_GSET;
+ err = send_ioctl(ctx, &ecmd);
+ if (err < 0)
+ return NULL;
+
+ link_usettings = calloc(1, sizeof(*link_usettings));
+ if (link_usettings == NULL)
+ return NULL;
+
+ /* remember that ETHTOOL_GSET was used */
+ link_usettings->base.cmd = ETHTOOL_GSET;
+
+ link_usettings->base.link_mode_masks_nwords = 1;
+ link_usettings->link_modes.supported[0] = ecmd.supported;
+ link_usettings->link_modes.advertising[0] = ecmd.advertising;
+ link_usettings->link_modes.lp_advertising[0] = ecmd.lp_advertising;
+ link_usettings->base.speed = ethtool_cmd_speed(&ecmd);
+ link_usettings->base.duplex = ecmd.duplex;
+ link_usettings->base.port = ecmd.port;
+ link_usettings->base.phy_address = ecmd.phy_address;
+ link_usettings->deprecated.transceiver = ecmd.transceiver;
+ link_usettings->base.autoneg = ecmd.autoneg;
+ link_usettings->base.mdio_support = ecmd.mdio_support;
+ /* ignored (fully deprecated): maxrxpkt, maxtxpkt */
+ link_usettings->base.eth_tp_mdix = ecmd.eth_tp_mdix;
+ link_usettings->base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl;
+
+ return link_usettings;
+}
+
+static bool ethtool_link_mode_is_backward_compatible(const u32 *mask)
+{
+ unsigned int i;
+
+ for (i = 1; i < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32; ++i)
+ if (mask[i])
+ return false;
+
+ return true;
+}
+
+static int
+do_ioctl_sset(struct cmd_context *ctx,
+ const struct ethtool_link_usettings *link_usettings)
+{
+ struct ethtool_cmd ecmd;
+
+ /* refuse to send ETHTOOL_SSET ioctl if link_usettings was
+ * retrieved with ETHTOOL_GLINKSETTINGS
+ */
+ if (link_usettings->base.cmd != ETHTOOL_GSET)
+ return -1;
+
+ if (link_usettings->base.link_mode_masks_nwords <= 0)
+ return -1;
+
+ /* refuse to sset if any bit > 31 is set */
+ if (!ethtool_link_mode_is_backward_compatible(
+ link_usettings->link_modes.supported))
+ return -1;
+ if (!ethtool_link_mode_is_backward_compatible(
+ link_usettings->link_modes.advertising))
+ return -1;
+ if (!ethtool_link_mode_is_backward_compatible(
+ link_usettings->link_modes.lp_advertising))
+ return -1;
+
+ memset(&ecmd, 0, sizeof(ecmd));
+ ecmd.cmd = ETHTOOL_SSET;
+
+ ecmd.supported = link_usettings->link_modes.supported[0];
+ ecmd.advertising = link_usettings->link_modes.advertising[0];
+ ecmd.lp_advertising = link_usettings->link_modes.lp_advertising[0];
+ ethtool_cmd_speed_set(&ecmd, link_usettings->base.speed);
+ ecmd.duplex = link_usettings->base.duplex;
+ ecmd.port = link_usettings->base.port;
+ ecmd.phy_address = link_usettings->base.phy_address;
+ ecmd.transceiver = link_usettings->deprecated.transceiver;
+ ecmd.autoneg = link_usettings->base.autoneg;
+ ecmd.mdio_support = link_usettings->base.mdio_support;
+ /* ignored (fully deprecated): maxrxpkt, maxtxpkt */
+ ecmd.eth_tp_mdix = link_usettings->base.eth_tp_mdix;
+ ecmd.eth_tp_mdix_ctrl = link_usettings->base.eth_tp_mdix_ctrl;
+ return send_ioctl(ctx, &ecmd);
+}
+
+static int do_gset(struct cmd_context *ctx)
+{
+ int err;
+ struct ethtool_link_usettings *link_usettings;
+ struct ethtool_wolinfo wolinfo;
+ struct ethtool_value edata;
+ int allfail = 1;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ fprintf(stdout, "Settings for %s:\n", ctx->devname);
+
+ link_usettings = do_ioctl_glinksettings(ctx);
+ if (link_usettings == NULL)
+ link_usettings = do_ioctl_gset(ctx);
+ if (link_usettings != NULL) {
+ err = dump_link_usettings(link_usettings);
+ free(link_usettings);
+ if (err)
+ return err;
+ allfail = 0;
+ } else if (errno != EOPNOTSUPP) {
+ perror("Cannot get device settings");
+ }
+
+ wolinfo.cmd = ETHTOOL_GWOL;
+ err = send_ioctl(ctx, &wolinfo);
+ if (err == 0) {
+ err = dump_wol(&wolinfo);
+ if (err)
+ return err;
+ allfail = 0;
+ } else if (errno != EOPNOTSUPP) {
+ perror("Cannot get wake-on-lan settings");
+ }
+
+ edata.cmd = ETHTOOL_GMSGLVL;
+ err = send_ioctl(ctx, &edata);
+ if (err == 0) {
+ fprintf(stdout, " Current message level: 0x%08x (%d)\n"
+ " ",
+ edata.data, edata.data);
+ print_flags(flags_msglvl, n_flags_msglvl, edata.data);
+ fprintf(stdout, "\n");
+ allfail = 0;
+ } else if (errno != EOPNOTSUPP) {
+ perror("Cannot get message level");
+ }
+
+ edata.cmd = ETHTOOL_GLINK;
+ err = send_ioctl(ctx, &edata);
+ if (err == 0) {
+ fprintf(stdout, " Link detected: %s\n",
+ edata.data ? "yes":"no");
+ allfail = 0;
+ } else if (errno != EOPNOTSUPP) {
+ perror("Cannot get link status");
+ }
+
+ if (allfail) {
+ fprintf(stdout, "No data available\n");
+ return 75;
+ }
+ return 0;
+}
+
+static int do_sset(struct cmd_context *ctx)
+{
+ int speed_wanted = -1;
+ int duplex_wanted = -1;
+ int port_wanted = -1;
+ int mdix_wanted = -1;
+ int autoneg_wanted = -1;
+ int phyad_wanted = -1;
+ int xcvr_wanted = -1;
+ u32 *full_advertising_wanted = NULL;
+ u32 *advertising_wanted = NULL;
+ ETHTOOL_DECLARE_LINK_MODE_MASK(mask_full_advertising_wanted);
+ ETHTOOL_DECLARE_LINK_MODE_MASK(mask_advertising_wanted);
+ int gset_changed = 0; /* did anything in GSET change? */
+ u32 wol_wanted = 0;
+ int wol_change = 0;
+ u8 sopass_wanted[SOPASS_MAX];
+ int sopass_change = 0;
+ int gwol_changed = 0; /* did anything in GWOL change? */
+ int msglvl_changed = 0;
+ u32 msglvl_wanted = 0;
+ u32 msglvl_mask = 0;
+ struct cmdline_info cmdline_msglvl[n_flags_msglvl];
+ unsigned int argc = ctx->argc;
+ char **argp = ctx->argp;
+ unsigned int i;
+ int err = 0;
+
+ for (i = 0; i < n_flags_msglvl; i++)
+ flag_to_cmdline_info(flags_msglvl[i].name,
+ flags_msglvl[i].value,
+ &msglvl_wanted, &msglvl_mask,
+ &cmdline_msglvl[i]);
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argp[i], "speed")) {
+ gset_changed = 1;
+ i += 1;
+ if (i >= argc)
+ exit_bad_args();
+ speed_wanted = get_int(argp[i], 10);
+ } else if (!strcmp(argp[i], "duplex")) {
+ gset_changed = 1;
+ i += 1;
+ if (i >= argc)
+ exit_bad_args();
+ if (!strcmp(argp[i], "half"))
+ duplex_wanted = DUPLEX_HALF;
+ else if (!strcmp(argp[i], "full"))
+ duplex_wanted = DUPLEX_FULL;
+ else
+ exit_bad_args();
+ } else if (!strcmp(argp[i], "port")) {
+ gset_changed = 1;
+ i += 1;
+ if (i >= argc)
+ exit_bad_args();
+ if (!strcmp(argp[i], "tp"))
+ port_wanted = PORT_TP;
+ else if (!strcmp(argp[i], "aui"))
+ port_wanted = PORT_AUI;
+ else if (!strcmp(argp[i], "bnc"))
+ port_wanted = PORT_BNC;
+ else if (!strcmp(argp[i], "mii"))
+ port_wanted = PORT_MII;
+ else if (!strcmp(argp[i], "fibre"))
+ port_wanted = PORT_FIBRE;
+ else
+ exit_bad_args();
+ } else if (!strcmp(argp[i], "mdix")) {
+ gset_changed = 1;
+ i += 1;
+ if (i >= argc)
+ exit_bad_args();
+ if (!strcmp(argp[i], "auto"))
+ mdix_wanted = ETH_TP_MDI_AUTO;
+ else if (!strcmp(argp[i], "on"))
+ mdix_wanted = ETH_TP_MDI_X;
+ else if (!strcmp(argp[i], "off"))
+ mdix_wanted = ETH_TP_MDI;
+ else
+ exit_bad_args();
+ } else if (!strcmp(argp[i], "autoneg")) {
+ i += 1;
+ if (i >= argc)
+ exit_bad_args();
+ if (!strcmp(argp[i], "on")) {
+ gset_changed = 1;
+ autoneg_wanted = AUTONEG_ENABLE;
+ } else if (!strcmp(argp[i], "off")) {
+ gset_changed = 1;
+ autoneg_wanted = AUTONEG_DISABLE;
+ } else {
+ exit_bad_args();
+ }
+ } else if (!strcmp(argp[i], "advertise")) {
+ gset_changed = 1;
+ i += 1;
+ if (i >= argc)
+ exit_bad_args();
+ if (parse_hex_u32_bitmap(
+ argp[i],
+ ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS,
+ mask_full_advertising_wanted))
+ exit_bad_args();
+ full_advertising_wanted = mask_full_advertising_wanted;
+ } else if (!strcmp(argp[i], "phyad")) {
+ gset_changed = 1;
+ i += 1;
+ if (i >= argc)
+ exit_bad_args();
+ phyad_wanted = get_int(argp[i], 0);
+ } else if (!strcmp(argp[i], "xcvr")) {
+ gset_changed = 1;
+ i += 1;
+ if (i >= argc)
+ exit_bad_args();
+ if (!strcmp(argp[i], "internal"))
+ xcvr_wanted = XCVR_INTERNAL;
+ else if (!strcmp(argp[i], "external"))
+ xcvr_wanted = XCVR_EXTERNAL;
+ else
+ exit_bad_args();
+ } else if (!strcmp(argp[i], "wol")) {
+ gwol_changed = 1;
+ i++;
+ if (i >= argc)
+ exit_bad_args();
+ if (parse_wolopts(argp[i], &wol_wanted) < 0)
+ exit_bad_args();
+ wol_change = 1;
+ } else if (!strcmp(argp[i], "sopass")) {
+ gwol_changed = 1;
+ i++;
+ if (i >= argc)
+ exit_bad_args();
+ get_mac_addr(argp[i], sopass_wanted);
+ sopass_change = 1;
+ } else if (!strcmp(argp[i], "msglvl")) {
+ i++;
+ if (i >= argc)
+ exit_bad_args();
+ if (isdigit((unsigned char)argp[i][0])) {
+ msglvl_changed = 1;
+ msglvl_mask = ~0;
+ msglvl_wanted =
+ get_uint_range(argp[i], 0,
+ 0xffffffff);
+ } else {
+ ctx->argc -= i;
+ ctx->argp += i;
+ parse_generic_cmdline(
+ ctx, &msglvl_changed,
+ cmdline_msglvl,
+ ARRAY_SIZE(cmdline_msglvl));
+ break;
+ }
+ } else if (!strcmp(argp[i], "master-slave")) {
+ exit_nlonly_param(argp[i]);
+ } else {
+ exit_bad_args();
+ }
+ }
+
+ if (full_advertising_wanted == NULL) {
+ /* User didn't supply a full advertisement bitfield:
+ * construct one from the specified speed and duplex.
+ */
+ int adv_bit = -1;
+
+ if (speed_wanted == SPEED_10 && duplex_wanted == DUPLEX_HALF)
+ adv_bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT;
+ else if (speed_wanted == SPEED_10 &&
+ duplex_wanted == DUPLEX_FULL)
+ adv_bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT;
+ else if (speed_wanted == SPEED_100 &&
+ duplex_wanted == DUPLEX_HALF)
+ adv_bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT;
+ else if (speed_wanted == SPEED_100 &&
+ duplex_wanted == DUPLEX_FULL)
+ adv_bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT;
+ else if (speed_wanted == SPEED_1000 &&
+ duplex_wanted == DUPLEX_HALF)
+ adv_bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT;
+ else if (speed_wanted == SPEED_1000 &&
+ duplex_wanted == DUPLEX_FULL)
+ adv_bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT;
+ else if (speed_wanted == SPEED_2500 &&
+ duplex_wanted == DUPLEX_FULL)
+ adv_bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT;
+ else if (speed_wanted == SPEED_10000 &&
+ duplex_wanted == DUPLEX_FULL)
+ adv_bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT;
+
+ if (adv_bit >= 0) {
+ advertising_wanted = mask_advertising_wanted;
+ ethtool_link_mode_zero(advertising_wanted);
+ ethtool_link_mode_set_bit(
+ adv_bit, advertising_wanted);
+ }
+ /* otherwise: auto negotiate without forcing,
+ * all supported speed will be assigned below
+ */
+ }
+
+ if (gset_changed) {
+ struct ethtool_link_usettings *link_usettings;
+
+ link_usettings = do_ioctl_glinksettings(ctx);
+ if (link_usettings == NULL)
+ link_usettings = do_ioctl_gset(ctx);
+ else
+ memset(&link_usettings->deprecated, 0,
+ sizeof(link_usettings->deprecated));
+ if (link_usettings == NULL) {
+ perror("Cannot get current device settings");
+ err = -1;
+ } else {
+ /* Change everything the user specified. */
+ if (speed_wanted != -1)
+ link_usettings->base.speed = speed_wanted;
+ if (duplex_wanted != -1)
+ link_usettings->base.duplex = duplex_wanted;
+ if (port_wanted != -1)
+ link_usettings->base.port = port_wanted;
+ if (mdix_wanted != -1) {
+ /* check driver supports MDI-X */
+ if (link_usettings->base.eth_tp_mdix_ctrl
+ != ETH_TP_MDI_INVALID)
+ link_usettings->base.eth_tp_mdix_ctrl
+ = mdix_wanted;
+ else
+ fprintf(stderr,
+ "setting MDI not supported\n");
+ }
+ if (autoneg_wanted != -1)
+ link_usettings->base.autoneg = autoneg_wanted;
+ if (phyad_wanted != -1)
+ link_usettings->base.phy_address = phyad_wanted;
+ if (xcvr_wanted != -1)
+ link_usettings->deprecated.transceiver
+ = xcvr_wanted;
+ /* XXX If the user specified speed or duplex
+ * then we should mask the advertised modes
+ * accordingly. For now, warn that we aren't
+ * doing that.
+ */
+ if ((speed_wanted != -1 || duplex_wanted != -1) &&
+ link_usettings->base.autoneg &&
+ advertising_wanted == NULL) {
+ fprintf(stderr, "Cannot advertise");
+ if (speed_wanted >= 0)
+ fprintf(stderr, " speed %d",
+ speed_wanted);
+ if (duplex_wanted >= 0)
+ fprintf(stderr, " duplex %s",
+ duplex_wanted ?
+ "full" : "half");
+ fprintf(stderr, "\n");
+ }
+ if (autoneg_wanted == AUTONEG_ENABLE &&
+ advertising_wanted == NULL &&
+ full_advertising_wanted == NULL) {
+ unsigned int i;
+
+ /* Auto negotiation enabled, but with
+ * unspecified speed and duplex: enable all
+ * supported speeds and duplexes.
+ */
+ ethtool_link_mode_for_each_u32(i) {
+ u32 sup = link_usettings->link_modes.supported[i];
+ u32 *adv = link_usettings->link_modes.advertising + i;
+
+ *adv = ((*adv & ~all_advertised_modes[i])
+ | (sup & all_advertised_modes[i]));
+ }
+
+ /* If driver supports unknown flags, we cannot
+ * be sure that we enable all link modes.
+ */
+ ethtool_link_mode_for_each_u32(i) {
+ u32 sup = link_usettings->link_modes.supported[i];
+
+ if ((sup & all_advertised_flags[i]) != sup) {
+ fprintf(stderr, "Driver supports one or more unknown flags\n");
+ break;
+ }
+ }
+ } else if (advertising_wanted != NULL) {
+ unsigned int i;
+
+ /* Enable all requested modes */
+ ethtool_link_mode_for_each_u32(i) {
+ u32 *adv = link_usettings->link_modes.advertising + i;
+
+ *adv = ((*adv & ~all_advertised_modes[i])
+ | advertising_wanted[i]);
+ }
+ } else if (full_advertising_wanted != NULL) {
+ ethtool_link_mode_copy(
+ link_usettings->link_modes.advertising,
+ full_advertising_wanted);
+ }
+
+ /* Try to perform the update. */
+ if (link_usettings->base.cmd == ETHTOOL_GLINKSETTINGS)
+ err = do_ioctl_slinksettings(ctx,
+ link_usettings);
+ else
+ err = do_ioctl_sset(ctx, link_usettings);
+ free(link_usettings);
+ if (err < 0)
+ perror("Cannot set new settings");
+ }
+ if (err < 0) {
+ if (speed_wanted != -1)
+ fprintf(stderr, " not setting speed\n");
+ if (duplex_wanted != -1)
+ fprintf(stderr, " not setting duplex\n");
+ if (port_wanted != -1)
+ fprintf(stderr, " not setting port\n");
+ if (autoneg_wanted != -1)
+ fprintf(stderr, " not setting autoneg\n");
+ if (phyad_wanted != -1)
+ fprintf(stderr, " not setting phy_address\n");
+ if (xcvr_wanted != -1)
+ fprintf(stderr, " not setting transceiver\n");
+ if (mdix_wanted != -1)
+ fprintf(stderr, " not setting mdix\n");
+ }
+ }
+
+ if (gwol_changed) {
+ struct ethtool_wolinfo wol;
+
+ wol.cmd = ETHTOOL_GWOL;
+ err = send_ioctl(ctx, &wol);
+ if (err < 0) {
+ perror("Cannot get current wake-on-lan settings");
+ } else {
+ /* Change everything the user specified. */
+ if (wol_change)
+ wol.wolopts = wol_wanted;
+ if (sopass_change) {
+ int i;
+ for (i = 0; i < SOPASS_MAX; i++)
+ wol.sopass[i] = sopass_wanted[i];
+ }
+
+ /* Try to perform the update. */
+ wol.cmd = ETHTOOL_SWOL;
+ err = send_ioctl(ctx, &wol);
+ if (err < 0)
+ perror("Cannot set new wake-on-lan settings");
+ }
+ if (err < 0) {
+ if (wol_change)
+ fprintf(stderr, " not setting wol\n");
+ if (sopass_change)
+ fprintf(stderr, " not setting sopass\n");
+ }
+ }
+
+ if (msglvl_changed) {
+ struct ethtool_value edata;
+
+ edata.cmd = ETHTOOL_GMSGLVL;
+ err = send_ioctl(ctx, &edata);
+ if (err < 0) {
+ perror("Cannot get msglvl");
+ } else {
+ edata.cmd = ETHTOOL_SMSGLVL;
+ edata.data = ((edata.data & ~msglvl_mask) |
+ msglvl_wanted);
+ err = send_ioctl(ctx, &edata);
+ if (err < 0)
+ perror("Cannot set new msglvl");
+ }
+ }
+
+ return 0;
+}
+
+static int do_gregs(struct cmd_context *ctx)
+{
+ int gregs_changed = 0;
+ int gregs_dump_raw = 0;
+ int gregs_dump_hex = 0;
+ char *gregs_dump_file = NULL;
+ struct cmdline_info cmdline_gregs[] = {
+ {
+ .name = "raw",
+ .type = CMDL_BOOL,
+ .wanted_val = &gregs_dump_raw,
+ },
+ {
+ .name = "hex",
+ .type = CMDL_BOOL,
+ .wanted_val = &gregs_dump_hex,
+ },
+ {
+ .name = "file",
+ .type = CMDL_STR,
+ .wanted_val = &gregs_dump_file,
+ },
+ };
+ int err;
+ struct ethtool_drvinfo drvinfo;
+ struct ethtool_regs *regs;
+
+ parse_generic_cmdline(ctx, &gregs_changed,
+ cmdline_gregs, ARRAY_SIZE(cmdline_gregs));
+
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ err = send_ioctl(ctx, &drvinfo);
+ if (err < 0) {
+ perror("Cannot get driver information");
+ return 72;
+ }
+
+ regs = calloc(1, sizeof(*regs)+drvinfo.regdump_len);
+ if (!regs) {
+ perror("Cannot allocate memory for register dump");
+ return 73;
+ }
+ regs->cmd = ETHTOOL_GREGS;
+ regs->len = drvinfo.regdump_len;
+ err = send_ioctl(ctx, regs);
+ if (err < 0) {
+ perror("Cannot get register dump");
+ free(regs);
+ return 74;
+ }
+
+ if (!gregs_dump_raw && gregs_dump_file != NULL) {
+ /* overwrite reg values from file dump */
+ FILE *f = fopen(gregs_dump_file, "r");
+ struct ethtool_regs *nregs;
+ struct stat st;
+ size_t nread;
+
+ if (!f || fstat(fileno(f), &st) < 0) {
+ fprintf(stderr, "Can't open '%s': %s\n",
+ gregs_dump_file, strerror(errno));
+ if (f)
+ fclose(f);
+ free(regs);
+ return 75;
+ }
+
+ nregs = realloc(regs, sizeof(*regs) + st.st_size);
+ if (!nregs) {
+ perror("Cannot allocate memory for register dump");
+ free(regs); /* was not freed by realloc */
+ return 73;
+ }
+ regs = nregs;
+ regs->len = st.st_size;
+ nread = fread(regs->data, regs->len, 1, f);
+ fclose(f);
+ if (nread != 1) {
+ free(regs);
+ return 75;
+ }
+ }
+
+ if (dump_regs(gregs_dump_raw, gregs_dump_hex,
+ &drvinfo, regs) < 0) {
+ fprintf(stderr, "Cannot dump registers\n");
+ free(regs);
+ return 75;
+ }
+ free(regs);
+
+ return 0;
+}
+
+static int do_nway_rst(struct cmd_context *ctx)
+{
+ struct ethtool_value edata;
+ int err;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ edata.cmd = ETHTOOL_NWAY_RST;
+ err = send_ioctl(ctx, &edata);
+ if (err < 0)
+ perror("Cannot restart autonegotiation");
+
+ return err;
+}
+
+static int do_geeprom(struct cmd_context *ctx)
+{
+ int geeprom_changed = 0;
+ int geeprom_dump_raw = 0;
+ u32 geeprom_offset = 0;
+ u32 geeprom_length = 0;
+ int geeprom_length_seen = 0;
+ struct cmdline_info cmdline_geeprom[] = {
+ {
+ .name = "offset",
+ .type = CMDL_U32,
+ .wanted_val = &geeprom_offset,
+ },
+ {
+ .name = "length",
+ .type = CMDL_U32,
+ .wanted_val = &geeprom_length,
+ .seen_val = &geeprom_length_seen,
+ },
+ {
+ .name = "raw",
+ .type = CMDL_BOOL,
+ .wanted_val = &geeprom_dump_raw,
+ },
+ };
+ int err;
+ struct ethtool_drvinfo drvinfo;
+ struct ethtool_eeprom *eeprom;
+
+ parse_generic_cmdline(ctx, &geeprom_changed,
+ cmdline_geeprom, ARRAY_SIZE(cmdline_geeprom));
+
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ err = send_ioctl(ctx, &drvinfo);
+ if (err < 0) {
+ perror("Cannot get driver information");
+ return 74;
+ }
+
+ if (!geeprom_length_seen)
+ geeprom_length = drvinfo.eedump_len;
+
+ if (drvinfo.eedump_len < geeprom_offset + geeprom_length)
+ geeprom_length = drvinfo.eedump_len - geeprom_offset;
+
+ eeprom = calloc(1, sizeof(*eeprom)+geeprom_length);
+ if (!eeprom) {
+ perror("Cannot allocate memory for EEPROM data");
+ return 75;
+ }
+ eeprom->cmd = ETHTOOL_GEEPROM;
+ eeprom->len = geeprom_length;
+ eeprom->offset = geeprom_offset;
+ err = send_ioctl(ctx, eeprom);
+ if (err < 0) {
+ perror("Cannot get EEPROM data");
+ free(eeprom);
+ return 74;
+ }
+ err = dump_eeprom(geeprom_dump_raw, &drvinfo, eeprom);
+ free(eeprom);
+
+ return err;
+}
+
+static int do_seeprom(struct cmd_context *ctx)
+{
+ int seeprom_changed = 0;
+ u32 seeprom_magic = 0;
+ u32 seeprom_length = 0;
+ u32 seeprom_offset = 0;
+ u8 seeprom_value = 0;
+ int seeprom_length_seen = 0;
+ int seeprom_value_seen = 0;
+ struct cmdline_info cmdline_seeprom[] = {
+ {
+ .name = "magic",
+ .type = CMDL_U32,
+ .wanted_val = &seeprom_magic,
+ },
+ {
+ .name = "offset",
+ .type = CMDL_U32,
+ .wanted_val = &seeprom_offset,
+ },
+ {
+ .name = "length",
+ .type = CMDL_U32,
+ .wanted_val = &seeprom_length,
+ .seen_val = &seeprom_length_seen,
+ },
+ {
+ .name = "value",
+ .type = CMDL_U8,
+ .wanted_val = &seeprom_value,
+ .seen_val = &seeprom_value_seen,
+ },
+ };
+ int err;
+ struct ethtool_drvinfo drvinfo;
+ struct ethtool_eeprom *eeprom;
+
+ parse_generic_cmdline(ctx, &seeprom_changed,
+ cmdline_seeprom, ARRAY_SIZE(cmdline_seeprom));
+
+ drvinfo.cmd = ETHTOOL_GDRVINFO;
+ err = send_ioctl(ctx, &drvinfo);
+ if (err < 0) {
+ perror("Cannot get driver information");
+ return 74;
+ }
+
+ if (seeprom_value_seen && !seeprom_length_seen)
+ seeprom_length = 1;
+ else if (!seeprom_length_seen)
+ seeprom_length = drvinfo.eedump_len;
+
+ if (seeprom_value_seen && (seeprom_length != 1)) {
+ fprintf(stderr, "value requires length 1\n");
+ return 1;
+ }
+
+ if (drvinfo.eedump_len < seeprom_offset + seeprom_length) {
+ fprintf(stderr, "offset & length out of bounds\n");
+ return 1;
+ }
+
+ eeprom = calloc(1, sizeof(*eeprom)+seeprom_length);
+ if (!eeprom) {
+ perror("Cannot allocate memory for EEPROM data");
+ return 75;
+ }
+
+ eeprom->cmd = ETHTOOL_SEEPROM;
+ eeprom->len = seeprom_length;
+ eeprom->offset = seeprom_offset;
+ eeprom->magic = seeprom_magic;
+ eeprom->data[0] = seeprom_value;
+
+ /* Multi-byte write: read input from stdin */
+ if (!seeprom_value_seen) {
+ if (fread(eeprom->data, eeprom->len, 1, stdin) != 1) {
+ fprintf(stderr, "not enough data from stdin\n");
+ free(eeprom);
+ return 75;
+ }
+ if ((fgetc(stdin) != EOF) || !feof(stdin)) {
+ fprintf(stderr, "too much data from stdin\n");
+ free(eeprom);
+ return 75;
+ }
+ }
+
+ err = send_ioctl(ctx, eeprom);
+ if (err < 0) {
+ perror("Cannot set EEPROM data");
+ err = 87;
+ }
+ free(eeprom);
+
+ return err;
+}
+
+static int do_test(struct cmd_context *ctx)
+{
+ enum {
+ ONLINE = 0,
+ OFFLINE,
+ EXTERNAL_LB,
+ } test_type;
+ int err;
+ struct ethtool_test *test;
+ struct ethtool_gstrings *strings;
+
+ if (ctx->argc > 1)
+ exit_bad_args();
+ if (ctx->argc == 1) {
+ if (!strcmp(ctx->argp[0], "online"))
+ test_type = ONLINE;
+ else if (!strcmp(*ctx->argp, "offline"))
+ test_type = OFFLINE;
+ else if (!strcmp(*ctx->argp, "external_lb"))
+ test_type = EXTERNAL_LB;
+ else
+ exit_bad_args();
+ } else {
+ test_type = OFFLINE;
+ }
+
+ strings = get_stringset(ctx, ETH_SS_TEST,
+ offsetof(struct ethtool_drvinfo, testinfo_len),
+ 1);
+ if (!strings) {
+ perror("Cannot get strings");
+ return 74;
+ }
+
+ test = calloc(1, sizeof(*test) + strings->len * sizeof(u64));
+ if (!test) {
+ perror("Cannot allocate memory for test info");
+ free(strings);
+ return 73;
+ }
+ memset(test->data, 0, strings->len * sizeof(u64));
+ test->cmd = ETHTOOL_TEST;
+ test->len = strings->len;
+ if (test_type == EXTERNAL_LB)
+ test->flags = (ETH_TEST_FL_OFFLINE | ETH_TEST_FL_EXTERNAL_LB);
+ else if (test_type == OFFLINE)
+ test->flags = ETH_TEST_FL_OFFLINE;
+ else
+ test->flags = 0;
+ err = send_ioctl(ctx, test);
+ if (err < 0) {
+ perror("Cannot test");
+ free(test);
+ free(strings);
+ return 74;
+ }
+
+ err = dump_test(test, strings);
+ free(test);
+ free(strings);
+
+ return err;
+}
+
+static int do_phys_id(struct cmd_context *ctx)
+{
+ int err;
+ struct ethtool_value edata;
+ int phys_id_time;
+
+ if (ctx->argc > 1)
+ exit_bad_args();
+ if (ctx->argc == 1)
+ phys_id_time = get_int(*ctx->argp, 0);
+ else
+ phys_id_time = 0;
+
+ edata.cmd = ETHTOOL_PHYS_ID;
+ edata.data = phys_id_time;
+ err = send_ioctl(ctx, &edata);
+ if (err < 0)
+ perror("Cannot identify NIC");
+
+ return err;
+}
+
+static int do_gstats(struct cmd_context *ctx, int cmd, int stringset,
+ const char *name)
+{
+ struct ethtool_gstrings *strings;
+ struct ethtool_stats *stats;
+ unsigned int n_stats, sz_stats, i;
+ int err;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ strings = get_stringset(ctx, stringset,
+ offsetof(struct ethtool_drvinfo, n_stats),
+ 0);
+ if (!strings) {
+ perror("Cannot get stats strings information");
+ return 96;
+ }
+
+ n_stats = strings->len;
+ if (n_stats < 1) {
+ fprintf(stderr, "no stats available\n");
+ free(strings);
+ return 94;
+ }
+
+ sz_stats = n_stats * sizeof(u64);
+
+ stats = calloc(1, sz_stats + sizeof(struct ethtool_stats));
+ if (!stats) {
+ fprintf(stderr, "no memory available\n");
+ free(strings);
+ return 95;
+ }
+
+ stats->cmd = cmd;
+ stats->n_stats = n_stats;
+ err = send_ioctl(ctx, stats);
+ if (err < 0) {
+ perror("Cannot get stats information");
+ free(strings);
+ free(stats);
+ return 97;
+ }
+
+ /* todo - pretty-print the strings per-driver */
+ fprintf(stdout, "%s statistics:\n", name);
+ for (i = 0; i < n_stats; i++) {
+ fprintf(stdout, " %.*s: %llu\n",
+ ETH_GSTRING_LEN,
+ &strings->data[i * ETH_GSTRING_LEN],
+ stats->data[i]);
+ }
+ free(strings);
+ free(stats);
+
+ return 0;
+}
+
+static int do_gnicstats(struct cmd_context *ctx)
+{
+ return do_gstats(ctx, ETHTOOL_GSTATS, ETH_SS_STATS, "NIC");
+}
+
+static int do_gphystats(struct cmd_context *ctx)
+{
+ return do_gstats(ctx, ETHTOOL_GPHYSTATS, ETH_SS_PHY_STATS, "PHY");
+}
+
+static int do_srxntuple(struct cmd_context *ctx,
+ struct ethtool_rx_flow_spec *rx_rule_fs);
+
+static int do_srxclass(struct cmd_context *ctx)
+{
+ int err;
+
+ if (ctx->argc < 2)
+ exit_bad_args();
+
+ if (!strcmp(ctx->argp[0], "rx-flow-hash")) {
+ int rx_fhash_set;
+ u32 rx_fhash_val;
+ struct ethtool_rxnfc nfccmd;
+ bool flow_rss = false;
+
+ if (ctx->argc == 5) {
+ if (strcmp(ctx->argp[3], "context"))
+ exit_bad_args();
+ flow_rss = true;
+ nfccmd.rss_context = get_u32(ctx->argp[4], 0);
+ } else if (ctx->argc != 3) {
+ exit_bad_args();
+ }
+ rx_fhash_set = rxflow_str_to_type(ctx->argp[1]);
+ if (!rx_fhash_set)
+ exit_bad_args();
+ if (parse_rxfhashopts(ctx->argp[2], &rx_fhash_val) < 0)
+ exit_bad_args();
+
+ nfccmd.cmd = ETHTOOL_SRXFH;
+ nfccmd.flow_type = rx_fhash_set;
+ nfccmd.data = rx_fhash_val;
+ if (flow_rss)
+ nfccmd.flow_type |= FLOW_RSS;
+
+ err = send_ioctl(ctx, &nfccmd);
+ if (err < 0)
+ perror("Cannot change RX network flow hashing options");
+ } else if (!strcmp(ctx->argp[0], "flow-type")) {
+ struct ethtool_rx_flow_spec rx_rule_fs;
+ __u32 rss_context = 0;
+
+ ctx->argc--;
+ ctx->argp++;
+ if (rxclass_parse_ruleopts(ctx, &rx_rule_fs, &rss_context) < 0)
+ exit_bad_args();
+
+ /* attempt to add rule via N-tuple specifier */
+ err = do_srxntuple(ctx, &rx_rule_fs);
+ if (!err)
+ return 0;
+
+ /* attempt to add rule via network flow classifier */
+ err = rxclass_rule_ins(ctx, &rx_rule_fs, rss_context);
+ if (err < 0) {
+ fprintf(stderr, "Cannot insert"
+ " classification rule\n");
+ return 1;
+ }
+ } else if (!strcmp(ctx->argp[0], "delete")) {
+ int rx_class_rule_del =
+ get_uint_range(ctx->argp[1], 0, INT_MAX);
+
+ err = rxclass_rule_del(ctx, rx_class_rule_del);
+
+ if (err < 0) {
+ fprintf(stderr, "Cannot delete"
+ " classification rule\n");
+ return 1;
+ }
+ } else {
+ exit_bad_args();
+ }
+
+ return 0;
+}
+
+static int do_grxclass(struct cmd_context *ctx)
+{
+ struct ethtool_rxnfc nfccmd;
+ int err;
+
+ if (ctx->argc > 0 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
+ int rx_fhash_get;
+ bool flow_rss = false;
+
+ if (ctx->argc == 4) {
+ if (strcmp(ctx->argp[2], "context"))
+ exit_bad_args();
+ flow_rss = true;
+ nfccmd.rss_context = get_u32(ctx->argp[3], 0);
+ } else if (ctx->argc != 2) {
+ exit_bad_args();
+ }
+
+ rx_fhash_get = rxflow_str_to_type(ctx->argp[1]);
+ if (!rx_fhash_get)
+ exit_bad_args();
+
+ nfccmd.cmd = ETHTOOL_GRXFH;
+ nfccmd.flow_type = rx_fhash_get;
+ if (flow_rss)
+ nfccmd.flow_type |= FLOW_RSS;
+ err = send_ioctl(ctx, &nfccmd);
+ if (err < 0) {
+ perror("Cannot get RX network flow hashing options");
+ } else {
+ if (flow_rss)
+ fprintf(stdout, "For RSS context %u:\n",
+ nfccmd.rss_context);
+ dump_rxfhash(rx_fhash_get, nfccmd.data);
+ }
+ } else if (ctx->argc == 2 && !strcmp(ctx->argp[0], "rule")) {
+ int rx_class_rule_get =
+ get_uint_range(ctx->argp[1], 0, INT_MAX);
+
+ err = rxclass_rule_get(ctx, rx_class_rule_get);
+ if (err < 0)
+ fprintf(stderr, "Cannot get RX classification rule\n");
+ } else if (ctx->argc == 0) {
+ nfccmd.cmd = ETHTOOL_GRXRINGS;
+ err = send_ioctl(ctx, &nfccmd);
+ if (err < 0)
+ perror("Cannot get RX rings");
+ else
+ fprintf(stdout, "%d RX rings available\n",
+ (int)nfccmd.data);
+
+ err = rxclass_rule_getall(ctx);
+ if (err < 0)
+ fprintf(stderr, "RX classification rule retrieval failed\n");
+
+ } else {
+ exit_bad_args();
+ }
+
+ return err ? 1 : 0;
+}
+
+static int do_grxfhindir(struct cmd_context *ctx,
+ struct ethtool_rxnfc *ring_count)
+{
+ struct ethtool_rxfh_indir indir_head;
+ struct ethtool_rxfh_indir *indir;
+ int err;
+
+ indir_head.cmd = ETHTOOL_GRXFHINDIR;
+ indir_head.size = 0;
+ err = send_ioctl(ctx, &indir_head);
+ if (err < 0) {
+ perror("Cannot get RX flow hash indirection table size");
+ return 1;
+ }
+
+ indir = malloc(sizeof(*indir) +
+ indir_head.size * sizeof(*indir->ring_index));
+ if (!indir) {
+ perror("Cannot allocate memory for indirection table");
+ return 1;
+ }
+
+ indir->cmd = ETHTOOL_GRXFHINDIR;
+ indir->size = indir_head.size;
+ err = send_ioctl(ctx, indir);
+ if (err < 0) {
+ perror("Cannot get RX flow hash indirection table");
+ free(indir);
+ return 1;
+ }
+
+ print_indir_table(ctx, ring_count->data, indir->size,
+ indir->ring_index);
+
+ free(indir);
+ return 0;
+}
+
+static int do_grxfh(struct cmd_context *ctx)
+{
+ struct ethtool_gstrings *hfuncs = NULL;
+ struct ethtool_rxfh rss_head = {0};
+ struct ethtool_rxnfc ring_count;
+ struct ethtool_rxfh *rss;
+ u32 rss_context = 0;
+ u32 i, indir_bytes;
+ unsigned int arg_num = 0;
+ u8 *hkey;
+ int err;
+
+ while (arg_num < ctx->argc) {
+ if (!strcmp(ctx->argp[arg_num], "context")) {
+ ++arg_num;
+ rss_context = get_int_range(ctx->argp[arg_num], 0, 1,
+ ETH_RXFH_CONTEXT_ALLOC - 1);
+ ++arg_num;
+ } else {
+ exit_bad_args();
+ }
+ }
+
+ ring_count.cmd = ETHTOOL_GRXRINGS;
+ err = send_ioctl(ctx, &ring_count);
+ if (err < 0) {
+ perror("Cannot get RX ring count");
+ return 1;
+ }
+
+ rss_head.cmd = ETHTOOL_GRSSH;
+ rss_head.rss_context = rss_context;
+ err = send_ioctl(ctx, &rss_head);
+ if (err < 0 && errno == EOPNOTSUPP && !rss_context) {
+ return do_grxfhindir(ctx, &ring_count);
+ } else if (err < 0) {
+ perror("Cannot get RX flow hash indir size and/or key size");
+ return 1;
+ }
+
+ rss = calloc(1, sizeof(*rss) +
+ rss_head.indir_size * sizeof(rss_head.rss_config[0]) +
+ rss_head.key_size);
+ if (!rss) {
+ perror("Cannot allocate memory for RX flow hash config");
+ return 1;
+ }
+
+ rss->cmd = ETHTOOL_GRSSH;
+ rss->rss_context = rss_context;
+ rss->indir_size = rss_head.indir_size;
+ rss->key_size = rss_head.key_size;
+ err = send_ioctl(ctx, rss);
+ if (err < 0) {
+ perror("Cannot get RX flow hash configuration");
+ free(rss);
+ return 1;
+ }
+
+ print_indir_table(ctx, ring_count.data, rss->indir_size,
+ rss->rss_config);
+
+ indir_bytes = rss->indir_size * sizeof(rss->rss_config[0]);
+ hkey = ((u8 *)rss->rss_config + indir_bytes);
+
+ print_rss_hkey(hkey, rss->key_size);
+
+ printf("RSS hash function:\n");
+ if (!rss->hfunc) {
+ printf(" Operation not supported\n");
+ goto out;
+ }
+
+ hfuncs = get_stringset(ctx, ETH_SS_RSS_HASH_FUNCS, 0, 1);
+ if (!hfuncs) {
+ perror("Cannot get hash functions names");
+ free(rss);
+ return 1;
+ }
+
+ for (i = 0; i < hfuncs->len; i++)
+ printf(" %s: %s\n",
+ (const char *)hfuncs->data + i * ETH_GSTRING_LEN,
+ (rss->hfunc & (1 << i)) ? "on" : "off");
+
+out:
+ free(hfuncs);
+ free(rss);
+ return 0;
+}
+
+static int fill_indir_table(u32 *indir_size, u32 *indir, int rxfhindir_default,
+ int rxfhindir_start, int rxfhindir_equal,
+ char **rxfhindir_weight, u32 num_weights)
+{
+ u32 i;
+
+ if (rxfhindir_equal) {
+ for (i = 0; i < *indir_size; i++)
+ indir[i] = rxfhindir_start + (i % rxfhindir_equal);
+ } else if (rxfhindir_weight) {
+ u32 j, weight, sum = 0, partial = 0;
+
+ for (j = 0; j < num_weights; j++) {
+ weight = get_u32(rxfhindir_weight[j], 0);
+ sum += weight;
+ }
+
+ if (sum == 0) {
+ fprintf(stderr,
+ "At least one weight must be non-zero\n");
+ return 2;
+ }
+
+ if (sum > *indir_size) {
+ fprintf(stderr,
+ "Total weight exceeds the size of the "
+ "indirection table\n");
+ return 2;
+ }
+
+ j = -1;
+ for (i = 0; i < *indir_size; i++) {
+ while (i >= (*indir_size) * partial / sum) {
+ j += 1;
+ weight = get_u32(rxfhindir_weight[j], 0);
+ partial += weight;
+ }
+ indir[i] = rxfhindir_start + j;
+ }
+ } else if (rxfhindir_default) {
+ /* "*indir_size == 0" ==> reset indir to default */
+ *indir_size = 0;
+ } else {
+ *indir_size = ETH_RXFH_INDIR_NO_CHANGE;
+ }
+
+ return 0;
+}
+
+static int do_srxfhindir(struct cmd_context *ctx, int rxfhindir_default,
+ int rxfhindir_start, int rxfhindir_equal,
+ char **rxfhindir_weight, u32 num_weights)
+{
+ struct ethtool_rxfh_indir indir_head;
+ struct ethtool_rxfh_indir *indir;
+ int err;
+
+ indir_head.cmd = ETHTOOL_GRXFHINDIR;
+ indir_head.size = 0;
+ err = send_ioctl(ctx, &indir_head);
+ if (err < 0) {
+ perror("Cannot get RX flow hash indirection table size");
+ return 1;
+ }
+
+ indir = malloc(sizeof(*indir) +
+ indir_head.size * sizeof(*indir->ring_index));
+
+ if (!indir) {
+ perror("Cannot allocate memory for indirection table");
+ return 1;
+ }
+
+ indir->cmd = ETHTOOL_SRXFHINDIR;
+ indir->size = indir_head.size;
+
+ if (fill_indir_table(&indir->size, indir->ring_index,
+ rxfhindir_default, rxfhindir_start,
+ rxfhindir_equal, rxfhindir_weight, num_weights)) {
+ free(indir);
+ return 1;
+ }
+
+ err = send_ioctl(ctx, indir);
+ if (err < 0) {
+ perror("Cannot set RX flow hash indirection table");
+ free(indir);
+ return 1;
+ }
+
+ free(indir);
+ return 0;
+}
+
+static int do_srxfh(struct cmd_context *ctx)
+{
+ struct ethtool_rxfh rss_head = {0};
+ struct ethtool_rxfh *rss = NULL;
+ struct ethtool_rxnfc ring_count;
+ int rxfhindir_equal = 0, rxfhindir_default = 0, rxfhindir_start = 0;
+ struct ethtool_gstrings *hfuncs = NULL;
+ char **rxfhindir_weight = NULL;
+ char *rxfhindir_key = NULL;
+ char *req_hfunc_name = NULL;
+ char *hfunc_name = NULL;
+ char *hkey = NULL;
+ int err = 0;
+ unsigned int i;
+ u32 arg_num = 0, indir_bytes = 0;
+ u32 req_hfunc = 0;
+ u32 entry_size = sizeof(rss_head.rss_config[0]);
+ u32 num_weights = 0;
+ u32 rss_context = 0;
+ int delete = 0;
+
+ if (ctx->argc < 1)
+ exit_bad_args();
+
+ while (arg_num < ctx->argc) {
+ if (!strcmp(ctx->argp[arg_num], "equal")) {
+ ++arg_num;
+ rxfhindir_equal = get_int_range(ctx->argp[arg_num],
+ 0, 1, INT_MAX);
+ ++arg_num;
+ } else if (!strcmp(ctx->argp[arg_num], "start")) {
+ ++arg_num;
+ rxfhindir_start = get_int_range(ctx->argp[arg_num],
+ 0, 0, INT_MAX);
+ ++arg_num;
+ } else if (!strcmp(ctx->argp[arg_num], "weight")) {
+ ++arg_num;
+ rxfhindir_weight = ctx->argp + arg_num;
+ while (arg_num < ctx->argc &&
+ isdigit((unsigned char)ctx->argp[arg_num][0])) {
+ ++arg_num;
+ ++num_weights;
+ }
+ if (!num_weights)
+ exit_bad_args();
+ } else if (!strcmp(ctx->argp[arg_num], "hkey")) {
+ ++arg_num;
+ rxfhindir_key = ctx->argp[arg_num];
+ if (!rxfhindir_key)
+ exit_bad_args();
+ ++arg_num;
+ } else if (!strcmp(ctx->argp[arg_num], "default")) {
+ ++arg_num;
+ rxfhindir_default = 1;
+ } else if (!strcmp(ctx->argp[arg_num], "hfunc")) {
+ ++arg_num;
+ req_hfunc_name = ctx->argp[arg_num];
+ if (!req_hfunc_name)
+ exit_bad_args();
+ ++arg_num;
+ } else if (!strcmp(ctx->argp[arg_num], "context")) {
+ ++arg_num;
+ if(!strcmp(ctx->argp[arg_num], "new"))
+ rss_context = ETH_RXFH_CONTEXT_ALLOC;
+ else
+ rss_context = get_int_range(
+ ctx->argp[arg_num], 0, 1,
+ ETH_RXFH_CONTEXT_ALLOC - 1);
+ ++arg_num;
+ } else if (!strcmp(ctx->argp[arg_num], "delete")) {
+ ++arg_num;
+ delete = 1;
+ } else {
+ exit_bad_args();
+ }
+ }
+
+ if (rxfhindir_equal && rxfhindir_weight) {
+ fprintf(stderr,
+ "Equal and weight options are mutually exclusive\n");
+ return 1;
+ }
+
+ if (rxfhindir_equal && rxfhindir_default) {
+ fprintf(stderr,
+ "Equal and default options are mutually exclusive\n");
+ return 1;
+ }
+
+ if (rxfhindir_weight && rxfhindir_default) {
+ fprintf(stderr,
+ "Weight and default options are mutually exclusive\n");
+ return 1;
+ }
+
+ if (rxfhindir_start && rxfhindir_default) {
+ fprintf(stderr,
+ "Start and default options are mutually exclusive\n");
+ return 1;
+ }
+
+ if (rxfhindir_start && !(rxfhindir_equal || rxfhindir_weight)) {
+ fprintf(stderr,
+ "Start must be used with equal or weight options\n");
+ return 1;
+ }
+
+ if (rxfhindir_default && rss_context) {
+ fprintf(stderr,
+ "Default and context options are mutually exclusive\n");
+ return 1;
+ }
+
+ if (delete && !rss_context) {
+ fprintf(stderr, "Delete option requires context option\n");
+ return 1;
+ }
+
+ if (delete && rxfhindir_weight) {
+ fprintf(stderr,
+ "Delete and weight options are mutually exclusive\n");
+ return 1;
+ }
+
+ if (delete && rxfhindir_equal) {
+ fprintf(stderr,
+ "Delete and equal options are mutually exclusive\n");
+ return 1;
+ }
+
+ if (delete && rxfhindir_default) {
+ fprintf(stderr,
+ "Delete and default options are mutually exclusive\n");
+ return 1;
+ }
+
+ if (delete && rxfhindir_key) {
+ fprintf(stderr,
+ "Delete and hkey options are mutually exclusive\n");
+ return 1;
+ }
+
+ ring_count.cmd = ETHTOOL_GRXRINGS;
+ err = send_ioctl(ctx, &ring_count);
+ if (err < 0) {
+ perror("Cannot get RX ring count");
+ return 1;
+ }
+
+ rss_head.cmd = ETHTOOL_GRSSH;
+ err = send_ioctl(ctx, &rss_head);
+ if (err < 0 && errno == EOPNOTSUPP && !rxfhindir_key &&
+ !req_hfunc_name && !rss_context) {
+ return do_srxfhindir(ctx, rxfhindir_default, rxfhindir_start,
+ rxfhindir_equal, rxfhindir_weight,
+ num_weights);
+ } else if (err < 0) {
+ perror("Cannot get RX flow hash indir size and key size");
+ return 1;
+ }
+
+ if (rxfhindir_key) {
+ err = parse_hkey(&hkey, rss_head.key_size,
+ rxfhindir_key);
+ if (err)
+ return err;
+ }
+
+ if (rxfhindir_equal || rxfhindir_weight)
+ indir_bytes = rss_head.indir_size * entry_size;
+
+ if (rss_head.hfunc && req_hfunc_name) {
+ hfuncs = get_stringset(ctx, ETH_SS_RSS_HASH_FUNCS, 0, 1);
+ if (!hfuncs) {
+ perror("Cannot get hash functions names");
+ err = 1;
+ goto free;
+ }
+
+ for (i = 0; i < hfuncs->len && !req_hfunc ; i++) {
+ hfunc_name = (char *)(hfuncs->data +
+ i * ETH_GSTRING_LEN);
+ if (!strncmp(hfunc_name, req_hfunc_name,
+ ETH_GSTRING_LEN))
+ req_hfunc = (u32)1 << i;
+ }
+
+ if (!req_hfunc) {
+ fprintf(stderr,
+ "Unknown hash function: %s\n", req_hfunc_name);
+ err = 1;
+ goto free;
+ }
+ }
+
+ rss = calloc(1, sizeof(*rss) + indir_bytes + rss_head.key_size);
+ if (!rss) {
+ perror("Cannot allocate memory for RX flow hash config");
+ err = 1;
+ goto free;
+ }
+ rss->cmd = ETHTOOL_SRSSH;
+ rss->rss_context = rss_context;
+ rss->hfunc = req_hfunc;
+ if (delete) {
+ rss->indir_size = rss->key_size = 0;
+ } else {
+ rss->indir_size = rss_head.indir_size;
+ rss->key_size = rss_head.key_size;
+ if (fill_indir_table(&rss->indir_size, rss->rss_config,
+ rxfhindir_default, rxfhindir_start,
+ rxfhindir_equal, rxfhindir_weight,
+ num_weights)) {
+ err = 1;
+ goto free;
+ }
+ }
+
+ if (hkey)
+ memcpy((char *)rss->rss_config + indir_bytes,
+ hkey, rss->key_size);
+ else
+ rss->key_size = 0;
+
+ err = send_ioctl(ctx, rss);
+ if (err < 0) {
+ perror("Cannot set RX flow hash configuration");
+ err = 1;
+ } else if (rss_context == ETH_RXFH_CONTEXT_ALLOC) {
+ printf("New RSS context is %d\n", rss->rss_context);
+ }
+
+free:
+ free(hkey);
+ free(rss);
+ free(hfuncs);
+ return err;
+}
+
+static int do_flash(struct cmd_context *ctx)
+{
+ char *flash_file;
+ int flash_region;
+ struct ethtool_flash efl;
+ int err;
+
+ if (ctx->argc < 1 || ctx->argc > 2)
+ exit_bad_args();
+ flash_file = ctx->argp[0];
+ if (ctx->argc == 2) {
+ flash_region = strtol(ctx->argp[1], NULL, 0);
+ if (flash_region < 0)
+ exit_bad_args();
+ } else {
+ flash_region = -1;
+ }
+
+ if (strlen(flash_file) > ETHTOOL_FLASH_MAX_FILENAME - 1) {
+ fprintf(stdout, "Filename too long\n");
+ return 99;
+ }
+
+ efl.cmd = ETHTOOL_FLASHDEV;
+ strcpy(efl.data, flash_file);
+
+ if (flash_region < 0)
+ efl.region = ETHTOOL_FLASH_ALL_REGIONS;
+ else
+ efl.region = flash_region;
+
+ err = send_ioctl(ctx, &efl);
+ if (err < 0)
+ perror("Flashing failed");
+
+ return err;
+}
+
+static int do_permaddr(struct cmd_context *ctx)
+{
+ unsigned int i;
+ int err;
+ struct ethtool_perm_addr *epaddr;
+
+ epaddr = malloc(sizeof(struct ethtool_perm_addr) + MAX_ADDR_LEN);
+ if (!epaddr) {
+ perror("Cannot allocate memory for operation");
+ return 1;
+ }
+
+ epaddr->cmd = ETHTOOL_GPERMADDR;
+ epaddr->size = MAX_ADDR_LEN;
+
+ err = send_ioctl(ctx, epaddr);
+ if (err < 0)
+ perror("Cannot read permanent address");
+ else {
+ printf("Permanent address:");
+ for (i = 0; i < epaddr->size; i++)
+ printf("%c%02x", (i == 0) ? ' ' : ':',
+ epaddr->data[i]);
+ printf("\n");
+ }
+ free(epaddr);
+
+ return err;
+}
+
+static bool flow_type_is_ntuple_supported(__u32 flow_type)
+{
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ case IPV4_USER_FLOW:
+ case ETHER_FLOW:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
+ struct ethtool_rx_ntuple_flow_spec *ntuple)
+{
+ size_t i;
+
+ /* verify location is not specified */
+ if (fsp->location != RX_CLS_LOC_ANY)
+ return -1;
+
+ /* destination MAC address in L3/L4 rules is not supported by ntuple */
+ if (fsp->flow_type & FLOW_MAC_EXT)
+ return -1;
+
+ /* verify ring cookie can transfer to action */
+ if (fsp->ring_cookie > INT_MAX && fsp->ring_cookie < (u64)(-2))
+ return -1;
+
+ /* verify only one field is setting data field */
+ if ((fsp->flow_type & FLOW_EXT) &&
+ (fsp->m_ext.data[0] || fsp->m_ext.data[1]) &&
+ fsp->m_ext.vlan_etype)
+ return -1;
+
+ /* IPv6 flow types are not supported by ntuple */
+ if (!flow_type_is_ntuple_supported(fsp->flow_type & ~FLOW_EXT))
+ return -1;
+
+ /* Set entire ntuple to ~0 to guarantee all masks are set */
+ memset(ntuple, ~0, sizeof(*ntuple));
+
+ /* set non-filter values */
+ ntuple->flow_type = fsp->flow_type;
+ ntuple->action = fsp->ring_cookie;
+
+ /*
+ * Copy over header union, they are identical in layout however
+ * the ntuple union contains additional padding on the end
+ */
+ memcpy(&ntuple->h_u, &fsp->h_u, sizeof(fsp->h_u));
+
+ /*
+ * The same rule mentioned above applies to the mask union. However,
+ * in addition we need to invert the mask bits to match the ntuple
+ * mask which is 1 for masked, versus 0 for masked as seen in nfc.
+ */
+ memcpy(&ntuple->m_u, &fsp->m_u, sizeof(fsp->m_u));
+ for (i = 0; i < sizeof(fsp->m_u); i++)
+ ntuple->m_u.hdata[i] ^= 0xFF;
+
+ /* copy extended fields */
+ if (fsp->flow_type & FLOW_EXT) {
+ ntuple->vlan_tag =
+ ntohs(fsp->h_ext.vlan_tci);
+ ntuple->vlan_tag_mask =
+ ~ntohs(fsp->m_ext.vlan_tci);
+ if (fsp->m_ext.vlan_etype) {
+ /*
+ * vlan_etype and user data are mutually exclusive
+ * in ntuple configuration as they occupy the same
+ * space.
+ */
+ if (fsp->m_ext.data[0] || fsp->m_ext.data[1])
+ return -1;
+ ntuple->data =
+ ntohl(fsp->h_ext.vlan_etype);
+ ntuple->data_mask =
+ ~(u64)ntohl(fsp->m_ext.vlan_etype);
+ } else {
+ ntuple->data =
+ (u64)ntohl(fsp->h_ext.data[0]) << 32;
+ ntuple->data |=
+ (u64)ntohl(fsp->h_ext.data[1]);
+ ntuple->data_mask =
+ (u64)ntohl(~fsp->m_ext.data[0]) << 32;
+ ntuple->data_mask |=
+ (u64)ntohl(~fsp->m_ext.data[1]);
+ }
+ }
+
+ /* Mask out the extended bit, because ntuple does not know it! */
+ ntuple->flow_type &= ~FLOW_EXT;
+
+ return 0;
+}
+
+static int do_srxntuple(struct cmd_context *ctx,
+ struct ethtool_rx_flow_spec *rx_rule_fs)
+{
+ struct ethtool_rx_ntuple ntuplecmd;
+ struct ethtool_value eval;
+ int err;
+
+ /* attempt to convert the flow classifier to an ntuple classifier */
+ err = flow_spec_to_ntuple(rx_rule_fs, &ntuplecmd.fs);
+ if (err)
+ return -1;
+
+ /*
+ * Check to see if the flag is set for N-tuple, this allows
+ * us to avoid the possible EINVAL response for the N-tuple
+ * flag not being set on the device
+ */
+ eval.cmd = ETHTOOL_GFLAGS;
+ err = send_ioctl(ctx, &eval);
+ if (err || !(eval.data & ETH_FLAG_NTUPLE))
+ return -1;
+
+ /* send rule via N-tuple */
+ ntuplecmd.cmd = ETHTOOL_SRXNTUPLE;
+ err = send_ioctl(ctx, &ntuplecmd);
+
+ /*
+ * Display error only if response is something other than op not
+ * supported. It is possible that the interface uses the network
+ * flow classifier interface instead of N-tuple.
+ */
+ if (err < 0) {
+ if (errno != EOPNOTSUPP)
+ perror("Cannot add new rule via N-tuple");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int do_writefwdump(struct ethtool_dump *dump, const char *dump_file)
+{
+ int err = 0;
+ FILE *f;
+ size_t bytes;
+
+ f = fopen(dump_file, "wb+");
+
+ if (!f) {
+ fprintf(stderr, "Can't open file %s: %s\n",
+ dump_file, strerror(errno));
+ return 1;
+ }
+ bytes = fwrite(dump->data, 1, dump->len, f);
+ if (bytes != dump->len) {
+ fprintf(stderr, "Can not write all of dump data\n");
+ err = 1;
+ }
+ if (fclose(f)) {
+ fprintf(stderr, "Can't close file %s: %s\n",
+ dump_file, strerror(errno));
+ err = 1;
+ }
+ return err;
+}
+
+static int do_getfwdump(struct cmd_context *ctx)
+{
+ u32 dump_flag;
+ char *dump_file;
+ int err;
+ struct ethtool_dump edata;
+ struct ethtool_dump *data;
+
+ if (ctx->argc == 2 && !strcmp(ctx->argp[0], "data")) {
+ dump_flag = ETHTOOL_GET_DUMP_DATA;
+ dump_file = ctx->argp[1];
+ } else if (ctx->argc == 0) {
+ dump_flag = 0;
+ dump_file = NULL;
+ } else {
+ exit_bad_args();
+ }
+
+ edata.cmd = ETHTOOL_GET_DUMP_FLAG;
+
+ err = send_ioctl(ctx, &edata);
+ if (err < 0) {
+ perror("Can not get dump level");
+ return 1;
+ }
+ if (dump_flag != ETHTOOL_GET_DUMP_DATA) {
+ fprintf(stdout, "flag: %u, version: %u, length: %u\n",
+ edata.flag, edata.version, edata.len);
+ return 0;
+ }
+ data = calloc(1, offsetof(struct ethtool_dump, data) + edata.len);
+ if (!data) {
+ perror("Can not allocate enough memory");
+ return 1;
+ }
+ data->cmd = ETHTOOL_GET_DUMP_DATA;
+ data->len = edata.len;
+ err = send_ioctl(ctx, data);
+ if (err < 0) {
+ perror("Can not get dump data");
+ err = 1;
+ goto free;
+ }
+ err = do_writefwdump(data, dump_file);
+free:
+ free(data);
+ return err;
+}
+
+static int do_setfwdump(struct cmd_context *ctx)
+{
+ u32 dump_flag;
+ int err;
+ struct ethtool_dump dump;
+
+ if (ctx->argc != 1)
+ exit_bad_args();
+ dump_flag = get_u32(ctx->argp[0], 0);
+
+ dump.cmd = ETHTOOL_SET_DUMP;
+ dump.flag = dump_flag;
+ err = send_ioctl(ctx, &dump);
+ if (err < 0) {
+ perror("Can not set dump level");
+ return 1;
+ }
+ return 0;
+}
+
+static int do_gprivflags(struct cmd_context *ctx)
+{
+ struct ethtool_gstrings *strings;
+ struct ethtool_value flags;
+ unsigned int i;
+ int max_len = 0, cur_len, rc;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ strings = get_stringset(ctx, ETH_SS_PRIV_FLAGS,
+ offsetof(struct ethtool_drvinfo, n_priv_flags),
+ 1);
+ if (!strings) {
+ perror("Cannot get private flag names");
+ return 1;
+ }
+ if (strings->len == 0) {
+ fprintf(stderr, "No private flags defined\n");
+ rc = 1;
+ goto err;
+ }
+ if (strings->len > 32) {
+ /* ETHTOOL_GPFLAGS can only cover 32 flags */
+ fprintf(stderr, "Only showing first 32 private flags\n");
+ strings->len = 32;
+ }
+
+ flags.cmd = ETHTOOL_GPFLAGS;
+ if (send_ioctl(ctx, &flags)) {
+ perror("Cannot get private flags");
+ rc = 1;
+ goto err;
+ }
+
+ /* Find longest string and align all strings accordingly */
+ for (i = 0; i < strings->len; i++) {
+ cur_len = strlen((const char *)strings->data +
+ i * ETH_GSTRING_LEN);
+ if (cur_len > max_len)
+ max_len = cur_len;
+ }
+
+ printf("Private flags for %s:\n", ctx->devname);
+ for (i = 0; i < strings->len; i++)
+ printf("%-*s: %s\n",
+ max_len,
+ (const char *)strings->data + i * ETH_GSTRING_LEN,
+ (flags.data & (1U << i)) ? "on" : "off");
+
+ rc = 0;
+
+err:
+ free(strings);
+ return rc;
+}
+
+static int do_sprivflags(struct cmd_context *ctx)
+{
+ struct ethtool_gstrings *strings;
+ struct cmdline_info *cmdline;
+ struct ethtool_value flags;
+ u32 wanted_flags = 0, seen_flags = 0;
+ int any_changed, rc;
+ unsigned int i;
+
+ strings = get_stringset(ctx, ETH_SS_PRIV_FLAGS,
+ offsetof(struct ethtool_drvinfo, n_priv_flags),
+ 1);
+ if (!strings) {
+ perror("Cannot get private flag names");
+ return 1;
+ }
+ if (strings->len == 0) {
+ fprintf(stderr, "No private flags defined\n");
+ rc = 1;
+ goto err;
+ }
+ if (strings->len > 32) {
+ /* ETHTOOL_{G,S}PFLAGS can only cover 32 flags */
+ fprintf(stderr, "Only setting first 32 private flags\n");
+ strings->len = 32;
+ }
+
+ cmdline = calloc(strings->len, sizeof(*cmdline));
+ if (!cmdline) {
+ perror("Cannot parse arguments");
+ rc = 1;
+ goto err;
+ }
+ for (i = 0; i < strings->len; i++) {
+ cmdline[i].name = ((const char *)strings->data +
+ i * ETH_GSTRING_LEN);
+ cmdline[i].type = CMDL_FLAG;
+ cmdline[i].wanted_val = &wanted_flags;
+ cmdline[i].flag_val = 1U << i;
+ cmdline[i].seen_val = &seen_flags;
+ }
+ parse_generic_cmdline(ctx, &any_changed, cmdline, strings->len);
+ free(cmdline);
+
+ flags.cmd = ETHTOOL_GPFLAGS;
+ if (send_ioctl(ctx, &flags)) {
+ perror("Cannot get private flags");
+ rc = 1;
+ goto err;
+ }
+
+ flags.cmd = ETHTOOL_SPFLAGS;
+ flags.data = (flags.data & ~seen_flags) | wanted_flags;
+ if (send_ioctl(ctx, &flags)) {
+ perror("Cannot set private flags");
+ rc = 1;
+ goto err;
+ }
+
+ rc = 0;
+err:
+ free(strings);
+ return rc;
+}
+
+static int do_tsinfo(struct cmd_context *ctx)
+{
+ struct ethtool_ts_info info;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ fprintf(stdout, "Time stamping parameters for %s:\n", ctx->devname);
+ info.cmd = ETHTOOL_GET_TS_INFO;
+ if (send_ioctl(ctx, &info)) {
+ perror("Cannot get device time stamping settings");
+ return -1;
+ }
+ dump_tsinfo(&info);
+ return 0;
+}
+
+static int do_getmodule(struct cmd_context *ctx)
+{
+ struct ethtool_modinfo modinfo;
+ struct ethtool_eeprom *eeprom;
+ u32 geeprom_offset = 0;
+ u32 geeprom_length = 0;
+ int geeprom_changed = 0;
+ int geeprom_dump_raw = 0;
+ int geeprom_dump_hex = 0;
+ int geeprom_length_seen = 0;
+ int err;
+
+ struct cmdline_info cmdline_geeprom[] = {
+ {
+ .name = "offset",
+ .type = CMDL_U32,
+ .wanted_val = &geeprom_offset,
+ },
+ {
+ .name = "length",
+ .type = CMDL_U32,
+ .wanted_val = &geeprom_length,
+ .seen_val = &geeprom_length_seen,
+ },
+ {
+ .name = "raw",
+ .type = CMDL_BOOL,
+ .wanted_val = &geeprom_dump_raw,
+ },
+ {
+ .name = "hex",
+ .type = CMDL_BOOL,
+ .wanted_val = &geeprom_dump_hex,
+ },
+ };
+
+ parse_generic_cmdline(ctx, &geeprom_changed,
+ cmdline_geeprom, ARRAY_SIZE(cmdline_geeprom));
+
+ if (geeprom_dump_raw && geeprom_dump_hex) {
+ printf("Hex and raw dump cannot be specified together\n");
+ return 1;
+ }
+
+ modinfo.cmd = ETHTOOL_GMODULEINFO;
+ err = send_ioctl(ctx, &modinfo);
+ if (err < 0) {
+ perror("Cannot get module EEPROM information");
+ return 1;
+ }
+
+ if (!geeprom_length_seen)
+ geeprom_length = modinfo.eeprom_len;
+
+ if (modinfo.eeprom_len < geeprom_offset + geeprom_length)
+ geeprom_length = modinfo.eeprom_len - geeprom_offset;
+
+ eeprom = calloc(1, sizeof(*eeprom)+geeprom_length);
+ if (!eeprom) {
+ perror("Cannot allocate memory for Module EEPROM data");
+ return 1;
+ }
+
+ eeprom->cmd = ETHTOOL_GMODULEEEPROM;
+ eeprom->len = geeprom_length;
+ eeprom->offset = geeprom_offset;
+ err = send_ioctl(ctx, eeprom);
+ if (err < 0) {
+ int saved_errno = errno;
+
+ perror("Cannot get Module EEPROM data");
+ if (saved_errno == ENODEV || saved_errno == EIO ||
+ saved_errno == ENXIO)
+ fprintf(stderr, "SFP module not in cage?\n");
+ free(eeprom);
+ return 1;
+ }
+
+ /*
+ * SFF-8079 EEPROM layout contains the memory available at A0 address on
+ * the PHY EEPROM.
+ * SFF-8472 defines a virtual extension of the EEPROM, where the
+ * microcontroller on the SFP/SFP+ generates a page at the A2 address,
+ * which contains data relative to optical diagnostics.
+ * The current kernel implementation returns a blob, which contains:
+ * - ETH_MODULE_SFF_8079 => The A0 page only.
+ * - ETH_MODULE_SFF_8472 => The A0 and A2 page concatenated.
+ */
+ if (geeprom_dump_raw) {
+ fwrite(eeprom->data, 1, eeprom->len, stdout);
+ } else {
+ if (eeprom->offset != 0 ||
+ (eeprom->len != modinfo.eeprom_len)) {
+ geeprom_dump_hex = 1;
+ } else if (!geeprom_dump_hex) {
+ switch (modinfo.type) {
+#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ case ETH_MODULE_SFF_8079:
+ sff8079_show_all_ioctl(eeprom->data);
+ break;
+ case ETH_MODULE_SFF_8472:
+ sff8079_show_all_ioctl(eeprom->data);
+ sff8472_show_all(eeprom->data);
+ break;
+ case ETH_MODULE_SFF_8436:
+ case ETH_MODULE_SFF_8636:
+ sff8636_show_all_ioctl(eeprom->data,
+ modinfo.eeprom_len);
+ break;
+#endif
+ default:
+ geeprom_dump_hex = 1;
+ break;
+ }
+ }
+ if (geeprom_dump_hex)
+ dump_hex(stdout, eeprom->data,
+ eeprom->len, eeprom->offset);
+ }
+
+ free(eeprom);
+
+ return 0;
+}
+
+static int do_geee(struct cmd_context *ctx)
+{
+ struct ethtool_eee eeecmd;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ eeecmd.cmd = ETHTOOL_GEEE;
+ if (send_ioctl(ctx, &eeecmd)) {
+ perror("Cannot get EEE settings");
+ return 1;
+ }
+
+ fprintf(stdout, "EEE Settings for %s:\n", ctx->devname);
+ dump_eeecmd(&eeecmd);
+
+ return 0;
+}
+
+static int do_seee(struct cmd_context *ctx)
+{
+ int adv_c = -1, lpi_c = -1, lpi_time_c = -1, eee_c = -1;
+ int change = -1, change2 = 0;
+ struct ethtool_eee eeecmd;
+ struct cmdline_info cmdline_eee[] = {
+ {
+ .name = "advertise",
+ .type = CMDL_U32,
+ .wanted_val = &adv_c,
+ .ioctl_val = &eeecmd.advertised,
+ },
+ {
+ .name = "tx-lpi",
+ .type = CMDL_BOOL,
+ .wanted_val = &lpi_c,
+ .ioctl_val = &eeecmd.tx_lpi_enabled,
+ },
+ {
+ .name = "tx-timer",
+ .type = CMDL_U32,
+ .wanted_val = &lpi_time_c,
+ .ioctl_val = &eeecmd.tx_lpi_timer,
+ },
+ {
+ .name = "eee",
+ .type = CMDL_BOOL,
+ .wanted_val = &eee_c,
+ .ioctl_val = &eeecmd.eee_enabled,
+ },
+ };
+
+ if (ctx->argc == 0)
+ exit_bad_args();
+
+ parse_generic_cmdline(ctx, &change, cmdline_eee,
+ ARRAY_SIZE(cmdline_eee));
+
+ eeecmd.cmd = ETHTOOL_GEEE;
+ if (send_ioctl(ctx, &eeecmd)) {
+ perror("Cannot get EEE settings");
+ return 1;
+ }
+
+ do_generic_set(cmdline_eee, ARRAY_SIZE(cmdline_eee), &change2);
+
+ if (change2) {
+ eeecmd.cmd = ETHTOOL_SEEE;
+ if (send_ioctl(ctx, &eeecmd)) {
+ perror("Cannot set EEE settings");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* copy of net/ethtool/common.c */
+char
+tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
+ [ETHTOOL_ID_UNSPEC] = "Unspec",
+ [ETHTOOL_RX_COPYBREAK] = "rx-copybreak",
+ [ETHTOOL_TX_COPYBREAK] = "tx-copybreak",
+ [ETHTOOL_TX_COPYBREAK_BUF_SIZE] = "tx-buf-size",
+ [ETHTOOL_PFC_PREVENTION_TOUT] = "pfc-prevention-tout",
+};
+
+union ethtool_tunable_info_val {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ int8_t s8;
+ int16_t s16;
+ int32_t s32;
+ int64_t s64;
+};
+
+struct ethtool_tunable_info {
+ enum tunable_id t_id;
+ enum tunable_type_id t_type_id;
+ size_t size;
+ cmdline_type_t type;
+ union ethtool_tunable_info_val wanted;
+ int seen;
+};
+
+static struct ethtool_tunable_info tunables_info[] = {
+ { .t_id = ETHTOOL_RX_COPYBREAK,
+ .t_type_id = ETHTOOL_TUNABLE_U32,
+ .size = sizeof(u32),
+ .type = CMDL_U32,
+ },
+ { .t_id = ETHTOOL_TX_COPYBREAK,
+ .t_type_id = ETHTOOL_TUNABLE_U32,
+ .size = sizeof(u32),
+ .type = CMDL_U32,
+ },
+ { .t_id = ETHTOOL_PFC_PREVENTION_TOUT,
+ .t_type_id = ETHTOOL_TUNABLE_U16,
+ .size = sizeof(u16),
+ .type = CMDL_U16,
+ },
+ { .t_id = ETHTOOL_TX_COPYBREAK_BUF_SIZE,
+ .t_type_id = ETHTOOL_TUNABLE_U32,
+ .size = sizeof(u32),
+ .type = CMDL_U32,
+ },
+};
+#define TUNABLES_INFO_SIZE ARRAY_SIZE(tunables_info)
+
+static int do_stunable(struct cmd_context *ctx)
+{
+ struct cmdline_info cmdline_tunable[TUNABLES_INFO_SIZE];
+ struct ethtool_tunable_info *tinfo = tunables_info;
+ int changed = 0;
+ unsigned int i;
+
+ for (i = 0; i < TUNABLES_INFO_SIZE; i++) {
+ cmdline_tunable[i].name = tunable_strings[tinfo[i].t_id];
+ cmdline_tunable[i].type = tinfo[i].type;
+ cmdline_tunable[i].wanted_val = &tinfo[i].wanted;
+ cmdline_tunable[i].seen_val = &tinfo[i].seen;
+ }
+
+ parse_generic_cmdline(ctx, &changed, cmdline_tunable, TUNABLES_INFO_SIZE);
+ if (!changed)
+ exit_bad_args();
+
+ for (i = 0; i < TUNABLES_INFO_SIZE; i++) {
+ struct ethtool_tunable *tuna;
+ size_t size;
+ int ret;
+
+ if (!tinfo[i].seen)
+ continue;
+
+ size = sizeof(*tuna) + tinfo[i].size;
+ tuna = calloc(1, size);
+ if (!tuna) {
+ perror(tunable_strings[tinfo[i].t_id]);
+ return 1;
+ }
+ tuna->cmd = ETHTOOL_STUNABLE;
+ tuna->id = tinfo[i].t_id;
+ tuna->type_id = tinfo[i].t_type_id;
+ tuna->len = tinfo[i].size;
+ memcpy(tuna->data, &tinfo[i].wanted, tuna->len);
+ ret = send_ioctl(ctx, tuna);
+ if (ret) {
+ perror(tunable_strings[tuna->id]);
+ free(tuna);
+ return ret;
+ }
+ free(tuna);
+ }
+ return 0;
+}
+
+static void print_tunable(struct ethtool_tunable *tuna)
+{
+ char *name = tunable_strings[tuna->id];
+ union ethtool_tunable_info_val *val;
+
+ val = (union ethtool_tunable_info_val *)tuna->data;
+ switch (tuna->type_id) {
+ case ETHTOOL_TUNABLE_U8:
+ fprintf(stdout, "%s: %" PRIu8 "\n", name, val->u8);
+ break;
+ case ETHTOOL_TUNABLE_U16:
+ fprintf(stdout, "%s: %" PRIu16 "\n", name, val->u16);
+ break;
+ case ETHTOOL_TUNABLE_U32:
+ fprintf(stdout, "%s: %" PRIu32 "\n", name, val->u32);
+ break;
+ case ETHTOOL_TUNABLE_U64:
+ fprintf(stdout, "%s: %" PRIu64 "\n", name, val->u64);
+ break;
+ case ETHTOOL_TUNABLE_S8:
+ fprintf(stdout, "%s: %" PRId8 "\n", name, val->s8);
+ break;
+ case ETHTOOL_TUNABLE_S16:
+ fprintf(stdout, "%s: %" PRId16 "\n", name, val->s16);
+ break;
+ case ETHTOOL_TUNABLE_S32:
+ fprintf(stdout, "%s: %" PRId32 "\n", name, val->s32);
+ break;
+ case ETHTOOL_TUNABLE_S64:
+ fprintf(stdout, "%s: %" PRId64 "\n", name, val->s64);
+ break;
+ default:
+ fprintf(stdout, "%s: Unknown format\n", name);
+ }
+}
+
+static int do_gtunable(struct cmd_context *ctx)
+{
+ struct ethtool_tunable_info *tinfo = tunables_info;
+ char **argp = ctx->argp;
+ unsigned int argc = ctx->argc;
+ unsigned int i, j;
+
+ if (argc < 1)
+ exit_bad_args();
+
+ for (i = 0; i < argc; i++) {
+ int valid = 0;
+
+ for (j = 0; j < TUNABLES_INFO_SIZE; j++) {
+ char *ts = tunable_strings[tinfo[j].t_id];
+ struct ethtool_tunable *tuna;
+ int ret;
+
+ if (strcmp(argp[i], ts))
+ continue;
+ valid = 1;
+
+ tuna = calloc(1, sizeof(*tuna) + tinfo[j].size);
+ if (!tuna) {
+ perror(ts);
+ return 1;
+ }
+ tuna->cmd = ETHTOOL_GTUNABLE;
+ tuna->id = tinfo[j].t_id;
+ tuna->type_id = tinfo[j].t_type_id;
+ tuna->len = tinfo[j].size;
+ ret = send_ioctl(ctx, tuna);
+ if (ret) {
+ fprintf(stderr, "%s: Cannot get tunable\n", ts);
+ free(tuna);
+ return ret;
+ }
+ print_tunable(tuna);
+ free(tuna);
+ }
+ if (!valid)
+ exit_bad_args();
+ }
+ return 0;
+}
+
+static int do_get_phy_tunable(struct cmd_context *ctx)
+{
+ unsigned int argc = ctx->argc;
+ char **argp = ctx->argp;
+
+ if (argc < 1)
+ exit_bad_args();
+
+ if (!strcmp(argp[0], "downshift")) {
+ struct {
+ struct ethtool_tunable ds;
+ u8 count;
+ } cont;
+
+ cont.ds.cmd = ETHTOOL_PHY_GTUNABLE;
+ cont.ds.id = ETHTOOL_PHY_DOWNSHIFT;
+ cont.ds.type_id = ETHTOOL_TUNABLE_U8;
+ cont.ds.len = 1;
+ if (send_ioctl(ctx, &cont.ds) < 0) {
+ perror("Cannot Get PHY downshift count");
+ return 87;
+ }
+ if (cont.count)
+ fprintf(stdout, "Downshift count: %d\n", cont.count);
+ else
+ fprintf(stdout, "Downshift disabled\n");
+ } else if (!strcmp(argp[0], "fast-link-down")) {
+ struct {
+ struct ethtool_tunable fld;
+ u8 msecs;
+ } cont;
+
+ cont.fld.cmd = ETHTOOL_PHY_GTUNABLE;
+ cont.fld.id = ETHTOOL_PHY_FAST_LINK_DOWN;
+ cont.fld.type_id = ETHTOOL_TUNABLE_U8;
+ cont.fld.len = 1;
+ if (send_ioctl(ctx, &cont.fld) < 0) {
+ perror("Cannot Get PHY Fast Link Down value");
+ return 87;
+ }
+
+ if (cont.msecs == ETHTOOL_PHY_FAST_LINK_DOWN_ON)
+ fprintf(stdout, "Fast Link Down enabled\n");
+ else if (cont.msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
+ fprintf(stdout, "Fast Link Down disabled\n");
+ else
+ fprintf(stdout, "Fast Link Down enabled, %d msecs\n",
+ cont.msecs);
+ } else if (!strcmp(argp[0], "energy-detect-power-down")) {
+ struct {
+ struct ethtool_tunable ds;
+ u16 msecs;
+ } cont;
+
+ cont.ds.cmd = ETHTOOL_PHY_GTUNABLE;
+ cont.ds.id = ETHTOOL_PHY_EDPD;
+ cont.ds.type_id = ETHTOOL_TUNABLE_U16;
+ cont.ds.len = 2;
+ if (send_ioctl(ctx, &cont.ds) < 0) {
+ perror("Cannot Get PHY Energy Detect Power Down value");
+ return 87;
+ }
+
+ if (cont.msecs == ETHTOOL_PHY_EDPD_DISABLE)
+ fprintf(stdout, "Energy Detect Power Down: disabled\n");
+ else if (cont.msecs == ETHTOOL_PHY_EDPD_NO_TX)
+ fprintf(stdout,
+ "Energy Detect Power Down: enabled, TX disabled\n");
+ else
+ fprintf(stdout,
+ "Energy Detect Power Down: enabled, TX %u msecs\n",
+ cont.msecs);
+ } else {
+ exit_bad_args();
+ }
+
+ return 0;
+}
+
+static __u32 parse_reset(char *val, __u32 bitset, char *arg, __u32 *data)
+{
+ __u32 bitval = 0;
+ int i;
+
+ /* Check for component match */
+ for (i = 0; val[i] != '\0'; i++)
+ if (arg[i] != val[i])
+ return 0;
+
+ /* Check if component has -shared specified or not */
+ if (arg[i] == '\0')
+ bitval = bitset;
+ else if (!strcmp(arg+i, "-shared"))
+ bitval = bitset << ETH_RESET_SHARED_SHIFT;
+
+ if (bitval) {
+ *data |= bitval;
+ return 1;
+ }
+ return 0;
+}
+
+static int do_reset(struct cmd_context *ctx)
+{
+ struct ethtool_value resetinfo;
+ __u32 data;
+ unsigned int argc = ctx->argc;
+ char **argp = ctx->argp;
+ unsigned int i;
+
+ if (argc == 0)
+ exit_bad_args();
+
+ data = 0;
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argp[i], "flags")) {
+ __u32 flags;
+
+ i++;
+ if (i >= argc)
+ exit_bad_args();
+ flags = strtoul(argp[i], NULL, 0);
+ if (flags == 0)
+ exit_bad_args();
+ else
+ data |= flags;
+ } else if (parse_reset("mgmt", ETH_RESET_MGMT,
+ argp[i], &data)) {
+ } else if (parse_reset("irq", ETH_RESET_IRQ,
+ argp[i], &data)) {
+ } else if (parse_reset("dma", ETH_RESET_DMA,
+ argp[i], &data)) {
+ } else if (parse_reset("filter", ETH_RESET_FILTER,
+ argp[i], &data)) {
+ } else if (parse_reset("offload", ETH_RESET_OFFLOAD,
+ argp[i], &data)) {
+ } else if (parse_reset("mac", ETH_RESET_MAC,
+ argp[i], &data)) {
+ } else if (parse_reset("phy", ETH_RESET_PHY,
+ argp[i], &data)) {
+ } else if (parse_reset("ram", ETH_RESET_RAM,
+ argp[i], &data)) {
+ } else if (parse_reset("ap", ETH_RESET_AP,
+ argp[i], &data)) {
+ } else if (!strcmp(argp[i], "dedicated")) {
+ data |= ETH_RESET_DEDICATED;
+ } else if (!strcmp(argp[i], "all")) {
+ data |= ETH_RESET_ALL;
+ } else {
+ exit_bad_args();
+ }
+ }
+
+ resetinfo.cmd = ETHTOOL_RESET;
+ resetinfo.data = data;
+ fprintf(stdout, "ETHTOOL_RESET 0x%x\n", resetinfo.data);
+
+ if (send_ioctl(ctx, &resetinfo)) {
+ perror("Cannot issue ETHTOOL_RESET");
+ return 1;
+ }
+
+ fprintf(stdout, "Components reset: 0x%x\n", data & ~resetinfo.data);
+ if (resetinfo.data)
+ fprintf(stdout, "Components not reset: 0x%x\n", resetinfo.data);
+
+ return 0;
+}
+
+static int parse_named_bool(struct cmd_context *ctx, const char *name, u8 *on)
+{
+ if (ctx->argc < 2)
+ return 0;
+
+ if (strcmp(*ctx->argp, name))
+ return 0;
+
+ if (!strcmp(*(ctx->argp + 1), "on")) {
+ *on = 1;
+ } else if (!strcmp(*(ctx->argp + 1), "off")) {
+ *on = 0;
+ } else {
+ fprintf(stderr, "Invalid boolean\n");
+ exit_bad_args();
+ }
+
+ ctx->argc -= 2;
+ ctx->argp += 2;
+
+ return 1;
+}
+
+static int parse_named_uint(struct cmd_context *ctx,
+ const char *name,
+ unsigned long long *val,
+ unsigned long long max)
+{
+ if (ctx->argc < 2)
+ return 0;
+
+ if (strcmp(*ctx->argp, name))
+ return 0;
+
+ *val = get_uint_range(*(ctx->argp + 1), 0, max);
+
+ ctx->argc -= 2;
+ ctx->argp += 2;
+
+ return 1;
+}
+
+static int parse_named_u8(struct cmd_context *ctx, const char *name, u8 *val)
+{
+ unsigned long long val1;
+ int ret;
+
+ ret = parse_named_uint(ctx, name, &val1, 0xff);
+ if (ret)
+ *val = val1;
+
+ return ret;
+}
+
+static int parse_named_u16(struct cmd_context *ctx, const char *name, u16 *val)
+{
+ unsigned long long val1;
+ int ret;
+
+ ret = parse_named_uint(ctx, name, &val1, 0xffff);
+ if (ret)
+ *val = val1;
+
+ return ret;
+}
+
+static int do_set_phy_tunable(struct cmd_context *ctx)
+{
+ int err = 0;
+ u8 ds_cnt = DOWNSHIFT_DEV_DEFAULT_COUNT;
+ u8 ds_changed = 0, ds_has_cnt = 0, ds_enable = 0;
+ u8 fld_changed = 0, fld_enable = 0;
+ u8 fld_msecs = ETHTOOL_PHY_FAST_LINK_DOWN_ON;
+ u8 edpd_changed = 0, edpd_enable = 0;
+ u16 edpd_tx_interval = ETHTOOL_PHY_EDPD_DFLT_TX_MSECS;
+
+ /* Parse arguments */
+ if (parse_named_bool(ctx, "downshift", &ds_enable)) {
+ ds_changed = 1;
+ ds_has_cnt = parse_named_u8(ctx, "count", &ds_cnt);
+ } else if (parse_named_bool(ctx, "fast-link-down", &fld_enable)) {
+ fld_changed = 1;
+ if (fld_enable)
+ parse_named_u8(ctx, "msecs", &fld_msecs);
+ } else if (parse_named_bool(ctx, "energy-detect-power-down",
+ &edpd_enable)) {
+ edpd_changed = 1;
+ if (edpd_enable)
+ parse_named_u16(ctx, "msecs", &edpd_tx_interval);
+ } else {
+ exit_bad_args();
+ }
+
+ /* Validate parameters */
+ if (ds_changed) {
+ if (!ds_enable && ds_has_cnt) {
+ fprintf(stderr, "'count' may not be set when downshift "
+ "is off.\n");
+ exit_bad_args();
+ }
+
+ if (ds_enable && ds_has_cnt && ds_cnt == 0) {
+ fprintf(stderr, "'count' may not be zero.\n");
+ exit_bad_args();
+ }
+
+ if (!ds_enable)
+ ds_cnt = DOWNSHIFT_DEV_DISABLE;
+ } else if (fld_changed) {
+ if (!fld_enable)
+ fld_msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF;
+ else if (fld_msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
+ exit_bad_args();
+ } else if (edpd_changed) {
+ if (!edpd_enable)
+ edpd_tx_interval = ETHTOOL_PHY_EDPD_DISABLE;
+ else if (edpd_tx_interval == 0)
+ edpd_tx_interval = ETHTOOL_PHY_EDPD_NO_TX;
+ else if (edpd_tx_interval > ETHTOOL_PHY_EDPD_NO_TX) {
+ fprintf(stderr, "'msecs' max value is %d.\n",
+ (ETHTOOL_PHY_EDPD_NO_TX - 1));
+ exit_bad_args();
+ }
+ }
+
+ /* Do it */
+ if (ds_changed) {
+ struct {
+ struct ethtool_tunable ds;
+ u8 count;
+ } cont;
+
+ cont.ds.cmd = ETHTOOL_PHY_STUNABLE;
+ cont.ds.id = ETHTOOL_PHY_DOWNSHIFT;
+ cont.ds.type_id = ETHTOOL_TUNABLE_U8;
+ cont.ds.len = 1;
+ cont.count = ds_cnt;
+ err = send_ioctl(ctx, &cont.ds);
+ if (err < 0) {
+ perror("Cannot Set PHY downshift count");
+ err = 87;
+ }
+ } else if (fld_changed) {
+ struct {
+ struct ethtool_tunable fld;
+ u8 msecs;
+ } cont;
+
+ cont.fld.cmd = ETHTOOL_PHY_STUNABLE;
+ cont.fld.id = ETHTOOL_PHY_FAST_LINK_DOWN;
+ cont.fld.type_id = ETHTOOL_TUNABLE_U8;
+ cont.fld.len = 1;
+ cont.msecs = fld_msecs;
+ err = send_ioctl(ctx, &cont.fld);
+ if (err < 0) {
+ perror("Cannot Set PHY Fast Link Down value");
+ err = 87;
+ }
+ } else if (edpd_changed) {
+ struct {
+ struct ethtool_tunable fld;
+ u16 msecs;
+ } cont;
+
+ cont.fld.cmd = ETHTOOL_PHY_STUNABLE;
+ cont.fld.id = ETHTOOL_PHY_EDPD;
+ cont.fld.type_id = ETHTOOL_TUNABLE_U16;
+ cont.fld.len = 2;
+ cont.msecs = edpd_tx_interval;
+ err = send_ioctl(ctx, &cont.fld);
+ if (err < 0) {
+ perror("Cannot Set PHY Energy Detect Power Down");
+ err = 87;
+ }
+ }
+
+ return err;
+}
+
+static int fecmode_str_to_type(const char *str)
+{
+ if (!strcasecmp(str, "auto"))
+ return ETHTOOL_FEC_AUTO;
+ if (!strcasecmp(str, "off"))
+ return ETHTOOL_FEC_OFF;
+ if (!strcasecmp(str, "rs"))
+ return ETHTOOL_FEC_RS;
+ if (!strcasecmp(str, "baser"))
+ return ETHTOOL_FEC_BASER;
+ if (!strcasecmp(str, "llrs"))
+ return ETHTOOL_FEC_LLRS;
+ return 0;
+}
+
+static int do_gfec(struct cmd_context *ctx)
+{
+ struct ethtool_fecparam feccmd = { 0 };
+ int rv;
+
+ if (ctx->argc != 0)
+ exit_bad_args();
+
+ feccmd.cmd = ETHTOOL_GFECPARAM;
+ rv = send_ioctl(ctx, &feccmd);
+ if (rv != 0) {
+ perror("Cannot get FEC settings");
+ return rv;
+ }
+
+ fprintf(stdout, "FEC parameters for %s:\n", ctx->devname);
+ fprintf(stdout, "Supported/Configured FEC encodings:");
+ dump_fec(feccmd.fec);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "Active FEC encoding:");
+ dump_fec(feccmd.active_fec);
+ fprintf(stdout, "\n");
+
+ return 0;
+}
+
+static int do_sfec(struct cmd_context *ctx)
+{
+ enum { ARG_NONE, ARG_ENCODING } state = ARG_NONE;
+ struct ethtool_fecparam feccmd;
+ int fecmode = 0, newmode;
+ unsigned int i;
+ int rv;
+
+ for (i = 0; i < ctx->argc; i++) {
+ if (!strcmp(ctx->argp[i], "encoding")) {
+ state = ARG_ENCODING;
+ continue;
+ }
+ if (state == ARG_ENCODING) {
+ newmode = fecmode_str_to_type(ctx->argp[i]);
+ if (!newmode)
+ exit_bad_args();
+ fecmode |= newmode;
+ continue;
+ }
+ exit_bad_args();
+ }
+
+ if (!fecmode)
+ exit_bad_args();
+
+ feccmd.cmd = ETHTOOL_SFECPARAM;
+ feccmd.fec = fecmode;
+ rv = send_ioctl(ctx, &feccmd);
+ if (rv != 0) {
+ perror("Cannot set FEC settings");
+ return rv;
+ }
+
+ return 0;
+}
+
+static int do_perqueue(struct cmd_context *ctx);
+
+#ifndef TEST_ETHTOOL
+int send_ioctl(struct cmd_context *ctx, void *cmd)
+{
+ ctx->ifr.ifr_data = cmd;
+ return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);
+}
+#endif
+
+static int show_usage(struct cmd_context *ctx);
+
+struct option {
+ const char *opts;
+ bool no_dev;
+ bool json;
+ int (*func)(struct cmd_context *);
+ nl_chk_t nlchk;
+ nl_func_t nlfunc;
+ const char *help;
+ const char *xhelp;
+};
+
+static const struct option args[] = {
+ {
+ /* "default" entry when no switch is used */
+ .opts = "",
+ .func = do_gset,
+ .nlfunc = nl_gset,
+ .help = "Display standard information about device",
+ },
+ {
+ .opts = "-s|--change",
+ .func = do_sset,
+ .nlfunc = nl_sset,
+ .help = "Change generic options",
+ .xhelp = " [ speed %d ]\n"
+ " [ lanes %d ]\n"
+ " [ duplex half|full ]\n"
+ " [ port tp|aui|bnc|mii|fibre|da ]\n"
+ " [ mdix auto|on|off ]\n"
+ " [ autoneg on|off ]\n"
+ " [ advertise %x[/%x] | mode on|off ... [--] ]\n"
+ " [ phyad %d ]\n"
+ " [ xcvr internal|external ]\n"
+ " [ wol %d[/%d] | p|u|m|b|a|g|s|f|d... ]\n"
+ " [ sopass %x:%x:%x:%x:%x:%x ]\n"
+ " [ msglvl %d[/%d] | type on|off ... [--] ]\n"
+ " [ master-slave preferred-master|preferred-slave|forced-master|forced-slave ]\n"
+ },
+ {
+ .opts = "-a|--show-pause",
+ .json = true,
+ .func = do_gpause,
+ .nlfunc = nl_gpause,
+ .help = "Show pause options",
+ .xhelp = " [ --src aggregate | emac | pmac ]\n"
+ },
+ {
+ .opts = "-A|--pause",
+ .func = do_spause,
+ .nlfunc = nl_spause,
+ .help = "Set pause options",
+ .xhelp = " [ autoneg on|off ]\n"
+ " [ rx on|off ]\n"
+ " [ tx on|off ]\n"
+ },
+ {
+ .opts = "-c|--show-coalesce",
+ .json = true,
+ .func = do_gcoalesce,
+ .nlfunc = nl_gcoalesce,
+ .help = "Show coalesce options"
+ },
+ {
+ .opts = "-C|--coalesce",
+ .func = do_scoalesce,
+ .nlfunc = nl_scoalesce,
+ .help = "Set coalesce options",
+ .xhelp = " [adaptive-rx on|off]\n"
+ " [adaptive-tx on|off]\n"
+ " [rx-usecs N]\n"
+ " [rx-frames N]\n"
+ " [rx-usecs-irq N]\n"
+ " [rx-frames-irq N]\n"
+ " [tx-usecs N]\n"
+ " [tx-frames N]\n"
+ " [tx-usecs-irq N]\n"
+ " [tx-frames-irq N]\n"
+ " [stats-block-usecs N]\n"
+ " [pkt-rate-low N]\n"
+ " [rx-usecs-low N]\n"
+ " [rx-frames-low N]\n"
+ " [tx-usecs-low N]\n"
+ " [tx-frames-low N]\n"
+ " [pkt-rate-high N]\n"
+ " [rx-usecs-high N]\n"
+ " [rx-frames-high N]\n"
+ " [tx-usecs-high N]\n"
+ " [tx-frames-high N]\n"
+ " [sample-interval N]\n"
+ " [cqe-mode-rx on|off]\n"
+ " [cqe-mode-tx on|off]\n"
+ " [tx-aggr-max-bytes N]\n"
+ " [tx-aggr-max-frames N]\n"
+ " [tx-aggr-time-usecs N]\n"
+ },
+ {
+ .opts = "-g|--show-ring",
+ .json = true,
+ .func = do_gring,
+ .nlfunc = nl_gring,
+ .help = "Query RX/TX ring parameters"
+ },
+ {
+ .opts = "-G|--set-ring",
+ .func = do_sring,
+ .nlfunc = nl_sring,
+ .help = "Set RX/TX ring parameters",
+ .xhelp = " [ rx N ]\n"
+ " [ rx-mini N ]\n"
+ " [ rx-jumbo N ]\n"
+ " [ tx N ]\n"
+ " [ rx-buf-len N ]\n"
+ " [ tcp-data-split auto|on|off ]\n"
+ " [ cqe-size N ]\n"
+ " [ tx-push on|off ]\n"
+ " [ rx-push on|off ]\n"
+ " [ tx-push-buf-len N]\n"
+ },
+ {
+ .opts = "-k|--show-features|--show-offload",
+ .json = true,
+ .func = do_gfeatures,
+ .nlfunc = nl_gfeatures,
+ .help = "Get state of protocol offload and other features"
+ },
+ {
+ .opts = "-K|--features|--offload",
+ .func = do_sfeatures,
+ .nlfunc = nl_sfeatures,
+ .help = "Set protocol offload and other features",
+ .xhelp = " FEATURE on|off ...\n"
+ },
+ {
+ .opts = "-i|--driver",
+ .func = do_gdrv,
+ .help = "Show driver information"
+ },
+ {
+ .opts = "-d|--register-dump",
+ .func = do_gregs,
+ .help = "Do a register dump",
+ .xhelp = " [ raw on|off ]\n"
+ " [ file FILENAME ]\n"
+ },
+ {
+ .opts = "-e|--eeprom-dump",
+ .func = do_geeprom,
+ .help = "Do a EEPROM dump",
+ .xhelp = " [ raw on|off ]\n"
+ " [ offset N ]\n"
+ " [ length N ]\n"
+ },
+ {
+ .opts = "-E|--change-eeprom",
+ .func = do_seeprom,
+ .help = "Change bytes in device EEPROM",
+ .xhelp = " [ magic N ]\n"
+ " [ offset N ]\n"
+ " [ length N ]\n"
+ " [ value N ]\n"
+ },
+ {
+ .opts = "-r|--negotiate",
+ .func = do_nway_rst,
+ .help = "Restart N-WAY negotiation"
+ },
+ {
+ .opts = "-p|--identify",
+ .func = do_phys_id,
+ .help = "Show visible port identification (e.g. blinking)",
+ .xhelp = " [ TIME-IN-SECONDS ]\n"
+ },
+ {
+ .opts = "-t|--test",
+ .func = do_test,
+ .help = "Execute adapter self test",
+ .xhelp = " [ online | offline | external_lb ]\n"
+ },
+ {
+ .opts = "-S|--statistics",
+ .json = true,
+ .func = do_gnicstats,
+ .nlchk = nl_gstats_chk,
+ .nlfunc = nl_gstats,
+ .help = "Show adapter statistics",
+ .xhelp = " [ --all-groups | --groups [eth-phy] [eth-mac] [eth-ctrl] [rmon] ]\n"
+ " [ --src aggregate | emac | pmac ]\n"
+ },
+ {
+ .opts = "--phy-statistics",
+ .func = do_gphystats,
+ .help = "Show phy statistics"
+ },
+ {
+ .opts = "-n|-u|--show-nfc|--show-ntuple",
+ .func = do_grxclass,
+ .help = "Show Rx network flow classification options or rules",
+ .xhelp = " [ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
+ "tcp6|udp6|ah6|esp6|sctp6 [context %d] |\n"
+ " rule %d ]\n"
+ },
+ {
+ .opts = "-N|-U|--config-nfc|--config-ntuple",
+ .func = do_srxclass,
+ .help = "Configure Rx network flow classification options or rules",
+ .xhelp = " rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
+ "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... [context %d] |\n"
+ " flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4|"
+ "ip6|tcp6|udp6|ah6|esp6|sctp6\n"
+ " [ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+ " [ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+ " [ proto %d [m %x] ]\n"
+ " [ src-ip IP-ADDRESS [m IP-ADDRESS] ]\n"
+ " [ dst-ip IP-ADDRESS [m IP-ADDRESS] ]\n"
+ " [ tos %d [m %x] ]\n"
+ " [ tclass %d [m %x] ]\n"
+ " [ l4proto %d [m %x] ]\n"
+ " [ src-port %d [m %x] ]\n"
+ " [ dst-port %d [m %x] ]\n"
+ " [ spi %d [m %x] ]\n"
+ " [ vlan-etype %x [m %x] ]\n"
+ " [ vlan %x [m %x] ]\n"
+ " [ user-def %x [m %x] ]\n"
+ " [ dst-mac %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+ " [ action %d ] | [ vf %d queue %d ]\n"
+ " [ context %d ]\n"
+ " [ loc %d ] |\n"
+ " delete %d\n"
+ },
+ {
+ .opts = "-T|--show-time-stamping",
+ .func = do_tsinfo,
+ .nlfunc = nl_tsinfo,
+ .help = "Show time stamping capabilities"
+ },
+ {
+ .opts = "-x|--show-rxfh-indir|--show-rxfh",
+ .json = true,
+ .func = do_grxfh,
+ .nlfunc = nl_grss,
+ .help = "Show Rx flow hash indirection table and/or RSS hash key",
+ .xhelp = " [ context %d ]\n"
+ },
+ {
+ .opts = "-X|--set-rxfh-indir|--rxfh",
+ .func = do_srxfh,
+ .help = "Set Rx flow hash indirection table and/or RSS hash key",
+ .xhelp = " [ context %d|new ]\n"
+ " [ equal N | weight W0 W1 ... | default ]\n"
+ " [ hkey %x:%x:%x:%x:%x:.... ]\n"
+ " [ hfunc FUNC ]\n"
+ " [ delete ]\n"
+ },
+ {
+ .opts = "-f|--flash",
+ .func = do_flash,
+ .help = "Flash firmware image from the specified file to a region on the device",
+ .xhelp = " FILENAME [ REGION-NUMBER-TO-FLASH ]\n"
+ },
+ {
+ .opts = "-P|--show-permaddr",
+ .func = do_permaddr,
+ .nlfunc = nl_permaddr,
+ .help = "Show permanent hardware address"
+ },
+ {
+ .opts = "-w|--get-dump",
+ .func = do_getfwdump,
+ .help = "Get dump flag, data",
+ .xhelp = " [ data FILENAME ]\n"
+ },
+ {
+ .opts = "-W|--set-dump",
+ .func = do_setfwdump,
+ .help = "Set dump flag of the device",
+ .xhelp = " N\n"
+ },
+ {
+ .opts = "-l|--show-channels",
+ .func = do_gchannels,
+ .nlfunc = nl_gchannels,
+ .help = "Query Channels"
+ },
+ {
+ .opts = "-L|--set-channels",
+ .func = do_schannels,
+ .nlfunc = nl_schannels,
+ .help = "Set Channels",
+ .xhelp = " [ rx N ]\n"
+ " [ tx N ]\n"
+ " [ other N ]\n"
+ " [ combined N ]\n"
+ },
+ {
+ .opts = "--show-priv-flags",
+ .func = do_gprivflags,
+ .nlfunc = nl_gprivflags,
+ .help = "Query private flags"
+ },
+ {
+ .opts = "--set-priv-flags",
+ .func = do_sprivflags,
+ .nlfunc = nl_sprivflags,
+ .help = "Set private flags",
+ .xhelp = " FLAG on|off ...\n"
+ },
+ {
+ .opts = "-m|--dump-module-eeprom|--module-info",
+ .func = do_getmodule,
+ .nlfunc = nl_getmodule,
+ .help = "Query/Decode Module EEPROM information and optical diagnostics if available",
+ .xhelp = " [ raw on|off ]\n"
+ " [ hex on|off ]\n"
+ " [ offset N ]\n"
+ " [ length N ]\n"
+ " [ page N ]\n"
+ " [ bank N ]\n"
+ " [ i2c N ]\n"
+ },
+ {
+ .opts = "--show-eee",
+ .func = do_geee,
+ .nlfunc = nl_geee,
+ .help = "Show EEE settings",
+ },
+ {
+ .opts = "--set-eee",
+ .func = do_seee,
+ .nlfunc = nl_seee,
+ .help = "Set EEE settings",
+ .xhelp = " [ eee on|off ]\n"
+ " [ advertise %x ]\n"
+ " [ tx-lpi on|off ]\n"
+ " [ tx-timer %d ]\n"
+ },
+ {
+ .opts = "--set-phy-tunable",
+ .func = do_set_phy_tunable,
+ .help = "Set PHY tunable",
+ .xhelp = " [ downshift on|off [count N] ]\n"
+ " [ fast-link-down on|off [msecs N] ]\n"
+ " [ energy-detect-power-down on|off [msecs N] ]\n"
+ },
+ {
+ .opts = "--get-phy-tunable",
+ .func = do_get_phy_tunable,
+ .help = "Get PHY tunable",
+ .xhelp = " [ downshift ]\n"
+ " [ fast-link-down ]\n"
+ " [ energy-detect-power-down ]\n"
+ },
+ {
+ .opts = "--get-tunable",
+ .func = do_gtunable,
+ .help = "Get tunable",
+ .xhelp = " [ rx-copybreak ]\n"
+ " [ tx-copybreak ]\n"
+ " [ tx-buf-size ]\n"
+ " [ pfc-prevention-tout ]\n"
+ },
+ {
+ .opts = "--set-tunable",
+ .func = do_stunable,
+ .help = "Set tunable",
+ .xhelp = " [ rx-copybreak N ]\n"
+ " [ tx-copybreak N ]\n"
+ " [ tx-buf-size N ]\n"
+ " [ pfc-prevention-tout N ]\n"
+ },
+ {
+ .opts = "--reset",
+ .func = do_reset,
+ .help = "Reset components",
+ .xhelp = " [ flags %x ]\n"
+ " [ mgmt ]\n"
+ " [ mgmt-shared ]\n"
+ " [ irq ]\n"
+ " [ irq-shared ]\n"
+ " [ dma ]\n"
+ " [ dma-shared ]\n"
+ " [ filter ]\n"
+ " [ filter-shared ]\n"
+ " [ offload ]\n"
+ " [ offload-shared ]\n"
+ " [ mac ]\n"
+ " [ mac-shared ]\n"
+ " [ phy ]\n"
+ " [ phy-shared ]\n"
+ " [ ram ]\n"
+ " [ ram-shared ]\n"
+ " [ ap ]\n"
+ " [ ap-shared ]\n"
+ " [ dedicated ]\n"
+ " [ all ]\n"
+ },
+ {
+ .opts = "--show-fec",
+ .json = true,
+ .func = do_gfec,
+ .nlfunc = nl_gfec,
+ .help = "Show FEC settings",
+ },
+ {
+ .opts = "--set-fec",
+ .func = do_sfec,
+ .nlfunc = nl_sfec,
+ .help = "Set FEC settings",
+ .xhelp = " [ encoding auto|off|rs|baser|llrs [...] ]\n"
+ },
+ {
+ .opts = "-Q|--per-queue",
+ .func = do_perqueue,
+ .help = "Apply per-queue command. ",
+ .xhelp = "The supported sub commands include --show-coalesce, --coalesce"
+ " [queue_mask %x] SUB_COMMAND\n",
+ },
+ {
+ .opts = "--cable-test",
+ .json = true,
+ .nlfunc = nl_cable_test,
+ .help = "Perform a cable test",
+ },
+ {
+ .opts = "--cable-test-tdr",
+ .json = true,
+ .nlfunc = nl_cable_test_tdr,
+ .help = "Print cable test time domain reflectrometery data",
+ .xhelp = " [ first N ]\n"
+ " [ last N ]\n"
+ " [ step N ]\n"
+ " [ pair N ]\n"
+ },
+ {
+ .opts = "--show-tunnels",
+ .nlfunc = nl_gtunnels,
+ .help = "Show NIC tunnel offload information",
+ },
+ {
+ .opts = "--show-module",
+ .json = true,
+ .nlfunc = nl_gmodule,
+ .help = "Show transceiver module settings",
+ },
+ {
+ .opts = "--set-module",
+ .nlfunc = nl_smodule,
+ .help = "Set transceiver module settings",
+ .xhelp = " [ power-mode-policy high|auto ]\n"
+ },
+ {
+ .opts = "--get-plca-cfg",
+ .nlfunc = nl_plca_get_cfg,
+ .help = "Get PLCA configuration",
+ },
+ {
+ .opts = "--set-plca-cfg",
+ .nlfunc = nl_plca_set_cfg,
+ .help = "Set PLCA configuration",
+ .xhelp = " [ enable on|off ]\n"
+ " [ node-id N ]\n"
+ " [ node-cnt N ]\n"
+ " [ to-tmr N ]\n"
+ " [ burst-cnt N ]\n"
+ " [ burst-tmr N ]\n"
+ },
+ {
+ .opts = "--get-plca-status",
+ .nlfunc = nl_plca_get_status,
+ .help = "Get PLCA status information",
+ },
+ {
+ .opts = "--show-mm",
+ .json = true,
+ .nlfunc = nl_get_mm,
+ .help = "Show MAC merge layer state",
+ },
+ {
+ .opts = "--set-mm",
+ .nlfunc = nl_set_mm,
+ .help = "Set MAC merge layer parameters",
+ " [ verify-enabled on|off ]\n"
+ " [ verify-time N ]\n"
+ " [ tx-enabled on|off ]\n"
+ " [ pmac-enabled on|off ]\n"
+ " [ tx-min-frag-size 60-252 ]\n"
+ },
+ {
+ .opts = "--show-pse",
+ .json = true,
+ .nlfunc = nl_gpse,
+ .help = "Show settings for Power Sourcing Equipment",
+ },
+ {
+ .opts = "--set-pse",
+ .nlfunc = nl_spse,
+ .help = "Set Power Sourcing Equipment settings",
+ .xhelp = " [ podl-pse-admin-control enable|disable ]\n"
+ },
+ {
+ .opts = "-h|--help",
+ .no_dev = true,
+ .func = show_usage,
+ .help = "Show this help"
+ },
+ {
+ .opts = "--version",
+ .no_dev = true,
+ .func = do_version,
+ .help = "Show version number"
+ },
+ {}
+};
+
+static int show_usage(struct cmd_context *ctx __maybe_unused)
+{
+ int i;
+
+ /* ethtool -h */
+ fprintf(stdout, PACKAGE " version " VERSION "\n");
+ fprintf(stdout, "Usage:\n");
+ for (i = 0; args[i].opts; i++) {
+ fputs(" ethtool [ FLAGS ] ", stdout);
+ fprintf(stdout, "%s %s\t%s\n",
+ args[i].opts,
+ args[i].no_dev ? "\t" : "DEVNAME",
+ args[i].help);
+ if (args[i].xhelp)
+ fputs(args[i].xhelp, stdout);
+ }
+ nl_monitor_usage();
+ fprintf(stdout, "\n");
+ fprintf(stdout, "FLAGS:\n");
+ fprintf(stdout, " --debug MASK turn on debugging messages\n");
+ fprintf(stdout, " --json enable JSON output format (not supported by all commands)\n");
+ fprintf(stdout, " -I|--include-statistics request device statistics related to the command (not supported by all commands)\n");
+
+ return 0;
+}
+
+static int find_option(char *arg)
+{
+ const char *opt;
+ size_t len;
+ int k;
+
+ for (k = 1; args[k].opts; k++) {
+ opt = args[k].opts;
+ for (;;) {
+ len = strcspn(opt, "|");
+ if (strncmp(arg, opt, len) == 0 && arg[len] == 0)
+ return k;
+
+ if (opt[len] == 0)
+ break;
+ opt += len + 1;
+ }
+ }
+
+ return -1;
+}
+
+#define MAX(x, y) (x > y ? x : y)
+
+static int find_max_num_queues(struct cmd_context *ctx)
+{
+ struct ethtool_channels echannels;
+
+ echannels.cmd = ETHTOOL_GCHANNELS;
+ if (send_ioctl(ctx, &echannels))
+ return -1;
+
+ return MAX(echannels.rx_count, echannels.tx_count) +
+ echannels.combined_count;
+}
+
+static struct ethtool_per_queue_op *
+get_per_queue_coalesce(struct cmd_context *ctx, __u32 *queue_mask, int n_queues)
+{
+ struct ethtool_per_queue_op *per_queue_opt;
+
+ per_queue_opt = malloc(sizeof(*per_queue_opt) + n_queues *
+ sizeof(struct ethtool_coalesce));
+ if (!per_queue_opt)
+ return NULL;
+
+ memcpy(per_queue_opt->queue_mask, queue_mask,
+ __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32) * sizeof(__u32));
+ per_queue_opt->cmd = ETHTOOL_PERQUEUE;
+ per_queue_opt->sub_command = ETHTOOL_GCOALESCE;
+ if (send_ioctl(ctx, per_queue_opt)) {
+ free(per_queue_opt);
+ perror("Cannot get device per queue parameters");
+ return NULL;
+ }
+
+ return per_queue_opt;
+}
+
+static void set_per_queue_coalesce(struct cmd_context *ctx,
+ struct ethtool_per_queue_op *per_queue_opt,
+ int n_queues)
+{
+ struct ethtool_coalesce ecoal;
+ DECLARE_COALESCE_OPTION_VARS();
+ struct cmdline_info cmdline_coalesce[] = COALESCE_CMDLINE_INFO(ecoal);
+ __u32 *queue_mask = per_queue_opt->queue_mask;
+ struct ethtool_coalesce *ecoal_q;
+ int gcoalesce_changed = 0;
+ int i, idx = 0;
+
+ parse_generic_cmdline(ctx, &gcoalesce_changed,
+ cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce));
+
+ ecoal_q = (struct ethtool_coalesce *)(per_queue_opt + 1);
+ for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
+ int queue = i * 32;
+ __u32 mask = queue_mask[i];
+
+ while (mask > 0) {
+ if (mask & 0x1) {
+ int changed = 0;
+
+ memcpy(&ecoal, ecoal_q + idx,
+ sizeof(struct ethtool_coalesce));
+ do_generic_set(cmdline_coalesce,
+ ARRAY_SIZE(cmdline_coalesce),
+ &changed);
+ if (!changed)
+ fprintf(stderr,
+ "Queue %d, no coalesce parameters changed\n",
+ queue);
+ memcpy(ecoal_q + idx, &ecoal,
+ sizeof(struct ethtool_coalesce));
+ idx++;
+ }
+ mask = mask >> 1;
+ queue++;
+ }
+ if (idx == n_queues)
+ break;
+ }
+
+ per_queue_opt->cmd = ETHTOOL_PERQUEUE;
+ per_queue_opt->sub_command = ETHTOOL_SCOALESCE;
+
+ if (send_ioctl(ctx, per_queue_opt))
+ perror("Cannot set device per queue parameters");
+}
+
+static int do_perqueue(struct cmd_context *ctx)
+{
+ struct ethtool_per_queue_op *per_queue_opt;
+ __u32 queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)] = {0};
+ int i, n_queues = 0;
+
+ if (ctx->argc == 0)
+ exit_bad_args();
+
+ /*
+ * The sub commands will be applied to
+ * all queues if no queue_mask set
+ */
+ if (strncmp(*ctx->argp, "queue_mask", 11)) {
+ n_queues = find_max_num_queues(ctx);
+ if (n_queues < 0) {
+ perror("Cannot get number of queues");
+ return -EFAULT;
+ } else if (n_queues > MAX_NUM_QUEUE) {
+ n_queues = MAX_NUM_QUEUE;
+ }
+ for (i = 0; i < n_queues / 32; i++)
+ queue_mask[i] = ~0;
+ if (n_queues % 32)
+ queue_mask[i] = (1 << (n_queues - i * 32)) - 1;
+ fprintf(stdout,
+ "The sub commands will be applied to all %d queues\n",
+ n_queues);
+ } else {
+ if (ctx->argc <= 2)
+ exit_bad_args();
+ ctx->argc--;
+ ctx->argp++;
+ if (parse_hex_u32_bitmap(*ctx->argp, MAX_NUM_QUEUE,
+ queue_mask)) {
+ fprintf(stdout, "Invalid queue mask\n");
+ return -1;
+ }
+ for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
+ __u32 mask = queue_mask[i];
+
+ while (mask > 0) {
+ if (mask & 0x1)
+ n_queues++;
+ mask = mask >> 1;
+ }
+ }
+ ctx->argc--;
+ ctx->argp++;
+ }
+
+ i = find_option(ctx->argp[0]);
+ if (i < 0)
+ exit_bad_args();
+
+ if (strstr(args[i].opts, "--show-coalesce") != NULL) {
+ per_queue_opt = get_per_queue_coalesce(ctx, queue_mask,
+ n_queues);
+ if (per_queue_opt == NULL) {
+ perror("Cannot get device per queue parameters");
+ return -EFAULT;
+ }
+ dump_per_queue_coalesce(per_queue_opt, queue_mask, n_queues);
+ free(per_queue_opt);
+ } else if (strstr(args[i].opts, "--coalesce") != NULL) {
+ ctx->argc--;
+ ctx->argp++;
+ per_queue_opt = get_per_queue_coalesce(ctx, queue_mask,
+ n_queues);
+ if (per_queue_opt == NULL) {
+ perror("Cannot get device per queue parameters");
+ return -EFAULT;
+ }
+ set_per_queue_coalesce(ctx, per_queue_opt, n_queues);
+ free(per_queue_opt);
+ } else {
+ perror("The subcommand is not supported yet");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ioctl_init(struct cmd_context *ctx, bool no_dev)
+{
+ if (no_dev) {
+ ctx->fd = -1;
+ return 0;
+ }
+ if (strlen(ctx->devname) >= IFNAMSIZ) {
+ fprintf(stderr, "Device name longer than %u characters\n",
+ IFNAMSIZ - 1);
+ exit_bad_args();
+ }
+
+ /* Setup our control structures. */
+ memset(&ctx->ifr, 0, sizeof(ctx->ifr));
+ strcpy(ctx->ifr.ifr_name, ctx->devname);
+
+ /* Open control socket. */
+ ctx->fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ctx->fd < 0)
+ ctx->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
+ if (ctx->fd < 0) {
+ perror("Cannot get control socket");
+ return 70;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argp)
+{
+ struct cmd_context ctx = {};
+ int ret;
+ int k;
+
+ init_global_link_mode_masks();
+
+ if (argc < 2)
+ exit_bad_args();
+
+ /* Skip command name */
+ argp++;
+ argc--;
+
+ while (true) {
+ if (*argp && !strcmp(*argp, "--debug")) {
+ char *eptr;
+
+ if (argc < 2)
+ exit_bad_args();
+ ctx.debug = strtoul(argp[1], &eptr, 0);
+ if (!argp[1][0] || *eptr)
+ exit_bad_args();
+
+ argp += 2;
+ argc -= 2;
+ continue;
+ }
+ if (*argp && !strcmp(*argp, "--json")) {
+ ctx.json = true;
+ argp += 1;
+ argc -= 1;
+ continue;
+ }
+ if (*argp && (!strcmp(*argp, "--include-statistics") ||
+ !strcmp(*argp, "-I"))) {
+ ctx.show_stats = true;
+ argp += 1;
+ argc -= 1;
+ continue;
+ }
+ break;
+ }
+ if (*argp && !strcmp(*argp, "--monitor")) {
+ ctx.argp = ++argp;
+ ctx.argc = --argc;
+ ret = nl_monitor(&ctx);
+ return ret ? 1 : 0;
+ }
+
+ /* First argument must be either a valid option or a device
+ * name to get settings for (which we don't expect to begin
+ * with '-').
+ */
+ if (!*argp)
+ exit_bad_args();
+
+ k = find_option(*argp);
+ if (k > 0) {
+ argp++;
+ argc--;
+ } else {
+ if ((*argp)[0] == '-')
+ exit_bad_args();
+ k = 0;
+ }
+
+ if (!args[k].no_dev) {
+ ctx.devname = *argp++;
+ argc--;
+
+ if (!ctx.devname)
+ exit_bad_args();
+ }
+ if (ctx.json && !args[k].json)
+ exit_bad_args();
+ ctx.argc = argc;
+ ctx.argp = argp;
+ netlink_run_handler(&ctx, args[k].nlchk, args[k].nlfunc, !args[k].func);
+
+ if (ctx.json) /* no IOCTL command supports JSON output */
+ exit_bad_args();
+
+ ret = ioctl_init(&ctx, args[k].no_dev);
+ if (ret)
+ return ret;
+
+ return args[k].func(&ctx);
+}
diff --git a/ethtool.spec.in b/ethtool.spec.in
new file mode 100644
index 0000000..75f9be6
--- /dev/null
+++ b/ethtool.spec.in
@@ -0,0 +1,41 @@
+Name : @PACKAGE@
+Version : @VERSION@
+Release : 1
+Group : Utilities
+
+Summary : Settings tool for Ethernet and other network devices
+
+License : GPL
+URL : https://ftp.kernel.org/pub/software/network/ethtool/
+
+Buildroot : %{_tmppath}/%{name}-%{version}
+Source : %{name}-%{version}.tar.gz
+
+
+%description
+This utility allows querying and changing settings such as speed,
+port, auto-negotiation, PCI locations and checksum offload on many
+network devices, especially Ethernet devices.
+
+%prep
+%setup -q
+
+
+%build
+CFLAGS="${RPM_OPT_FLAGS}" ./configure --prefix=%{_prefix} --mandir=%{_mandir}
+make
+
+
+%install
+make install DESTDIR=${RPM_BUILD_ROOT}
+
+
+%files
+%defattr(-,root,root)
+%{_sbindir}/ethtool
+%{_mandir}/man8/ethtool.8*
+%{_datadir}/bash-completion/completions/ethtool
+%doc AUTHORS COPYING NEWS README
+
+
+%changelog
diff --git a/fec.c b/fec.c
new file mode 100644
index 0000000..d2373d6
--- /dev/null
+++ b/fec.c
@@ -0,0 +1,220 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "internal.h"
+
+/* Macros and dump functions for the 32-bit "fec" driver registers */
+
+#define REG(_reg, _name, _val) \
+ printf("0x%.03x: %-44.44s 0x%.8x\n", _reg, _name, _val)
+
+#define FIELD(_name, _fmt, ...) \
+ printf(" %-47.47s " _fmt "\n", _name, ##__VA_ARGS__)
+
+static void fec_dump_reg_v1(int reg, u32 val)
+{
+ switch (reg) {
+ case 0x000: /* FEC_ECNTRL */
+ case 0x004: /* FEC_IEVENT */
+ case 0x008: /* FEC_IMASK */
+ case 0x00c: /* FEC_IVEC */
+ case 0x010: /* FEC_R_DES_ACTIVE_0 */
+ case 0x014: /* FEC_X_DES_ACTIVE_0 */
+ case 0x040: /* FEC_MII_DATA */
+ case 0x044: /* FEC_MII_SPEED */
+ case 0x08c: /* FEC_R_BOUND */
+ case 0x090: /* FEC_R_FSTART */
+ case 0x0a4: /* FEC_X_WMRK */
+ case 0x0ac: /* FEC_X_FSTART */
+ case 0x104: /* FEC_R_CNTRL */
+ case 0x108: /* FEC_MAX_FRM_LEN */
+ case 0x144: /* FEC_X_CNTRL */
+ case 0x3c0: /* FEC_ADDR_LOW */
+ case 0x3c4: /* FEC_ADDR_HIGH */
+ case 0x3c8: /* FEC_GRP_HASH_TABLE_HIGH */
+ case 0x3cc: /* FEC_GRP_HASH_TABLE_LOW */
+ case 0x3d0: /* FEC_R_DES_START_0 */
+ case 0x3d4: /* FEC_X_DES_START_0 */
+ case 0x3d8: /* FEC_R_BUFF_SIZE_0 */
+ REG(reg, "", val);
+ break;
+ }
+}
+
+static void fec_dump_reg_v2(int reg, u32 val)
+{
+ switch (reg) {
+ case 0x084: /* FEC_R_CNTRL */
+ REG(reg, "RCR (Receive Control Register)", val);
+ FIELD("MAX_FL (Maximum frame length)", "%u", (val & 0x07ff0000) >> 16);
+ FIELD("FCE (Flow control enable)", "%u", !!(val & 0x00000020));
+ FIELD("BC_REJ (Broadcast frame reject)", "%u", !!(val & 0x00000010));
+ FIELD("PROM (Promiscuous mode)", "%u", !!(val & 0x00000008));
+ FIELD("DRT (Disable receive on transmit)", "%u", !!(val & 0x00000002));
+ FIELD("LOOP (Internal loopback)", "%u", !!(val & 0x00000001));
+ break;
+ case 0x0c4: /* FEC_X_CNTRL */
+ REG(reg, "TCR (Transmit Control Register)", val);
+ FIELD("RFC_PAUSE (Receive frame control pause)", "%u", !!(val & 0x00000010));
+ FIELD("TFC_PAUSE (Transmit frame control pause)", "%u", !!(val & 0x00000008));
+ FIELD("FDEN (Full duplex enable)", "%u", !!(val & 0x00000004));
+ FIELD("HBC (Heartbeat control)", "%u", !!(val & 0x00000002));
+ FIELD("GTS (Graceful transmit stop)", "%u", !!(val & 0x00000001));
+ break;
+ case 0x118: /* FEC_HASH_TABLE_HIGH */
+ REG(reg, "IAUR (Individual Address Upper Register)", val);
+ FIELD("IADDR1", "0x%.16llx", (u64)((u64)val) << 32);
+ break;
+ case 0x11c: /* FEC_HASH_TABLE_LOW */
+ REG(reg, "IALR (Individual Address Lower Register)", val);
+ FIELD("IADDR2", "0x%.16x", val);
+ break;
+ case 0x120: /* FEC_GRP_HASH_TABLE_HIGH */
+ REG(reg, "GAUR (Group Address Upper Register)", val);
+ FIELD("GADDR1", "0x%.16llx", (u64)((u64)val) << 32);
+ break;
+ case 0x124: /* FEC_GRP_HASH_TABLE_LOW */
+ REG(reg, "GALR (Group Address Lower Register)", val);
+ FIELD("GADDR2", "0x%.16x", val);
+ break;
+ case 0x144: /* FEC_X_WMRK */
+ REG(reg, "TFWR (Transmit FIFO Watermark Register)", val);
+ FIELD("X_WMRK", "%s",
+ (val & 0x00000003) == 0x00000000 ? "64 bytes" :
+ (val & 0x00000003) == 0x00000002 ? "128 bytes" :
+ (val & 0x00000003) == 0x00000003 ? "192 bytes" : "?");
+ break;
+ case 0x14c: /* FEC_R_BOUND */
+ REG(reg, "FRBR (FIFO Receive Bound Register)", val);
+ FIELD("R_BOUND (Highest valid FIFO RAM address)", "0x%.2x", (val & 0x000003fc) >> 2);
+ break;
+ case 0x188: /* FEC_R_BUFF_SIZE_0 */
+ REG(reg, "EMRBR (Maximum Receive Buffer Size)", val);
+ FIELD("R_BUF_SIZE (Receive buffer size)", "%u", (val & 0x000007f0) >> 4);
+ break;
+ case 0x004: /* FEC_IEVENT */
+ case 0x008: /* FEC_IMASK */
+ case 0x010: /* FEC_R_DES_ACTIVE_0 */
+ case 0x014: /* FEC_X_DES_ACTIVE_0 */
+ case 0x024: /* FEC_ECNTRL */
+ case 0x040: /* FEC_MII_DATA */
+ case 0x044: /* FEC_MII_SPEED */
+ case 0x064: /* FEC_MIB_CTRLSTAT */
+ case 0x0e4: /* FEC_ADDR_LOW */
+ case 0x0e8: /* FEC_ADDR_HIGH */
+ case 0x0ec: /* FEC_OPD */
+ case 0x0f0: /* FEC_TXIC0 */
+ case 0x0f4: /* FEC_TXIC1 */
+ case 0x0f8: /* FEC_TXIC2 */
+ case 0x100: /* FEC_RXIC0 */
+ case 0x104: /* FEC_RXIC1 */
+ case 0x108: /* FEC_RXIC2 */
+ case 0x150: /* FEC_R_FSTART */
+ case 0x160: /* FEC_R_DES_START_1 */
+ case 0x164: /* FEC_X_DES_START_1 */
+ case 0x168: /* FEC_R_BUFF_SIZE_1 */
+ case 0x16c: /* FEC_R_DES_START_2 */
+ case 0x170: /* FEC_X_DES_START_2 */
+ case 0x174: /* FEC_R_BUFF_SIZE_2 */
+ case 0x180: /* FEC_R_DES_START_0 */
+ case 0x184: /* FEC_X_DES_START_0 */
+ case 0x190: /* FEC_R_FIFO_RSFL */
+ case 0x194: /* FEC_R_FIFO_RSEM */
+ case 0x198: /* FEC_R_FIFO_RAEM */
+ case 0x19c: /* FEC_R_FIFO_RAFL */
+ case 0x1c4: /* FEC_RACC */
+ case 0x1c8: /* FEC_RCMR_1 */
+ case 0x1cc: /* FEC_RCMR_2 */
+ case 0x1d8: /* FEC_DMA_CFG_1 */
+ case 0x1dc: /* FEC_DMA_CFG_2 */
+ case 0x1e0: /* FEC_R_DES_ACTIVE_1 */
+ case 0x1e4: /* FEC_X_DES_ACTIVE_1 */
+ case 0x1e8: /* FEC_R_DES_ACTIVE_2 */
+ case 0x1ec: /* FEC_X_DES_ACTIVE_2 */
+ case 0x1f0: /* FEC_QOS_SCHEME */
+ case 0x200: /* RMON_T_DROP */
+ case 0x204: /* RMON_T_PACKETS */
+ case 0x208: /* RMON_T_BC_PKT */
+ case 0x20c: /* RMON_T_MC_PKT */
+ case 0x210: /* RMON_T_CRC_ALIGN */
+ case 0x214: /* RMON_T_UNDERSIZE */
+ case 0x218: /* RMON_T_OVERSIZE */
+ case 0x21c: /* RMON_T_FRAG */
+ case 0x220: /* RMON_T_JAB */
+ case 0x224: /* RMON_T_COL */
+ case 0x228: /* RMON_T_P64 */
+ case 0x22c: /* RMON_T_P65TO127 */
+ case 0x230: /* RMON_T_P128TO255 */
+ case 0x234: /* RMON_T_P256TO511 */
+ case 0x238: /* RMON_T_P512TO1023 */
+ case 0x23c: /* RMON_T_P1024TO2047 */
+ case 0x240: /* RMON_T_P_GTE2048 */
+ case 0x244: /* RMON_T_OCTETS */
+ case 0x248: /* IEEE_T_DROP */
+ case 0x24c: /* IEEE_T_FRAME_OK */
+ case 0x250: /* IEEE_T_1COL */
+ case 0x254: /* IEEE_T_MCOL */
+ case 0x258: /* IEEE_T_DEF */
+ case 0x25c: /* IEEE_T_LCOL */
+ case 0x260: /* IEEE_T_EXCOL */
+ case 0x264: /* IEEE_T_MACERR */
+ case 0x268: /* IEEE_T_CSERR */
+ case 0x26c: /* IEEE_T_SQE */
+ case 0x270: /* IEEE_T_FDXFC */
+ case 0x274: /* IEEE_T_OCTETS_OK */
+ case 0x284: /* RMON_R_PACKETS */
+ case 0x288: /* RMON_R_BC_PKT */
+ case 0x28c: /* RMON_R_MC_PKT */
+ case 0x290: /* RMON_R_CRC_ALIGN */
+ case 0x294: /* RMON_R_UNDERSIZE */
+ case 0x298: /* RMON_R_OVERSIZE */
+ case 0x29c: /* RMON_R_FRAG */
+ case 0x2a0: /* RMON_R_JAB */
+ case 0x2a4: /* RMON_R_RESVD_O */
+ case 0x2a8: /* RMON_R_P64 */
+ case 0x2ac: /* RMON_R_P65TO127 */
+ case 0x2b0: /* RMON_R_P128TO255 */
+ case 0x2b4: /* RMON_R_P256TO511 */
+ case 0x2b8: /* RMON_R_P512TO1023 */
+ case 0x2bc: /* RMON_R_P1024TO2047 */
+ case 0x2c0: /* RMON_R_P_GTE2048 */
+ case 0x2c4: /* RMON_R_OCTETS */
+ case 0x2c8: /* IEEE_R_DROP */
+ case 0x2cc: /* IEEE_R_FRAME_OK */
+ case 0x2d0: /* IEEE_R_CRC */
+ case 0x2d4: /* IEEE_R_ALIGN */
+ case 0x2d8: /* IEEE_R_MACERR */
+ case 0x2dc: /* IEEE_R_FDXFC */
+ case 0x2e0: /* IEEE_R_OCTETS_OK */
+ REG(reg, "", val);
+ break;
+ }
+}
+
+#undef FIELD
+#undef REG
+
+int fec_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ const u32 *data = (u32 *)regs->data;
+ unsigned int offset;
+ u32 val;
+
+ for (offset = 0; offset < regs->len; offset += 4) {
+ val = data[offset / 4];
+
+ switch (regs->version) {
+ case 1:
+ fec_dump_reg_v1(offset, val);
+ break;
+ case 2:
+ fec_dump_reg_v2(offset, val);
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/fec_8xx.c b/fec_8xx.c
new file mode 100644
index 0000000..63352fc
--- /dev/null
+++ b/fec_8xx.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2004 Intracom S.A.
+ * Pantelis Antoniou <panto@intracom.gr>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "internal.h"
+
+struct fec {
+ uint32_t addr_low; /* lower 32 bits of station address */
+ uint32_t addr_high; /* upper 16 bits of station address||0 */
+ uint32_t hash_table_high;/* upper 32-bits of hash table */
+ uint32_t hash_table_low; /* lower 32-bits of hash table */
+ uint32_t r_des_start; /* beginning of Rx descriptor ring */
+ uint32_t x_des_start; /* beginning of Tx descriptor ring */
+ uint32_t r_buff_size; /* Rx buffer size */
+ uint32_t res2[9]; /* reserved */
+ uint32_t ecntrl; /* ethernet control register */
+ uint32_t ievent; /* interrupt event register */
+ uint32_t imask; /* interrupt mask register */
+ uint32_t ivec; /* interrupt level and vector status */
+ uint32_t r_des_active; /* Rx ring updated flag */
+ uint32_t x_des_active; /* Tx ring updated flag */
+ uint32_t res3[10]; /* reserved */
+ uint32_t mii_data; /* MII data register */
+ uint32_t mii_speed; /* MII speed control register */
+ uint32_t res4[17]; /* reserved */
+ uint32_t r_bound; /* end of RAM (read-only) */
+ uint32_t r_fstart; /* Rx FIFO start address */
+ uint32_t res5[6]; /* reserved */
+ uint32_t x_fstart; /* Tx FIFO start address */
+ uint32_t res6[17]; /* reserved */
+ uint32_t fun_code; /* fec SDMA function code */
+ uint32_t res7[3]; /* reserved */
+ uint32_t r_cntrl; /* Rx control register */
+ uint32_t r_hash; /* Rx hash register */
+ uint32_t res8[14]; /* reserved */
+ uint32_t x_cntrl; /* Tx control register */
+ uint32_t res9[0x1e]; /* reserved */
+};
+
+#define DUMP_REG(f, x) fprintf(stdout, \
+ "0x%04lx: %-16s 0x%08x\n", \
+ (unsigned long)(offsetof(struct fec, x)), \
+ #x, f->x)
+
+int fec_8xx_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ struct fec *f = (struct fec *)regs->data;
+
+ fprintf(stdout, "Descriptor Registers\n");
+ fprintf(stdout, "---------------------\n");
+
+ DUMP_REG(f, addr_low);
+ DUMP_REG(f, addr_high);
+ DUMP_REG(f, hash_table_high);
+ DUMP_REG(f, hash_table_low);
+ DUMP_REG(f, r_des_start);
+ DUMP_REG(f, x_des_start);
+ DUMP_REG(f, r_buff_size);
+ DUMP_REG(f, ecntrl);
+ DUMP_REG(f, ievent);
+ DUMP_REG(f, imask);
+ DUMP_REG(f, ivec);
+ DUMP_REG(f, r_des_active);
+ DUMP_REG(f, x_des_active);
+ DUMP_REG(f, mii_data);
+ DUMP_REG(f, mii_speed);
+ DUMP_REG(f, r_bound);
+ DUMP_REG(f, r_fstart);
+ DUMP_REG(f, x_fstart);
+ DUMP_REG(f, fun_code);
+ DUMP_REG(f, r_cntrl);
+ DUMP_REG(f, r_hash);
+ DUMP_REG(f, x_cntrl);
+
+ return 0;
+}
diff --git a/fjes.c b/fjes.c
new file mode 100644
index 0000000..05bd245
--- /dev/null
+++ b/fjes.c
@@ -0,0 +1,90 @@
+/* Copyright (c) 2016 FUJITSU LIMITED */
+#include <stdio.h>
+#include "internal.h"
+
+int fjes_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *regs_buff = (u32 *)regs->data;
+
+ if (regs->version != 1)
+ return -1;
+
+ /* Information registers */
+ fprintf(stdout,
+ "0x0000: OWNER_EPID (Owner EPID) 0x%08X\n",
+ regs_buff[0]);
+
+ fprintf(stdout,
+ "0x0004: MAX_EP (Maximum EP) 0x%08X\n",
+ regs_buff[1]);
+
+ /* Device Control registers */
+ fprintf(stdout,
+ "0x0010: DCTL (Device Control) 0x%08X\n",
+ regs_buff[4]);
+
+ /* Command Control registers */
+ fprintf(stdout,
+ "0x0020: CR (Command request) 0x%08X\n",
+ regs_buff[8]);
+
+ fprintf(stdout,
+ "0x0024: CS (Command status) 0x%08X\n",
+ regs_buff[9]);
+
+ fprintf(stdout,
+ "0x0028: SHSTSAL (Share status address Low) 0x%08X\n",
+ regs_buff[10]);
+
+ fprintf(stdout,
+ "0x002C: SHSTSAH (Share status address High) 0x%08X\n",
+ regs_buff[11]);
+
+ fprintf(stdout,
+ "0x0034: REQBL (Request Buffer length) 0x%08X\n",
+ regs_buff[13]);
+
+ fprintf(stdout,
+ "0x0038: REQBAL (Request Buffer Address Low) 0x%08X\n",
+ regs_buff[14]);
+
+ fprintf(stdout,
+ "0x003C: REQBAH (Request Buffer Address High) 0x%08X\n",
+ regs_buff[15]);
+
+ fprintf(stdout,
+ "0x0044: RESPBL (Response Buffer Length) 0x%08X\n",
+ regs_buff[17]);
+
+ fprintf(stdout,
+ "0x0048: RESPBAL (Response Buffer Address Low) 0x%08X\n",
+ regs_buff[18]);
+
+ fprintf(stdout,
+ "0x004C: RESPBAH (Response Buffer Address High) 0x%08X\n",
+ regs_buff[19]);
+
+ /* Interrupt Control registers */
+ fprintf(stdout,
+ "0x0080: IS (Interrupt status) 0x%08X\n",
+ regs_buff[32]);
+
+ fprintf(stdout,
+ "0x0084: IMS (Interrupt mask set) 0x%08X\n",
+ regs_buff[33]);
+
+ fprintf(stdout,
+ "0x0088: IMC (Interrupt mask clear) 0x%08X\n",
+ regs_buff[34]);
+
+ fprintf(stdout,
+ "0x008C: IG (Interrupt generator) 0x%08X\n",
+ regs_buff[35]);
+
+ fprintf(stdout,
+ "0x0090: ICTL (Interrupt control) 0x%08X\n",
+ regs_buff[36]);
+
+ return 0;
+}
diff --git a/fsl_enetc.c b/fsl_enetc.c
new file mode 100644
index 0000000..1180a66
--- /dev/null
+++ b/fsl_enetc.c
@@ -0,0 +1,259 @@
+/* Code to dump registers for the Freescale/NXP ENETC controller.
+ *
+ * Copyright 2022 NXP
+ */
+#include <stdio.h>
+#include "internal.h"
+
+#define BIT(x) (1U << (x))
+
+enum enetc_bdr_type {TX, RX};
+#define ENETC_SIMR 0
+#define ENETC_SIPMAR0 0x80
+#define ENETC_SIPMAR1 0x84
+#define ENETC_SICBDRMR 0x800
+#define ENETC_SICBDRSR 0x804
+#define ENETC_SICBDRBAR0 0x810
+#define ENETC_SICBDRBAR1 0x814
+#define ENETC_SICBDRPIR 0x818
+#define ENETC_SICBDRCIR 0x81c
+#define ENETC_SICBDRLENR 0x820
+#define ENETC_SICAPR0 0x900
+#define ENETC_SICAPR1 0x904
+#define ENETC_SIUEFDCR 0xe28
+
+#define ENETC_BDR_OFF(i) ((i) * 0x200)
+#define ENETC_BDR(t, i, r) (0x8000 + (t) * 0x100 + ENETC_BDR_OFF(i) + (r))
+
+/* RX BDR reg offsets */
+#define ENETC_RBMR 0
+#define ENETC_RBSR 0x4
+#define ENETC_RBBSR 0x8
+#define ENETC_RBCIR 0xc
+#define ENETC_RBBAR0 0x10
+#define ENETC_RBBAR1 0x14
+#define ENETC_RBPIR 0x18
+#define ENETC_RBLENR 0x20
+#define ENETC_RBIER 0xa0
+#define ENETC_RBICR0 0xa8
+#define ENETC_RBICR1 0xac
+
+/* TX BDR reg offsets */
+#define ENETC_TBMR 0
+#define ENETC_TBSR 0x4
+#define ENETC_TBBAR0 0x10
+#define ENETC_TBBAR1 0x14
+#define ENETC_TBPIR 0x18
+#define ENETC_TBCIR 0x1c
+#define ENETC_TBLENR 0x20
+#define ENETC_TBIER 0xa0
+#define ENETC_TBIDR 0xa4
+#define ENETC_TBICR0 0xa8
+#define ENETC_TBICR1 0xac
+
+/* Port registers */
+#define ENETC_PORT_BASE 0x10000
+#define ENETC_PMR ENETC_PORT_BASE + 0x0000
+#define ENETC_PSR ENETC_PORT_BASE + 0x0004
+#define ENETC_PSIPMR ENETC_PORT_BASE + 0x0018
+#define ENETC_PSIPMAR0(n) ENETC_PORT_BASE + (0x0100 + (n) * 0x8) /* n = SI index */
+#define ENETC_PSIPMAR1(n) ENETC_PORT_BASE + (0x0104 + (n) * 0x8)
+#define ENETC_PTXMBAR ENETC_PORT_BASE + 0x0608
+#define ENETC_PCAPR0 ENETC_PORT_BASE + 0x0900
+#define ENETC_PCAPR1 ENETC_PORT_BASE + 0x0904
+#define ENETC_PSICFGR0(n) ENETC_PORT_BASE + (0x0940 + (n) * 0xc) /* n = SI index */
+
+#define ENETC_PRFSCAPR ENETC_PORT_BASE + 0x1804
+#define ENETC_PTCMSDUR(n) ENETC_PORT_BASE + (0x2020 + (n) * 4) /* n = TC index [0..7] */
+
+#define ENETC_PM0_CMD_CFG ENETC_PORT_BASE + 0x8008
+#define ENETC_PM0_CMD_TX_EN BIT(0)
+#define ENETC_PM0_CMD_RX_EN BIT(1)
+#define ENETC_PM0_CMD_WAN BIT(3)
+#define ENETC_PM0_CMD_PROMISC BIT(4)
+#define ENETC_PM0_CMD_PAD BIT(5)
+#define ENETC_PM0_CMD_CRC BIT(6)
+#define ENETC_PM0_CMD_PAUSE_FWD BIT(7)
+#define ENETC_PM0_CMD_PAUSE_IGN BIT(8)
+#define ENETC_PM0_CMD_TX_ADDR_INS BIT(9)
+#define ENETC_PM0_CMD_XGLP BIT(10)
+#define ENETC_PM0_CMD_TXP BIT(11)
+#define ENETC_PM0_CMD_SWR BIT(12)
+#define ENETC_PM0_CMD_CNT_FRM_EN BIT(13)
+#define ENETC_PM0_CMD_SEND_IDLE BIT(16)
+#define ENETC_PM0_CMD_NO_LEN_CHK BIT(17)
+#define ENETC_PM0_CMD_SFD BIT(21)
+#define ENETC_PM0_CMD_TX_LOWP_ENA BIT(23)
+#define ENETC_PM0_CMD_REG_LOWP_RXETY BIT(24)
+#define ENETC_PM0_CMD_RXSTP BIT(29)
+#define ENETC_PM0_CMD_MG BIT(31)
+
+#define ENETC_PM0_MAXFRM ENETC_PORT_BASE + 0x8014
+#define ENETC_PM0_IF_MODE ENETC_PORT_BASE + 0x8300
+
+struct enetc_register {
+ u32 addr;
+ const char *name;
+ void (*decode)(u32 val, char *buf);
+};
+
+#define REG(_reg, _name) { .addr = (_reg), .name = (_name) }
+
+#define REG_DEC(_reg, _name, _decode) \
+ { .addr = (_reg), .name = (_name), .decode = (_decode) }
+
+static void decode_cmd_cfg(u32 val, char *buf)
+{
+ sprintf(buf, "\tMG %d\n\tRXSTP %d\n\tREG_LOWP_RXETY %d\n"
+ "\tTX_LOWP_ENA %d\n\tSFD %d\n\tNO_LEN_CHK %d\n\tSEND_IDLE %d\n"
+ "\tCNT_FRM_EN %d\n\tSWR %d\n\tTXP %d\n\tXGLP %d\n"
+ "\tTX_ADDR_INS %d\n\tPAUSE_IGN %d\n\tPAUSE_FWD %d\n\tCRC %d\n"
+ "\tPAD %d\n\tPROMIS %d\n\tWAN %d\n\tRX_EN %d\n\tTX_EN %d\n",
+ !!(val & ENETC_PM0_CMD_MG),
+ !!(val & ENETC_PM0_CMD_RXSTP),
+ !!(val & ENETC_PM0_CMD_REG_LOWP_RXETY),
+ !!(val & ENETC_PM0_CMD_TX_LOWP_ENA),
+ !!(val & ENETC_PM0_CMD_SFD),
+ !!(val & ENETC_PM0_CMD_NO_LEN_CHK),
+ !!(val & ENETC_PM0_CMD_SEND_IDLE),
+ !!(val & ENETC_PM0_CMD_CNT_FRM_EN),
+ !!(val & ENETC_PM0_CMD_SWR),
+ !!(val & ENETC_PM0_CMD_TXP),
+ !!(val & ENETC_PM0_CMD_XGLP),
+ !!(val & ENETC_PM0_CMD_TX_ADDR_INS),
+ !!(val & ENETC_PM0_CMD_PAUSE_IGN),
+ !!(val & ENETC_PM0_CMD_PAUSE_FWD),
+ !!(val & ENETC_PM0_CMD_CRC),
+ !!(val & ENETC_PM0_CMD_PAD),
+ !!(val & ENETC_PM0_CMD_PROMISC),
+ !!(val & ENETC_PM0_CMD_WAN),
+ !!(val & ENETC_PM0_CMD_RX_EN),
+ !!(val & ENETC_PM0_CMD_TX_EN));
+}
+
+#define RXBDR_REGS(_i) \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBMR), "RX BDR " #_i " mode register"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBSR), "RX BDR " #_i " status register"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBBSR), "RX BDR " #_i " buffer size register"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBPIR), "RX BDR " #_i " producer index register"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBCIR), "RX BDR " #_i " consumer index register"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBBAR0), "RX BDR " #_i " base address register 0"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBBAR1), "RX BDR " #_i " base address register 1"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBLENR), "RX BDR " #_i " length register"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBIER), "RX BDR " #_i " interrupt enable register"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBICR0), "RX BDR " #_i " interrupt coalescing register 0"), \
+ REG(ENETC_BDR(RX, (_i), ENETC_RBICR1), "RX BDR " #_i " interrupt coalescing register 1")
+
+#define TXBDR_REGS(_i) \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBMR), "TX BDR " #_i " mode register"), \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBSR), "TX BDR " #_i " status register"), \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBBAR0), "TX BDR " #_i " base address register 0"), \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBBAR1), "TX BDR " #_i " base address register 1"), \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBPIR), "TX BDR " #_i " producer index register"), \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBCIR), "TX BDR " #_i " consumer index register"), \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBLENR), "TX BDR " #_i " length register"), \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBIER), "TX BDR " #_i " interrupt enable register"), \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBICR0), "TX BDR " #_i " interrupt coalescing register 0"), \
+ REG(ENETC_BDR(TX, (_i), ENETC_TBICR1), "TX BDR " #_i " interrupt coalescing register 1")
+
+static const struct enetc_register known_enetc_regs[] = {
+ REG(ENETC_SIMR, "SI mode register"),
+ REG(ENETC_SIPMAR0, "SI primary MAC address register 0"),
+ REG(ENETC_SIPMAR1, "SI primary MAC address register 1"),
+ REG(ENETC_SICBDRMR, "SI control BDR mode register"),
+ REG(ENETC_SICBDRSR, "SI control BDR status register"),
+ REG(ENETC_SICBDRBAR0, "SI control BDR base address register 0"),
+ REG(ENETC_SICBDRBAR1, "SI control BDR base address register 1"),
+ REG(ENETC_SICBDRPIR, "SI control BDR producer index register"),
+ REG(ENETC_SICBDRCIR, "SI control BDR consumer index register"),
+ REG(ENETC_SICBDRLENR, "SI control BDR length register"),
+ REG(ENETC_SICAPR0, "SI capability register 0"),
+ REG(ENETC_SICAPR1, "SI capability register 1"),
+ REG(ENETC_SIUEFDCR, "SI uncorrectable error frame drop count register"),
+
+ TXBDR_REGS(0), TXBDR_REGS(1), TXBDR_REGS(2), TXBDR_REGS(3),
+ TXBDR_REGS(4), TXBDR_REGS(5), TXBDR_REGS(6), TXBDR_REGS(7),
+ TXBDR_REGS(8), TXBDR_REGS(9), TXBDR_REGS(10), TXBDR_REGS(11),
+ TXBDR_REGS(12), TXBDR_REGS(13), TXBDR_REGS(14), TXBDR_REGS(15),
+
+ RXBDR_REGS(0), RXBDR_REGS(1), RXBDR_REGS(2), RXBDR_REGS(3),
+ RXBDR_REGS(4), RXBDR_REGS(5), RXBDR_REGS(6), RXBDR_REGS(7),
+ RXBDR_REGS(8), RXBDR_REGS(9), RXBDR_REGS(10), RXBDR_REGS(11),
+ RXBDR_REGS(12), RXBDR_REGS(13), RXBDR_REGS(14), RXBDR_REGS(15),
+
+ REG(ENETC_PMR, "Port mode register"),
+ REG(ENETC_PSR, "Port status register"),
+ REG(ENETC_PSIPMR, "Port SI promiscuous mode register"),
+ REG(ENETC_PSIPMAR0(0), "Port SI0 primary MAC address register 0"),
+ REG(ENETC_PSIPMAR1(0), "Port SI0 primary MAC address register 1"),
+ REG(ENETC_PTXMBAR, "Port HTA transmit memory buffer allocation register"),
+ REG(ENETC_PCAPR0, "Port capability register 0"),
+ REG(ENETC_PCAPR1, "Port capability register 1"),
+ REG(ENETC_PSICFGR0(0), "Port SI0 configuration register 0"),
+ REG(ENETC_PRFSCAPR, "Port RFS capability register"),
+ REG(ENETC_PTCMSDUR(0), "Port traffic class 0 maximum SDU register"),
+ REG_DEC(ENETC_PM0_CMD_CFG, "Port eMAC Command and Configuration Register",
+ decode_cmd_cfg),
+ REG(ENETC_PM0_MAXFRM, "Port eMAC Maximum Frame Length Register"),
+ REG(ENETC_PM0_IF_MODE, "Port eMAC Interface Mode Control Register"),
+};
+
+static void decode_known_reg(const struct enetc_register *reg, u32 val)
+{
+ char buf[512];
+
+ reg->decode(val, buf);
+ fprintf(stdout, "%s: 0x%x\n%s", reg->name, val, buf);
+}
+
+static void dump_known_reg(const struct enetc_register *reg, u32 val)
+{
+ fprintf(stdout, "%s: 0x%x\n", reg->name, val);
+}
+
+static void dump_unknown_reg(u32 addr, u32 val)
+{
+ fprintf(stdout, "Reg 0x%x: 0x%x\n", addr, val);
+}
+
+static void dump_reg(u32 addr, u32 val)
+{
+ const struct enetc_register *reg;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(known_enetc_regs); i++) {
+ reg = &known_enetc_regs[i];
+ if (reg->addr == addr) {
+ if (reg->decode)
+ decode_known_reg(reg, val);
+ else
+ dump_known_reg(reg, val);
+ return;
+ }
+ }
+
+ dump_unknown_reg(addr, val);
+}
+
+/* Registers are structured in an array of key/value u32 pairs.
+ * Compare each key to our list of known registers, or print it
+ * as a raw address otherwise.
+ */
+int fsl_enetc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *data = (u32 *)regs->data;
+ u32 len = regs->len;
+
+ if (len % 8) {
+ fprintf(stdout, "Expected length to be multiple of 8 bytes\n");
+ return -1;
+ }
+
+ while (len) {
+ dump_reg(data[0], data[1]);
+ data += 2; len -= 8;
+ }
+
+ return 0;
+}
diff --git a/hns3.c b/hns3.c
new file mode 100644
index 0000000..7199426
--- /dev/null
+++ b/hns3.c
@@ -0,0 +1,829 @@
+/* Copyright (c) 2023 Huawei Corporation */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "internal.h"
+
+/* distinguish drive register data of earlier versions */
+#define HNS3_REG_MAGIC_NUMBER 0x686e733372656773 /* hns3regs */
+#define HNS3_REG_RSV_NAME "reserved"
+#define HNS3_REG_UNKNOW_NAME "unknown"
+#define HNS3_REG_UNKNOW_VALUE_LEN 4
+
+struct hns3_reg_tlv {
+ u16 tag;
+ u16 len;
+};
+
+struct hns3_reg_header {
+ u64 magic_number;
+ u8 is_vf;
+ u8 rsv[7];
+};
+
+struct hns3_reg_info {
+ const char *name;
+ u16 value_len;
+};
+
+struct hns3_regs_group {
+ const char *group_name;
+ const struct hns3_reg_info *regs;
+ u16 regs_count;
+};
+
+enum hns3_reg_tag {
+ HNS3_TAG_CMDQ = 0,
+ HNS3_TAG_COMMON,
+ HNS3_TAG_RING,
+ HNS3_TAG_TQP_INTR,
+ HNS3_TAG_QUERY_32_BIT,
+ HNS3_TAG_QUERY_64_BIT,
+ HNS3_TAG_DFX_BIOS_COMMON,
+ HNS3_TAG_DFX_SSU_0,
+ HNS3_TAG_DFX_SSU_1,
+ HNS3_TAG_DFX_IGU_EGU,
+ HNS3_TAG_DFX_RPU_0,
+ HNS3_TAG_DFX_RPU_1,
+ HNS3_TAG_DFX_NCSI,
+ HNS3_TAG_DFX_RTC,
+ HNS3_TAG_DFX_PPP,
+ HNS3_TAG_DFX_RCB,
+ HNS3_TAG_DFX_TQP,
+ HNS3_TAG_DFX_SSU_2,
+ HNS3_TAG_DFX_RPU_TNL,
+ HNS3_TAG_MAX,
+};
+
+const bool hns3_reg_is_repeat_tag_array[] = {
+ [HNS3_TAG_RING] = true,
+ [HNS3_TAG_TQP_INTR] = true,
+ [HNS3_TAG_DFX_RPU_TNL] = true,
+};
+
+const struct hns3_reg_info pf_cmdq_regs[] = {
+ {"comm_nic_csq_baseaddr_l", 4},
+ {"comm_nic_csq_baseaddr_h", 4},
+ {"comm_nic_csq_depth", 4},
+ {"comm_nic_csq_tail", 4},
+ {"comm_nic_csq_head", 4},
+ {"comm_nic_crq_baseaddr_l", 4},
+ {"comm_nic_crq_baseaddr_h", 4},
+ {"comm_nic_crq_depth", 4},
+ {"comm_nic_crq_tail", 4},
+ {"comm_nic_crq_head", 4},
+ {"comm_vector0_cmdq_src", 4},
+ {"comm_cmdq_intr_sts", 4},
+ {"comm_cmdq_intr_en", 4},
+ {"comm_cmdq_intr_gen", 4},
+};
+
+const struct hns3_reg_info pf_common_regs[] = {
+ {"misc_vector_base", 4},
+ {"pf_other_int", 4},
+ {"misc_reset_sts", 4},
+ {"misc_vector_int_sts", 4},
+ {"global_reset", 4},
+ {"fun_rst_ing", 4},
+ {"gro_en", 4},
+};
+
+const struct hns3_reg_info pf_ring_regs[] = {
+ {"ring_rx_addr_l", 4},
+ {"ring_rx_addr_h", 4},
+ {"ring_rx_bd_num", 4},
+ {"ring_rx_bd_length", 4},
+ {"ring_rx_merge_en", 4},
+ {"ring_rx_tail", 4},
+ {"ring_rx_head", 4},
+ {"ring_rx_fbd_num", 4},
+ {"ring_rx_offset", 4},
+ {"ring_rx_fbd_offset", 4},
+ {"ring_rx_stash", 4},
+ {"ring_rx_bd_err", 4},
+ {"ring_tx_addr_l", 4},
+ {"ring_tx_addr_h", 4},
+ {"ring_tx_bd_num", 4},
+ {"ring_tx_priority", 4},
+ {"ring_tx_tc", 4},
+ {"ring_tx_merge_en", 4},
+ {"ring_tx_tail", 4},
+ {"ring_tx_head", 4},
+ {"ring_tx_fbd_num", 4},
+ {"ring_tx_offset", 4},
+ {"ring_tx_ebd_num", 4},
+ {"ring_tx_ebd_offset", 4},
+ {"ring_tx_bd_err", 4},
+ {"ring_en", 4},
+};
+
+const struct hns3_reg_info pf_tqp_intr_regs[] = {
+ {"tqp_intr_ctrl", 4},
+ {"tqp_intr_gl0", 4},
+ {"tqp_intr_gl1", 4},
+ {"tqp_intr_gl2", 4},
+ {"tqp_intr_rl", 4},
+};
+
+const struct hns3_reg_info query_32_bit_regs[] = {
+ {"ssu_common_err_int", 4},
+ {"ssu_port_based_err_int", 4},
+ {"ssu_fifo_overflow_int", 4},
+ {"ssu_ets_tcg_int", 4},
+ {"ssu_bp_status_0", 4},
+ {"ssu_bp_status_1", 4},
+ {"ssu_bp_status_2", 4},
+ {"ssu_bp_status_3", 4},
+ {"ssu_bp_status_4", 4},
+ {"ssu_bp_status_5", 4},
+ {"ssu_mac_tx_pfc_ind", 4},
+ {"ssu_mac_rx_pfc_ind", 4},
+ {"ssu_rx_oq_drop_pkt_cnt", 4},
+ {"ssu_tx_oq_drop_pkt_cnt", 4},
+};
+
+const struct hns3_reg_info query_64_bit_regs[] = {
+ {"ppp_get_rx_pkt_cnt", 8},
+ {"ppp_get_tx_pkt_cnt", 8},
+ {"ppp_send_uc_prt2host_pkt_cnt", 8},
+ {"ppp_send_uc_prt2prt_pkt_cnt", 8},
+ {"ppp_send_uc_host2host_pkt_cnt", 8},
+ {"ppp_send_uc_host2prt_pkt_cnt", 8},
+ {"ppp_send_mc_from_prt_cnt", 8},
+};
+
+const struct hns3_reg_info dfx_bios_common_regs[] = {
+ {HNS3_REG_RSV_NAME, 4},
+ {"bp_cpu_state", 4},
+ {"dfx_msix_info_nic_0", 4},
+ {"dfx_msix_info_nic_1", 4},
+ {"dfx_msix_info_nic_2", 4},
+ {"dfx_msix_info_nic_3", 4},
+ {"dfx_msix_info_roc_0", 4},
+ {"dfx_msix_info_roc_1", 4},
+ {"dfx_msix_info_roc_2", 4},
+ {"dfx_msix_info_roc_3", 4},
+ {HNS3_REG_RSV_NAME, 8},
+};
+
+const struct hns3_reg_info dfx_ssu_0_regs[] = {
+ {HNS3_REG_RSV_NAME, 4},
+ {"ssu_ets_port_status", 4},
+ {"ssu_ets_tcg_status", 4},
+ {HNS3_REG_RSV_NAME, 4},
+ {HNS3_REG_RSV_NAME, 4},
+ {"ssu_bp_status_0", 4},
+ {"ssu_bp_status_1", 4},
+ {"ssu_bp_status_2", 4},
+ {"ssu_bp_status_3", 4},
+ {"ssu_bp_status_4", 4},
+ {"ssu_bp_status_5", 4},
+ {"ssu_mac_tx_pfc_ind", 4},
+ {"mac_ssu_rx_pfc_ind", 4},
+ {"btmp_ageing_st_b0", 4},
+ {"btmp_ageing_st_b1", 4},
+ {"btmp_ageing_st_b2", 4},
+ {HNS3_REG_RSV_NAME, 8},
+ {"full_drop_num", 4},
+ {"part_drop_num", 4},
+ {"ppp_key_drop_num", 4},
+ {"ppp_rlt_drop_num", 4},
+ {"lo_pri_unicast_rlt_drop_num", 4},
+ {"hi_pri_multicast_rlt_drop_num", 4},
+ {"lo_pri_multicast_rlt_drop_num", 4},
+ {"ncsi_packet_curr_buffer_cnt", 4},
+ {HNS3_REG_RSV_NAME, 12},
+ {"ssu_mb_rd_rlt_drop_cnt", 4},
+ {"ssu_ppp_mac_key_num", 8},
+ {"ssu_ppp_host_key_num", 8},
+ {"ppp_ssu_mac_rlt_num", 8},
+ {"ppp_ssu_host_rlt_num", 8},
+ {"ncsi_rx_packet_in_cnt", 8},
+ {"ncsi_tx_packet_out_cnt", 8},
+ {"ssu_key_drop_num", 4},
+ {"mb_uncopy_num", 4},
+ {"rx_oq_drop_pkt_cnt", 4},
+ {"tx_oq_drop_pkt_cnt", 4},
+ {"bank_unbalance_drop_cnt", 4},
+ {"bank_unbalance_rx_drop_cnt", 4},
+ {"nic_l2_err_drop_pkt_cnt", 4},
+ {"roc_l2_err_drop_pkt_cnt", 4},
+ {"nic_l2_err_drop_pkt_cnt_rx", 4},
+ {"roc_l2_err_drop_pkt_cnt_rx", 4},
+ {"rx_oq_glb_drop_pkt_cnt", 4},
+ {HNS3_REG_RSV_NAME, 4},
+ {"lo_pri_unicast_cur_cnt", 4},
+ {"hi_pri_multicast_cur_cnt", 4},
+ {"lo_pri_multicast_cur_cnt", 4},
+ {HNS3_REG_RSV_NAME, 12},
+};
+
+const struct hns3_reg_info dfx_ssu_1_regs[] = {
+ {"prt_id", 4},
+ {"packet_tc_curr_buffer_cnt_0", 4},
+ {"packet_tc_curr_buffer_cnt_1", 4},
+ {"packet_tc_curr_buffer_cnt_2", 4},
+ {"packet_tc_curr_buffer_cnt_3", 4},
+ {"packet_tc_curr_buffer_cnt_4", 4},
+ {"packet_tc_curr_buffer_cnt_5", 4},
+ {"packet_tc_curr_buffer_cnt_6", 4},
+ {"packet_tc_curr_buffer_cnt_7", 4},
+ {"packet_curr_buffer_cnt", 4},
+ {HNS3_REG_RSV_NAME, 8},
+ {"rx_packet_in_cnt", 8},
+ {"rx_packet_out_cnt", 8},
+ {"tx_packet_in_cnt", 8},
+ {"tx_packet_out_cnt", 8},
+ {"roc_rx_packet_in_cnt", 8},
+ {"roc_tx_packet_out_cnt", 8},
+ {"rx_packet_tc_in_cnt_0", 8},
+ {"rx_packet_tc_in_cnt_1", 8},
+ {"rx_packet_tc_in_cnt_2", 8},
+ {"rx_packet_tc_in_cnt_3", 8},
+ {"rx_packet_tc_in_cnt_4", 8},
+ {"rx_packet_tc_in_cnt_5", 8},
+ {"rx_packet_tc_in_cnt_6", 8},
+ {"rx_packet_tc_in_cnt_7", 8},
+ {"rx_packet_tc_out_cnt_0", 8},
+ {"rx_packet_tc_out_cnt_1", 8},
+ {"rx_packet_tc_out_cnt_2", 8},
+ {"rx_packet_tc_out_cnt_3", 8},
+ {"rx_packet_tc_out_cnt_4", 8},
+ {"rx_packet_tc_out_cnt_5", 8},
+ {"rx_packet_tc_out_cnt_6", 8},
+ {"rx_packet_tc_out_cnt_7", 8},
+ {"tx_packet_tc_in_cnt_0", 8},
+ {"tx_packet_tc_in_cnt_1", 8},
+ {"tx_packet_tc_in_cnt_2", 8},
+ {"tx_packet_tc_in_cnt_3", 8},
+ {"tx_packet_tc_in_cnt_4", 8},
+ {"tx_packet_tc_in_cnt_5", 8},
+ {"tx_packet_tc_in_cnt_6", 8},
+ {"tx_packet_tc_in_cnt_7", 8},
+ {"tx_packet_tc_out_cnt_0", 8},
+ {"tx_packet_tc_out_cnt_1", 8},
+ {"tx_packet_tc_out_cnt_2", 8},
+ {"tx_packet_tc_out_cnt_3", 8},
+ {"tx_packet_tc_out_cnt_4", 8},
+ {"tx_packet_tc_out_cnt_5", 8},
+ {"tx_packet_tc_out_cnt_6", 8},
+ {"tx_packet_tc_out_cnt_7", 8},
+ {HNS3_REG_RSV_NAME, 8},
+};
+
+const struct hns3_reg_info dfx_igu_egu_regs[] = {
+ {"prt_id", 4},
+ {"igu_rx_err_pkt", 4},
+ {"igu_rx_no_sof_pkt", 4},
+ {"egu_tx_1588_short_pkt", 4},
+ {"egu_tx_1588_pkt", 4},
+ {"egu_tx_err_pkt", 4},
+ {"igu_rx_out_l2_pkt", 4},
+ {"igu_rx_out_l3_pkt", 4},
+ {"igu_rx_out_l4_pkt", 4},
+ {"igu_rx_in_l2_pkt", 4},
+ {"igu_rx_in_l3_pkt", 4},
+ {"igu_rx_in_l4_pkt", 4},
+ {"igu_rx_el3e_pkt", 4},
+ {"igu_rx_el4e_pkt", 4},
+ {"igu_rx_l3e_pkt", 4},
+ {"igu_rx_l4e_pkt", 4},
+ {"igu_rx_rocee_pkt", 4},
+ {"igu_rx_out_udp0_pkt", 4},
+ {"igu_rx_in_udp0_pkt", 4},
+ {"mul_car_drop_pkt_cnt", 8},
+ {"bro_car_drop_pkt_cnt", 8},
+ {HNS3_REG_RSV_NAME, 4},
+ {"igu_rx_oversize_pkt", 8},
+ {"igu_rx_undersize_pkt", 8},
+ {"igu_rx_out_all_pkt", 8},
+ {"igu_tx_out_all_pkt", 8},
+ {"igu_rx_uni_pkt", 8},
+ {"igu_rx_multi_pkt", 8},
+ {"igu_rx_broad_pkt", 8},
+ {"egu_tx_out_all_pkt", 8},
+ {"egu_tx_uni_pkt", 8},
+ {"egu_tx_multi_pkt", 8},
+ {"egu_tx_broad_pkt", 8},
+ {"igu_tx_key_num", 8},
+ {"igu_rx_non_tun_pkt", 8},
+ {"igu_rx_tun_pkt", 8},
+ {HNS3_REG_RSV_NAME, 8},
+};
+
+const struct hns3_reg_info dfx_rpu_0_regs[] = {
+ {HNS3_REG_RSV_NAME, 4},
+ {"fsm_dfx_st0", 4},
+ {"fsm_dfx_st1", 4},
+ {"rpu_rx_pkt_drop_cnt", 4},
+ {"buf_wait_timeout", 4},
+ {"buf_wait_timeout_qid", 4},
+};
+
+const struct hns3_reg_info dfx_rpu_1_regs[] = {
+ {HNS3_REG_RSV_NAME, 4},
+ {"fifo_dfx_st0", 4},
+ {"fifo_dfx_st1", 4},
+ {"fifo_dfx_st2", 4},
+ {"fifo_dfx_st3", 4},
+ {"fifo_dfx_st4", 4},
+ {"fifo_dfx_st5", 4},
+ {HNS3_REG_RSV_NAME, 20},
+};
+
+const struct hns3_reg_info dfx_ncsi_regs[] = {
+ {HNS3_REG_RSV_NAME, 4},
+ {"ncsi_egu_tx_fifo_sts", 4},
+ {"ncsi_pause_status", 4},
+ {"ncsi_rx_ctrl_dmac_err_cnt", 4},
+ {"ncsi_rx_ctrl_smac_err_cnt", 4},
+ {"ncsi_rx_ctrl_cks_err_cnt", 4},
+ {"ncsi_rx_ctrl_pkt_cnt", 4},
+ {"ncsi_rx_pt_dmac_err_cnt", 4},
+ {"ncsi_rx_pt_smac_err_cnt", 4},
+ {"ncsi_rx_pt_pkt_cnt", 4},
+ {"ncsi_rx_fcs_err_cnt", 4},
+ {"ncsi_tx_ctrl_dmac_err_cnt", 4},
+ {"ncsi_tx_ctrl_smac_err_cnt", 4},
+ {"ncsi_tx_ctrl_pkt_cnt", 4},
+ {"ncsi_tx_pt_dmac_err_cnt", 4},
+ {"ncsi_tx_pt_smac_err_cnt", 4},
+ {"ncsi_tx_pt_pkt_cnt", 4},
+ {"ncsi_tx_pt_pkt_trun_cnt", 4},
+ {"ncsi_tx_pt_pkt_err_cnt", 4},
+ {"ncsi_tx_ctrl_pkt_err_cnt", 4},
+ {"ncsi_rx_ctrl_pkt_trun_cnt", 4},
+ {"ncsi_rx_ctrl_pkt_cflit_cnt", 4},
+ {HNS3_REG_RSV_NAME, 8},
+ {"ncsi_mac_rx_octets_ok", 4},
+ {"ncsi_mac_rx_octets_bad", 4},
+ {"ncsi_mac_rx_uc_pkts", 4},
+ {"ncsi_mac_rx_mc_pkts", 4},
+ {"ncsi_mac_rx_bc_pkts", 4},
+ {"ncsi_mac_rx_pkts_64octets", 4},
+ {"ncsi_mac_rx_pkts_65to127octets", 4},
+ {"ncsi_mac_rx_pkts_128to255octets", 4},
+ {"ncsi_mac_rx_pkts_256to511octets", 4},
+ {"ncsi_mac_rx_pkts_512to1023octets", 4},
+ {"ncsi_mac_rx_pkts_1024to1518octets", 4},
+ {"ncsi_mac_rx_pkts_1519tomaxoctets", 4},
+ {"ncsi_mac_rx_fcs_errors", 4},
+ {"ncsi_mac_rx_long_errors", 4},
+ {"ncsi_mac_rx_jabber_errors", 4},
+ {"ncsi_mac_rx_runt_err_cnt", 4},
+ {"ncsi_mac_rx_short_err_cnt", 4},
+ {"ncsi_mac_rx_filt_pkt_cnt", 4},
+ {"ncsi_mac_rx_octets_total_filt", 4},
+ {"ncsi_mac_tx_octets_ok", 4},
+ {"ncsi_mac_tx_octets_bad", 4},
+ {"ncsi_mac_tx_uc_pkts", 4},
+ {"ncsi_mac_tx_mc_pkts", 4},
+ {"ncsi_mac_tx_bc_pkts", 4},
+ {"ncsi_mac_tx_pkts_64octets", 4},
+ {"ncsi_mac_tx_pkts_65to127octets", 4},
+ {"ncsi_mac_tx_pkts_128to255octets", 4},
+ {"ncsi_mac_tx_pkts_256to511octets", 4},
+ {"ncsi_mac_tx_pkts_512to1023octets", 4},
+ {"ncsi_mac_tx_pkts_1024to1518octets", 4},
+ {"ncsi_mac_tx_pkts_1519tomaxoctets", 4},
+ {"ncsi_mac_tx_underrun", 4},
+ {"ncsi_mac_tx_crc_error", 4},
+ {"ncsi_mac_tx_pause_frames", 4},
+ {"ncsi_mac_rx_pad_pkts", 4},
+ {"ncsi_mac_rx_pause_frames", 4},
+};
+
+const struct hns3_reg_info dfx_rtc_regs[] = {
+ {HNS3_REG_RSV_NAME, 4},
+ {"lge_igu_afifo_dfx_0", 4},
+ {"lge_igu_afifo_dfx_1", 4},
+ {"lge_igu_afifo_dfx_2", 4},
+ {"lge_igu_afifo_dfx_3", 4},
+ {"lge_igu_afifo_dfx_4", 4},
+ {"lge_igu_afifo_dfx_5", 4},
+ {"lge_igu_afifo_dfx_6", 4},
+ {"lge_igu_afifo_dfx_7", 4},
+ {"lge_egu_afifo_dfx_0", 4},
+ {"lge_egu_afifo_dfx_1", 4},
+ {"lge_egu_afifo_dfx_2", 4},
+ {"lge_egu_afifo_dfx_3", 4},
+ {"lge_egu_afifo_dfx_4", 4},
+ {"lge_egu_afifo_dfx_5", 4},
+ {"lge_egu_afifo_dfx_6", 4},
+ {"lge_egu_afifo_dfx_7", 4},
+ {"cge_igu_afifo_dfx_0", 4},
+ {"cge_igu_afifo_dfx_1", 4},
+ {"cge_egu_afifo_dfx_0", 4},
+ {"cge_egu_afifo_dfx_1", 4},
+ {HNS3_REG_RSV_NAME, 12},
+};
+
+const struct hns3_reg_info dfx_ppp_regs[] = {
+ {HNS3_REG_RSV_NAME, 4},
+ {"drop_from_prt_pkt_cnt", 4},
+ {"drop_from_host_pkt_cnt", 4},
+ {"drop_tx_vlan_proc_cnt", 4},
+ {"drop_mng_cnt", 4},
+ {"drop_fd_cnt", 4},
+ {"drop_no_dst_cnt", 4},
+ {"drop_mc_mbid_full_cnt", 4},
+ {"drop_sc_filtered", 4},
+ {"ppp_mc_drop_pkt_cnt", 4},
+ {"drop_pt_cnt", 4},
+ {"drop_mac_anti_spoof_cnt", 4},
+ {"drop_ig_vfv_cnt", 4},
+ {"drop_ig_prtv_cnt", 4},
+ {"drop_cnm_pfc_pause_cnt", 4},
+ {"drop_torus_tc_cnt", 4},
+ {"drop_torus_lpbk_cnt", 4},
+ {"ppp_hfs_sts", 4},
+ {"ppp_mc_rslt_sts", 4},
+ {"ppp_p3u_sts", 4},
+ {HNS3_REG_RSV_NAME, 4},
+ {"ppp_umv_sts_0", 4},
+ {"ppp_umv_sts_1", 4},
+ {"ppp_vfv_sts", 4},
+ {"ppp_gro_key_cnt", 4},
+ {"ppp_gro_info_cnt", 4},
+ {"ppp_gro_drop_cnt", 4},
+ {"ppp_gro_out_cnt", 4},
+ {"ppp_gro_key_match_data_cnt", 4},
+ {"ppp_gro_key_match_tcam_cnt", 4},
+ {"ppp_gro_info_match_cnt", 4},
+ {"ppp_gro_free_entry_cnt", 4},
+ {"ppp_gro_inner_dfx_signal", 4},
+ {HNS3_REG_RSV_NAME, 12},
+ {"get_rx_pkt_cnt", 8},
+ {"get_tx_pkt_cnt", 8},
+ {"send_uc_prt2host_pkt_cnt", 8},
+ {"send_uc_prt2prt_pkt_cnt", 8},
+ {"send_uc_host2host_pkt_cnt", 8},
+ {"send_uc_host2prt_pkt_cnt", 8},
+ {"send_mc_from_prt_cnt", 8},
+ {"send_mc_from_host_cnt", 8},
+ {"ssu_mc_rd_cnt", 8},
+ {"ssu_mc_drop_cnt", 8},
+ {"ssu_mc_rd_pkt_cnt", 8},
+ {"ppp_mc_2host_pkt_cnt", 8},
+ {"ppp_mc_2prt_pkt_cnt", 8},
+ {"ntsnos_pkt_cnt", 8},
+ {"ntup_pkt_cnt", 8},
+ {"ntlcl_pkt_cnt", 8},
+ {"nttgt_pkt_cnt", 8},
+ {"rtns_pkt_cnt", 8},
+ {"rtlpbk_pkt_cnt", 8},
+ {"nr_pkt_cnt", 8},
+ {"rr_pkt_cnt", 8},
+ {"mng_tbl_hit_cnt", 8},
+ {"fd_tbl_hit_cnt", 8},
+ {"fd_lkup_cnt", 8},
+ {"bc_hit_cnt", 8},
+ {"um_tbl_uc_hit_cnt", 8},
+ {"um_tbl_mc_hit_cnt", 8},
+ {"um_tbl_snq_hit_cnt", 8},
+ {HNS3_REG_RSV_NAME, 8},
+ {"fwd_bonding_hit_cnt", 8},
+ {"promis_tbl_hit_cnt", 8},
+ {"get_tunl_pkt_cnt", 8},
+ {"get_bmc_pkt_cnt", 8},
+ {"send_uc_prt2bmc_pkt_cnt", 8},
+ {"send_uc_host2bmc_pkt_cnt", 8},
+ {"send_uc_bmc2host_pkt_cnt", 8},
+ {"send_uc_bmc2prt_pkt_cnt", 8},
+ {"ppp_mc_2bmc_pkt_cnt", 8},
+ {HNS3_REG_RSV_NAME, 24},
+ {"rx_default_host_hit_cnt", 8},
+ {"lan_pair_cnt", 8},
+ {"um_tbl_mc_hit_pkt_cnt", 8},
+ {"mta_tbl_hit_pkt_cnt", 8},
+ {"promis_tbl_hit_pkt_cnt", 8},
+ {HNS3_REG_RSV_NAME, 16},
+};
+
+const struct hns3_reg_info dfx_rcb_regs[] = {
+ {HNS3_REG_RSV_NAME, 4},
+ {"fsm_dfx_st0", 4},
+ {"fsm_dfx_st1", 4},
+ {"fsm_dfx_st2", 4},
+ {"fifo_dfx_st0", 4},
+ {"fifo_dfx_st1", 4},
+ {"fifo_dfx_st2", 4},
+ {"fifo_dfx_st3", 4},
+ {"fifo_dfx_st4", 4},
+ {"fifo_dfx_st5", 4},
+ {"fifo_dfx_st6", 4},
+ {"fifo_dfx_st7", 4},
+ {"fifo_dfx_st8", 4},
+ {"fifo_dfx_st9", 4},
+ {"fifo_dfx_st10", 4},
+ {"fifo_dfx_st11", 4},
+ {"q_credit_vld_0", 4},
+ {"q_credit_vld_1", 4},
+ {"q_credit_vld_2", 4},
+ {"q_credit_vld_3", 4},
+ {"q_credit_vld_4", 4},
+ {"q_credit_vld_5", 4},
+ {"q_credit_vld_6", 4},
+ {"q_credit_vld_7", 4},
+ {"q_credit_vld_8", 4},
+ {"q_credit_vld_9", 4},
+ {"q_credit_vld_10", 4},
+ {"q_credit_vld_11", 4},
+ {"q_credit_vld_12", 4},
+ {"q_credit_vld_13", 4},
+ {"q_credit_vld_14", 4},
+ {"q_credit_vld_15", 4},
+ {"q_credit_vld_16", 4},
+ {"q_credit_vld_17", 4},
+ {"q_credit_vld_18", 4},
+ {"q_credit_vld_19", 4},
+ {"q_credit_vld_20", 4},
+ {"q_credit_vld_21", 4},
+ {"q_credit_vld_22", 4},
+ {"q_credit_vld_23", 4},
+ {"q_credit_vld_24", 4},
+ {"q_credit_vld_25", 4},
+ {"q_credit_vld_26", 4},
+ {"q_credit_vld_27", 4},
+ {"q_credit_vld_28", 4},
+ {"q_credit_vld_29", 4},
+ {"q_credit_vld_30", 4},
+ {"q_credit_vld_31", 4},
+ {"gro_bd_serr_cnt", 4},
+ {"gro_context_serr_cnt", 4},
+ {"rx_stash_cfg_serr_cnt", 4},
+ {"rcb_tx_mem_serr_cnt", 4},
+ {"gro_bd_merr_cnt", 4},
+ {"gro_context_merr_cnt", 4},
+ {"rx_stash_cfg_merr_cnt", 4},
+ {"rcb_tx_mem_merr_cnt", 4},
+ {HNS3_REG_RSV_NAME, 16},
+};
+
+const struct hns3_reg_info dfx_tqp_regs[] = {
+ {"q_num", 4},
+ {"rcb_cfg_rx_ring_tail", 4},
+ {"rcb_cfg_rx_ring_head", 4},
+ {"rcb_cfg_rx_ring_fbdnum", 4},
+ {"rcb_cfg_rx_ring_offset", 4},
+ {"rcb_cfg_rx_ring_fbdoffset", 4},
+ {"rcb_cfg_rx_ring_pktnum_record", 4},
+ {"rcb_cfg_tx_ring_tail", 4},
+ {"rcb_cfg_tx_ring_head", 4},
+ {"rcb_cfg_tx_ring_fbdnum", 4},
+ {"rcb_cfg_tx_ring_offset", 4},
+ {"rcb_cfg_tx_ring_ebdnum", 4},
+};
+
+const struct hns3_reg_info dfx_ssu_2_regs[] = {
+ {"oq_index", 4},
+ {"queue_cnt", 4},
+ {HNS3_REG_RSV_NAME, 16},
+};
+
+const struct hns3_reg_info vf_cmdq_regs[] = {
+ {"comm_nic_csq_baseaddr_l", 4},
+ {"comm_nic_csq_baseaddr_h", 4},
+ {"comm_nic_csq_depth", 4},
+ {"comm_nic_csq_tail", 4},
+ {"comm_nic_csq_head", 4},
+ {"comm_nic_crq_baseaddr_l", 4},
+ {"comm_nic_crq_baseaddr_h", 4},
+ {"comm_nic_crq_depth", 4},
+ {"comm_nic_crq_tail", 4},
+ {"comm_nic_crq_head", 4},
+ {"comm_vector0_cmdq_src", 4},
+ {"comm_vector0_cmdq_state", 4},
+ {"comm_cmdq_intr_en", 4},
+ {"comm_cmdq_intr_gen", 4},
+};
+
+const struct hns3_reg_info vf_common_regs[] = {
+ {"misc_vector_base", 4},
+ {"rst_ing", 4},
+ {"gro_en", 4},
+};
+
+const struct hns3_reg_info vf_ring_regs[] = {
+ {"ring_rx_addr_l", 4},
+ {"ring_rx_addr_h", 4},
+ {"ring_rx_bd_num", 4},
+ {"ring_rx_bd_length", 4},
+ {"ring_rx_merge_en", 4},
+ {"ring_rx_tail", 4},
+ {"ring_rx_head", 4},
+ {"ring_rx_fbd_num", 4},
+ {"ring_rx_offset", 4},
+ {"ring_rx_fbd_offset", 4},
+ {"ring_rx_stash", 4},
+ {"ring_rx_bd_err", 4},
+ {"ring_tx_addr_l", 4},
+ {"ring_tx_addr_h", 4},
+ {"ring_tx_bd_num", 4},
+ {"ring_tx_priority", 4},
+ {"ring_tx_tc", 4},
+ {"ring_tx_merge_en", 4},
+ {"ring_tx_tail", 4},
+ {"ring_tx_head", 4},
+ {"ring_tx_fbd_num", 4},
+ {"ring_tx_offset", 4},
+ {"ring_tx_ebd_num", 4},
+ {"ring_tx_ebd_offset", 4},
+ {"ring_tx_bd_err", 4},
+ {"ring_en", 4},
+};
+
+const struct hns3_reg_info vf_tqp_intr_regs[] = {
+ {"tqp_intr_ctrl", 4},
+ {"tqp_intr_gl0", 4},
+ {"tqp_intr_gl1", 4},
+ {"tqp_intr_gl2", 4},
+ {"tqp_intr_rl", 4},
+};
+
+const struct hns3_regs_group pf_regs_groups[] = {
+ [HNS3_TAG_CMDQ] = {"cmdq_regs", pf_cmdq_regs, ARRAY_SIZE(pf_cmdq_regs)},
+ [HNS3_TAG_COMMON] = {"common_regs", pf_common_regs,
+ ARRAY_SIZE(pf_common_regs)},
+ [HNS3_TAG_RING] = {"ring_regs", pf_ring_regs, ARRAY_SIZE(pf_ring_regs)},
+ [HNS3_TAG_TQP_INTR] = {"tqp_intr_regs", pf_tqp_intr_regs,
+ ARRAY_SIZE(pf_tqp_intr_regs)},
+ [HNS3_TAG_QUERY_32_BIT] = {"dfx_32_regs", query_32_bit_regs,
+ ARRAY_SIZE(query_32_bit_regs)},
+ [HNS3_TAG_QUERY_64_BIT] = {"dfx_64_regs", query_64_bit_regs,
+ ARRAY_SIZE(query_64_bit_regs)},
+ [HNS3_TAG_DFX_BIOS_COMMON] = {"dfx_bios_common_regs",
+ dfx_bios_common_regs,
+ ARRAY_SIZE(dfx_bios_common_regs)},
+ [HNS3_TAG_DFX_SSU_0] = {"dfx_ssu_0_regs", dfx_ssu_0_regs,
+ ARRAY_SIZE(dfx_ssu_0_regs)},
+ [HNS3_TAG_DFX_SSU_1] = {"dfx_ssu_1_regs", dfx_ssu_1_regs,
+ ARRAY_SIZE(dfx_ssu_1_regs)},
+ [HNS3_TAG_DFX_IGU_EGU] = {"dfx_igu_egu_regs", dfx_igu_egu_regs,
+ ARRAY_SIZE(dfx_igu_egu_regs)},
+ [HNS3_TAG_DFX_RPU_0] = {"dfx_rpu_0_regs", dfx_rpu_0_regs,
+ ARRAY_SIZE(dfx_rpu_0_regs)},
+ [HNS3_TAG_DFX_RPU_1] = {"dfx_rpu_1_regs", dfx_rpu_1_regs,
+ ARRAY_SIZE(dfx_rpu_1_regs)},
+ [HNS3_TAG_DFX_NCSI] = {"dfx_ncsi_regs", dfx_ncsi_regs,
+ ARRAY_SIZE(dfx_ncsi_regs)},
+ [HNS3_TAG_DFX_RTC] = {"dfx_rtc_regs", dfx_rtc_regs,
+ ARRAY_SIZE(dfx_rtc_regs)},
+ [HNS3_TAG_DFX_PPP] = {"dfx_ppp_regs", dfx_ppp_regs,
+ ARRAY_SIZE(dfx_ppp_regs)},
+ [HNS3_TAG_DFX_RCB] = {"dfx_rcb_regs", dfx_rcb_regs,
+ ARRAY_SIZE(dfx_rcb_regs)},
+ [HNS3_TAG_DFX_TQP] = {"dfx_tqp_regs", dfx_tqp_regs,
+ ARRAY_SIZE(dfx_tqp_regs)},
+ [HNS3_TAG_DFX_SSU_2] = {"dfx_ssu_2_regs", dfx_ssu_2_regs,
+ ARRAY_SIZE(dfx_ssu_2_regs)},
+ [HNS3_TAG_DFX_RPU_TNL] = {"dfx_rpu_tnl", dfx_rpu_0_regs,
+ ARRAY_SIZE(dfx_rpu_0_regs)},
+};
+
+const struct hns3_regs_group vf_regs_groups[] = {
+ [HNS3_TAG_CMDQ] = {"cmdq_regs", vf_cmdq_regs, ARRAY_SIZE(vf_cmdq_regs)},
+ [HNS3_TAG_COMMON] = {"common_regs", vf_common_regs,
+ ARRAY_SIZE(vf_common_regs)},
+ [HNS3_TAG_RING] = {"ring_regs", vf_ring_regs, ARRAY_SIZE(vf_ring_regs)},
+ [HNS3_TAG_TQP_INTR] = {"tqp_intr_regs", vf_tqp_intr_regs,
+ ARRAY_SIZE(vf_tqp_intr_regs)},
+};
+
+static void hns3_dump_reg_hex(const char *regs_name, const u8 *regs_data,
+ u16 value_len, u32 name_max_len)
+{
+ if (strcmp(regs_name, HNS3_REG_RSV_NAME) == 0)
+ return;
+
+ fprintf(stdout, " %-*s : ", name_max_len, regs_name);
+ if (value_len == 4) /* 4 bytes register */
+ fprintf(stdout, "0x%08x\n", *(u32 *)regs_data);
+ if (value_len == 8) /* 8 bytes register */
+ fprintf(stdout, "0x%016llx\n", *(u64 *)regs_data);
+}
+
+static u32 hns3_get_group_regs_name_max_len(const struct hns3_regs_group *group)
+{
+ const struct hns3_reg_info *reg;
+ u32 max_len = 0;
+ u16 i;
+
+ for (i = 0; i < group->regs_count; i++) {
+ reg = &group->regs[i];
+ max_len = strlen(reg->name) > max_len ?
+ strlen(reg->name) : max_len;
+ }
+
+ return max_len;
+}
+
+static const char *hns3_get_group_name(const struct hns3_regs_group *group,
+ u32 tag)
+{
+ static u32 pre_tag = HNS3_TAG_MAX;
+ static char group_name[256];
+ static u32 index;
+
+ if (!hns3_reg_is_repeat_tag_array[tag])
+ return group->group_name;
+
+ if (tag != pre_tag)
+ index = 0;
+
+ pre_tag = tag;
+ sprintf(group_name, "%s_%u", group->group_name, index++);
+ return group_name;
+}
+
+static void hns3_dump_reg_group(const struct hns3_regs_group *group, u32 tag,
+ u32 expected_len, const u8 *regs_data)
+{
+ u32 name_max_len = hns3_get_group_regs_name_max_len(group);
+ const struct hns3_reg_info *reg;
+ u32 dump_offset = 0;
+ u16 i;
+
+ fprintf(stdout, "[%s]\n", hns3_get_group_name(group, tag));
+ for (i = 0; i < group->regs_count && dump_offset < expected_len; i++) {
+ reg = &group->regs[i];
+ hns3_dump_reg_hex(reg->name, regs_data + dump_offset,
+ reg->value_len, name_max_len);
+ dump_offset += reg->value_len;
+ }
+
+ /* the driver may add new register.
+ * In this case, the register name is unknown.
+ * The register can be parsed as unknown:value format.
+ */
+ while (dump_offset < expected_len) {
+ hns3_dump_reg_hex(HNS3_REG_UNKNOW_NAME, regs_data + dump_offset,
+ HNS3_REG_UNKNOW_VALUE_LEN, name_max_len);
+ dump_offset += HNS3_REG_UNKNOW_VALUE_LEN;
+ }
+}
+
+static void hns3_dump_as_groups(const struct hns3_regs_group *groups,
+ const u8 *regs_data, u32 regs_len)
+{
+ u32 tlv_size = sizeof(struct hns3_reg_tlv);
+ const struct hns3_reg_tlv *tlv;
+ u32 dump_offset = 0;
+
+ while (dump_offset < regs_len) {
+ tlv = (const struct hns3_reg_tlv *)(regs_data + dump_offset);
+ hns3_dump_reg_group(&groups[tlv->tag], tlv->tag,
+ tlv->len - tlv_size,
+ regs_data + dump_offset + tlv_size);
+ dump_offset += tlv->len;
+ }
+}
+
+static bool hns3_dump_validate(const u8 *regs_data, u32 regs_len)
+{
+ u32 tlv_size = sizeof(struct hns3_reg_tlv);
+ const struct hns3_reg_tlv *tlv;
+ u32 dump_offset = 0;
+
+ while (dump_offset < regs_len) {
+ tlv = (const struct hns3_reg_tlv *)(regs_data + dump_offset);
+
+ /* register value length is 4 bytes or 8 bytes */
+ if ((tlv->len - tlv_size) % 4)
+ return false;
+
+ if (tlv->tag >= HNS3_TAG_MAX)
+ return false;
+
+ dump_offset += tlv->len;
+ }
+
+ return dump_offset == regs_len;
+}
+
+int hns3_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ const struct hns3_regs_group *groups = pf_regs_groups;
+ u32 header_len = sizeof(struct hns3_reg_header);
+ const struct hns3_reg_header *header;
+
+ /* must contain header and register data */
+ if (regs->len <= header_len)
+ return -ENODATA;
+
+ header = (struct hns3_reg_header *)regs->data;
+ if (header->magic_number != HNS3_REG_MAGIC_NUMBER)
+ return -EOPNOTSUPP;
+
+ if (!hns3_dump_validate(regs->data + header_len,
+ regs->len - header_len))
+ return -EINVAL;
+
+ if (header->is_vf)
+ groups = vf_regs_groups;
+
+ hns3_dump_as_groups(groups, regs->data + header_len,
+ regs->len - header_len);
+ return 0;
+}
diff --git a/ibm_emac.c b/ibm_emac.c
new file mode 100644
index 0000000..9f7cae6
--- /dev/null
+++ b/ibm_emac.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2004, 2005 Zultys Technologies
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "internal.h"
+
+/* Ethtool get_regs complex data.
+ * we want to get not just EMAC registers, but also MAL, ZMII, RGMII, TAH
+ * when available.
+ *
+ * Returned BLOB consists of the ibm_emac_ethtool_regs_hdr,
+ * MAL registers, EMAC registers and optional ZMII, RGMII, TAH registers.
+ * Each register component is preceded with emac_ethtool_regs_subhdr.
+ * Order of the optional headers follows their relative bit posititions
+ * in emac_ethtool_regs_hdr.components
+ */
+#define EMAC_ETHTOOL_REGS_ZMII 0x00000001
+#define EMAC_ETHTOOL_REGS_RGMII 0x00000002
+#define EMAC_ETHTOOL_REGS_TAH 0x00000004
+
+#define EMAC_VERSION 3
+#define EMAC4_VERSION 4
+#define EMAC4SYNC_VERSION 5
+
+struct emac_ethtool_regs_hdr {
+ u32 components;
+};
+
+struct emac_ethtool_regs_subhdr {
+ u32 version;
+ u32 index;
+};
+
+struct emac_regs {
+ /* Common registers across all EMAC implementations. */
+ u32 mr0; /* Special */
+ u32 mr1; /* Reset */
+ u32 tmr0; /* Special */
+ u32 tmr1; /* Special */
+ u32 rmr; /* Reset */
+ u32 isr; /* Always */
+ u32 iser; /* Reset */
+ u32 iahr; /* Reset, R, T */
+ u32 ialr; /* Reset, R, T */
+ u32 vtpid; /* Reset, R, T */
+ u32 vtci; /* Reset, R, T */
+ u32 ptr; /* Reset, T */
+ union {
+ /* Registers unique to EMAC4 implementations */
+ struct {
+ u32 iaht1; /* Reset, R */
+ u32 iaht2; /* Reset, R */
+ u32 iaht3; /* Reset, R */
+ u32 iaht4; /* Reset, R */
+ u32 gaht1; /* Reset, R */
+ u32 gaht2; /* Reset, R */
+ u32 gaht3; /* Reset, R */
+ u32 gaht4; /* Reset, R */
+ } emac4;
+ /* Registers unique to EMAC4SYNC implementations */
+ struct {
+ u32 mahr; /* Reset, R, T */
+ u32 malr; /* Reset, R, T */
+ u32 mmahr; /* Reset, R, T */
+ u32 mmalr; /* Reset, R, T */
+ u32 rsvd0[4];
+ } emac4sync;
+ } u0;
+ /* Common registers across all EMAC implementations. */
+ u32 lsah;
+ u32 lsal;
+ u32 ipgvr; /* Reset, T */
+ u32 stacr; /* Special */
+ u32 trtr; /* Special */
+ u32 rwmr; /* Reset */
+ u32 octx;
+ u32 ocrx;
+ union {
+ /* Registers unique to EMAC4 implementations */
+ struct {
+ u32 ipcr;
+ } emac4;
+ /* Registers unique to EMAC4SYNC implementations */
+ struct {
+ u32 rsvd1;
+ u32 revid;
+ u32 rsvd2[2];
+ u32 iaht1; /* Reset, R */
+ u32 iaht2; /* Reset, R */
+ u32 iaht3; /* Reset, R */
+ u32 iaht4; /* Reset, R */
+ u32 iaht5; /* Reset, R */
+ u32 iaht6; /* Reset, R */
+ u32 iaht7; /* Reset, R */
+ u32 iaht8; /* Reset, R */
+ u32 gaht1; /* Reset, R */
+ u32 gaht2; /* Reset, R */
+ u32 gaht3; /* Reset, R */
+ u32 gaht4; /* Reset, R */
+ u32 gaht5; /* Reset, R */
+ u32 gaht6; /* Reset, R */
+ u32 gaht7; /* Reset, R */
+ u32 gaht8; /* Reset, R */
+ u32 tpc; /* Reset, T */
+ } emac4sync;
+ } u1;
+};
+
+struct mal_regs {
+ u32 tx_count;
+ u32 rx_count;
+
+ u32 cfg;
+ u32 esr;
+ u32 ier;
+ u32 tx_casr;
+ u32 tx_carr;
+ u32 tx_eobisr;
+ u32 tx_deir;
+ u32 rx_casr;
+ u32 rx_carr;
+ u32 rx_eobisr;
+ u32 rx_deir;
+ u32 tx_ctpr[32];
+ u32 rx_ctpr[32];
+ u32 rcbs[32];
+};
+
+struct zmii_regs {
+ u32 fer;
+ u32 ssr;
+ u32 smiisr;
+};
+
+struct rgmii_regs {
+ u32 fer;
+ u32 ssr;
+};
+
+struct tah_regs {
+ u32 revid;
+ u32 pad[3];
+ u32 mr;
+ u32 ssr0;
+ u32 ssr1;
+ u32 ssr2;
+ u32 ssr3;
+ u32 ssr4;
+ u32 ssr5;
+ u32 tsr;
+};
+
+static void *print_emac_regs(void *buf)
+{
+ struct emac_ethtool_regs_subhdr *hdr = buf;
+ struct emac_regs *p = (struct emac_regs *)(hdr + 1);
+ void *res = p + 1;
+
+ if (!((hdr->version == EMAC_VERSION) ||
+ (hdr->version == EMAC4_VERSION) ||
+ (hdr->version == EMAC4SYNC_VERSION)))
+ {
+ printf("This driver version doesn't support information\n"
+ " output for EMAC area, please update it or use older\n"
+ " ethtool version\n");
+ return res;
+ }
+
+ printf("EMAC%d Registers\n", hdr->index);
+ printf("-----------------\n");
+ printf("MR0 = 0x%08x MR1 = 0x%08x RMR = 0x%08x\n"
+ "ISR = 0x%08x ISER = 0x%08x\n"
+ "TMR0 = 0x%08x TMR1 = 0x%08x\n"
+ "TRTR = 0x%08x RWMR = 0x%08x\n"
+ "IAR = %04x%08x\n"
+ "LSA = %04x%08x\n"
+ "VTPID = 0x%04x VTCI = 0x%04x\n"
+ "IPGVR = 0x%04x STACR = 0x%08x\n"
+ "OCTX = 0x%08x OCRX = 0x%08x\n",
+ p->mr0, p->mr1, p->rmr,
+ p->isr, p->iser,
+ p->tmr0, p->tmr1,
+ p->trtr, p->rwmr,
+ p->iahr, p->ialr,
+ p->lsah, p->lsal,
+ p->vtpid, p->vtci,
+ p->ipgvr, p->stacr, p->octx, p->ocrx);
+
+ if (hdr->version == EMAC4SYNC_VERSION) {
+ printf("MAHR = 0x%08x MALR = 0x%08x MMAHR = 0x%08x\n"
+ "MMALR = 0x%08x REVID = 0x%08x\n",
+ p->u0.emac4sync.mahr, p->u0.emac4sync.malr,
+ p->u0.emac4sync.mmahr, p->u0.emac4sync.mmalr,
+ p->u1.emac4sync.revid);
+
+ printf("IAHT = 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ p->u1.emac4sync.iaht1, p->u1.emac4sync.iaht2,
+ p->u1.emac4sync.iaht3, p->u1.emac4sync.iaht4);
+ printf(" 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ p->u1.emac4sync.iaht5, p->u1.emac4sync.iaht6,
+ p->u1.emac4sync.iaht7, p->u1.emac4sync.iaht8);
+
+
+ printf("GAHT = 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ p->u1.emac4sync.gaht1, p->u1.emac4sync.gaht2,
+ p->u1.emac4sync.gaht3, p->u1.emac4sync.gaht4);
+ printf(" 0x%04x 0x%04x 0x%04x 0x%04x\n\n",
+ p->u1.emac4sync.gaht5, p->u1.emac4sync.gaht6,
+ p->u1.emac4sync.gaht7, p->u1.emac4sync.gaht8);
+
+ } else if (hdr->version == EMAC4_VERSION) {
+ printf("IAHT = 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ p->u0.emac4.iaht1, p->u0.emac4.iaht2,
+ p->u0.emac4.iaht3, p->u0.emac4.iaht4);
+
+ printf("GAHT = 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ p->u0.emac4.gaht1, p->u0.emac4.gaht2,
+ p->u0.emac4.gaht3, p->u0.emac4.gaht4);
+
+ printf(" IPCR = 0x%08x\n\n", p->u1.emac4.ipcr);
+ } else if (hdr->version == EMAC_VERSION) {
+ printf("IAHT = 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ p->u0.emac4.iaht1, p->u0.emac4.iaht2,
+ p->u0.emac4.iaht3, p->u0.emac4.iaht4);
+
+ printf("GAHT = 0x%04x 0x%04x 0x%04x 0x%04x\n",
+ p->u0.emac4.gaht1, p->u0.emac4.gaht2,
+ p->u0.emac4.gaht3, p->u0.emac4.gaht4);
+ }
+ return res;
+}
+
+static void *print_mal_regs(void *buf)
+{
+ struct emac_ethtool_regs_subhdr *hdr = buf;
+ struct mal_regs *p = (struct mal_regs *)(hdr + 1);
+ unsigned int i;
+
+ printf("MAL%d Registers\n", hdr->index);
+ printf("-----------------\n");
+ printf("CFG = 0x%08x ESR = 0x%08x IER = 0x%08x\n"
+ "TX|CASR = 0x%08x CARR = 0x%08x EOBISR = 0x%08x DEIR = 0x%08x\n"
+ "RX|CASR = 0x%08x CARR = 0x%08x EOBISR = 0x%08x DEIR = 0x%08x\n",
+ p->cfg, p->esr, p->ier,
+ p->tx_casr, p->tx_carr, p->tx_eobisr, p->tx_deir,
+ p->rx_casr, p->rx_carr, p->rx_eobisr, p->rx_deir);
+
+ printf("TX|");
+ for (i = 0; i < p->tx_count; ++i) {
+ if (i && !(i % 4))
+ printf("\n ");
+ printf("CTP%d = 0x%08x ", i, p->tx_ctpr[i]);
+ }
+ printf("\nRX|");
+ for (i = 0; i < p->rx_count; ++i) {
+ if (i && !(i % 4))
+ printf("\n ");
+ printf("CTP%d = 0x%08x ", i, p->rx_ctpr[i]);
+ }
+ printf("\n ");
+ for (i = 0; i < p->rx_count; ++i) {
+ u32 r = p->rcbs[i];
+ if (i && !(i % 3))
+ printf("\n ");
+ printf("RCBS%d = 0x%08x (%d) ", i, r, r * 16);
+ }
+ printf("\n\n");
+ return p + 1;
+}
+
+static void *print_zmii_regs(void *buf)
+{
+ struct emac_ethtool_regs_subhdr *hdr = buf;
+ struct zmii_regs *p = (struct zmii_regs *)(hdr + 1);
+
+ printf("ZMII%d Registers\n", hdr->index);
+ printf("-----------------\n");
+ printf("FER = %08x SSR = %08x\n"
+ "SMIISR = %08x\n\n", p->fer, p->ssr, p->smiisr);
+
+ return p + 1;
+}
+
+static void *print_rgmii_regs(void *buf)
+{
+ struct emac_ethtool_regs_subhdr *hdr = buf;
+ struct rgmii_regs *p = (struct rgmii_regs *)(hdr + 1);
+
+ printf("RGMII%d Registers\n", hdr->index);
+ printf("-----------------\n");
+ printf("FER = %08x SSR = %08x\n\n", p->fer, p->ssr);
+
+ return p + 1;
+}
+
+static void *print_tah_regs(void *buf)
+{
+ struct emac_ethtool_regs_subhdr *hdr = buf;
+ struct tah_regs *p = (struct tah_regs *)(hdr + 1);
+
+ printf("TAH%d Registers\n", hdr->index);
+ printf("-----------------\n");
+
+ printf("REVID = %08x MR = %08x TSR = %08x\n"
+ "SSR0 = %08x SSR1 = %08x SSR2 = %08x\n"
+ "SSR3 = %08x SSR4 = %08x SSR5 = %08x\n\n",
+ p->revid, p->mr, p->tsr,
+ p->ssr0, p->ssr1, p->ssr2, p->ssr3, p->ssr4, p->ssr5);
+
+ return p + 1;
+}
+
+int ibm_emac_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ struct emac_ethtool_regs_hdr *hdr =
+ (struct emac_ethtool_regs_hdr *)regs->data;
+ void *buf = hdr + 1;
+
+ buf = print_mal_regs(buf);
+ buf = print_emac_regs(buf);
+ if (hdr->components & EMAC_ETHTOOL_REGS_ZMII)
+ buf = print_zmii_regs(buf);
+ if (hdr->components & EMAC_ETHTOOL_REGS_RGMII)
+ buf = print_rgmii_regs(buf);
+ if (hdr->components & EMAC_ETHTOOL_REGS_TAH)
+ print_tah_regs(buf);
+
+ return 0;
+}
diff --git a/igb.c b/igb.c
new file mode 100644
index 0000000..f358f53
--- /dev/null
+++ b/igb.c
@@ -0,0 +1,876 @@
+/* Copyright (c) 2007 Intel Corporation */
+#include <stdio.h>
+#include "internal.h"
+
+/* Register Bit Masks */
+/* Device Control */
+#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */
+#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */
+#define E1000_CTRL_GIOMASTERD 0x00000008 /* GIO Master Disable*/
+#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */
+#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */
+#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */
+#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
+#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */
+#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */
+#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */
+#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */
+#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */
+#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */
+#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */
+#define E1000_CTRL_SDP0_GPIEN 0x00010000 /* General Purpose Interrupt Detection Enable for SDP0 */
+#define E1000_CTRL_SDP1_GPIEN 0x00020000 /* General Purpose Interrupt Detection Enable for SDP1 */
+#define E1000_CTRL_SDP0_DATA 0x00040000 /* SDP0 Data */
+#define E1000_CTRL_SDP1_DATA 0x00080000 /* SDP1 Data */
+#define E1000_CTRL_ADVD3WUC 0x00100000 /* D3Cold WakeUp Capability Advertisement Enable */
+#define E1000_CTRL_SDP0_WDE 0x00200000 /* Watchdog Indication for SDP0 */
+#define E1000_CTRL_SDP1_IODIR 0x00400000 /* SDP1 Directionality */
+#define E1000_CTRL_RST 0x04000000 /* Global reset */
+#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */
+#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */
+#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
+#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */
+
+/* Device Status */
+#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */
+#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */
+#define E1000_STATUS_LANID 0x00000008 /* LAN ID */
+#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */
+#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */
+#define E1000_STATUS_SPEED_MASK 0x000000C0 /* Speed Mask */
+#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */
+#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */
+#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */
+#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */
+#define E1000_STATUS_PHYRA 0x00000400 /* PHY Reset Asserted */
+#define E1000_STATUS_GIOMASTEREN 0x00080000 /* GIO Master Enable Status */
+#define E1000_STATUS_DMA_CGEN 0x80000000 /* DMA clock gating Enable */
+
+/* Receive Control */
+#define E1000_RCTL_EN 0x00000002 /* enable */
+#define E1000_RCTL_SBP 0x00000004 /* store bad packet */
+#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */
+#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */
+#define E1000_RCTL_LPE 0x00000020 /* long packet enable */
+#define E1000_RCTL_LBM_MASK 0x000000C0 /* Loopback mode mask */
+#define E1000_RCTL_LBM_NORM 0x00000000 /* normal loopback mode */
+#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */
+#define E1000_RCTL_LBM_SERDES 0x000000C0 /* SERDES loopback mode */
+#define E1000_RCTL_RDMTS 0x00000300 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */
+#define E1000_RCTL_MO 0x00003000 /* multicast offset shift */
+#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 47:36 */
+#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 46:35 */
+#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 45:34 */
+#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 43:32 */
+#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */
+#define E1000_RCTL_BSIZE 0x00030000 /* rx buffer size */
+#define E1000_RCTL_BSIZE_2048 0x00000000 /* rx buffer size 2048 */
+#define E1000_RCTL_BSIZE_1024 0x00010000 /* rx buffer size 1024 */
+#define E1000_RCTL_BSIZE_512 0x00020000 /* rx buffer size 512 */
+#define E1000_RCTL_BSIZE_256 0x00030000 /* rx buffer size 256 */
+#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */
+#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */
+#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */
+#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */
+#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */
+#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC from packet.0=No strip;1=strip */
+
+/* Transmit Control */
+#define E1000_TCTL_EN 0x00000002 /* enable tx */
+#define E1000_TCTL_PSP 0x00000008 /* pad short packets */
+#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */
+#define E1000_TCTL_BST 0x003ff000 /* Backoff Slot time */
+#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */
+#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */
+#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
+#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
+
+int igb_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *regs_buff = (u32 *)regs->data;
+ u32 reg;
+ u8 i;
+ u8 version = (u8)(regs->version >> 24);
+
+ if (version != 1)
+ return -1;
+
+ /* Device control register */
+ reg = regs_buff[0];
+ fprintf(stdout,
+ "0x00000: CTRL (Device control register) 0x%08X\n"
+ " Invert Loss-Of-Signal: %s\n"
+ " Receive flow control: %s\n"
+ " Transmit flow control: %s\n"
+ " VLAN mode: %s\n"
+ " Set link up: %s\n"
+ " D3COLD WakeUp capability advertisement: %s\n",
+ reg,
+ reg & E1000_CTRL_ILOS ? "yes" : "no",
+ reg & E1000_CTRL_RFCE ? "enabled" : "disabled",
+ reg & E1000_CTRL_TFCE ? "enabled" : "disabled",
+ reg & E1000_CTRL_VME ? "enabled" : "disabled",
+ reg & E1000_CTRL_SLU ? "1" : "0",
+ reg & E1000_CTRL_ADVD3WUC ? "enabled" : "disabled");
+ fprintf(stdout,
+ " Auto speed detect: %s\n"
+ " Speed select: %s\n"
+ " Force speed: %s\n"
+ " Force duplex: %s\n",
+ reg & E1000_CTRL_ASDE ? "enabled" : "disabled",
+ (reg & E1000_CTRL_SPD_SEL) == E1000_CTRL_SPD_10 ? "10Mb/s" :
+ (reg & E1000_CTRL_SPD_SEL) == E1000_CTRL_SPD_100 ? "100Mb/s" :
+ (reg & E1000_CTRL_SPD_SEL) == E1000_CTRL_SPD_1000 ? "1000Mb/s" :
+ "not used",
+ reg & E1000_CTRL_FRCSPD ? "yes" : "no",
+ reg & E1000_CTRL_FRCDPX ? "yes" : "no");
+
+ /* Device status register */
+ reg = regs_buff[1];
+ fprintf(stdout,
+ "0x00008: STATUS (Device status register) 0x%08X\n"
+ " Duplex: %s\n"
+ " Link up: %s\n"
+ " Transmission: %s\n"
+ " DMA clock gating: %s\n",
+ reg,
+ reg & E1000_STATUS_FD ? "full" : "half",
+ reg & E1000_STATUS_LU ? "link config" : "no link config",
+ reg & E1000_STATUS_TXOFF ? "paused" : "on",
+ reg & E1000_STATUS_DMA_CGEN ? "enabled" : "disabled");
+ fprintf(stdout,
+ " TBI mode: %s\n"
+ " Link speed: %s\n"
+ " Bus type: %s\n",
+ reg & E1000_STATUS_TBIMODE ? "enabled" : "disabled",
+ (reg & E1000_STATUS_SPEED_MASK) == E1000_STATUS_SPEED_10 ?
+ "10Mb/s" :
+ (reg & E1000_STATUS_SPEED_MASK) == E1000_STATUS_SPEED_100 ?
+ "100Mb/s" :
+ (reg & E1000_STATUS_SPEED_MASK) == E1000_STATUS_SPEED_1000 ?
+ "1000Mb/s" : "not used",
+ "PCI Express");
+
+ /* Receive control register */
+ reg = regs_buff[32];
+ fprintf(stdout,
+ "0x00100: RCTL (Receive control register) 0x%08X\n"
+ " Receiver: %s\n"
+ " Store bad packets: %s\n"
+ " Unicast promiscuous: %s\n"
+ " Multicast promiscuous: %s\n"
+ " Long packet: %s\n"
+ " Descriptor minimum threshold size: %s\n"
+ " Broadcast accept mode: %s\n"
+ " VLAN filter: %s\n"
+ " Cononical form indicator: %s\n"
+ " Discard pause frames: %s\n"
+ " Pass MAC control frames: %s\n"
+ " Loopback mode: %s\n",
+ reg,
+ reg & E1000_RCTL_EN ? "enabled" : "disabled",
+ reg & E1000_RCTL_SBP ? "enabled" : "disabled",
+ reg & E1000_RCTL_UPE ? "enabled" : "disabled",
+ reg & E1000_RCTL_MPE ? "enabled" : "disabled",
+ reg & E1000_RCTL_LPE ? "enabled" : "disabled",
+ (reg & E1000_RCTL_RDMTS) == E1000_RCTL_RDMTS_HALF ? "1/2" :
+ (reg & E1000_RCTL_RDMTS) == E1000_RCTL_RDMTS_QUAT ? "1/4" :
+ (reg & E1000_RCTL_RDMTS) == E1000_RCTL_RDMTS_EIGTH ? "1/8" :
+ "reserved",
+ reg & E1000_RCTL_BAM ? "accept" : "ignore",
+ reg & E1000_RCTL_VFE ? "enabled" : "disabled",
+ reg & E1000_RCTL_CFIEN ? "enabled" : "disabled",
+ reg & E1000_RCTL_DPF ? "ignored" : "filtered",
+ reg & E1000_RCTL_PMCF ? "pass" : "don't pass",
+ (reg & E1000_RCTL_LBM_MASK) == E1000_RCTL_LBM_NORM ? "normal" :
+ (reg & E1000_RCTL_LBM_MASK) == E1000_RCTL_LBM_MAC ? "MAC":
+ (reg & E1000_RCTL_LBM_MASK) == E1000_RCTL_LBM_SERDES ? "SERDES":
+ "undefined");
+ fprintf(stdout,
+ " Receive buffer size: %s\n",
+ (reg & E1000_RCTL_BSIZE)==E1000_RCTL_BSIZE_2048 ? "2048" :
+ (reg & E1000_RCTL_BSIZE)==E1000_RCTL_BSIZE_1024 ? "1024" :
+ (reg & E1000_RCTL_BSIZE)==E1000_RCTL_BSIZE_512 ? "512" :
+ "256");
+
+ /* Receive descriptor registers */
+ fprintf(stdout,
+ "0x02808: RDLEN (Receive desc length) 0x%08X\n",
+ regs_buff[137]);
+ fprintf(stdout,
+ "0x02810: RDH (Receive desc head) 0x%08X\n",
+ regs_buff[141]);
+ fprintf(stdout,
+ "0x02818: RDT (Receive desc tail) 0x%08X\n",
+ regs_buff[145]);
+
+ /* Transmit control register */
+ reg = regs_buff[38];
+ fprintf(stdout,
+ "0x00400: TCTL (Transmit ctrl register) 0x%08X\n"
+ " Transmitter: %s\n"
+ " Pad short packets: %s\n"
+ " Software XOFF Transmission: %s\n",
+ reg,
+ reg & E1000_TCTL_EN ? "enabled" : "disabled",
+ reg & E1000_TCTL_PSP ? "enabled" : "disabled",
+ reg & E1000_TCTL_SWXOFF ? "enabled" : "disabled");
+ fprintf(stdout,
+ " Re-transmit on late collision: %s\n",
+ reg & E1000_TCTL_RTLC ? "enabled" : "disabled");
+
+ /* Transmit descriptor registers */
+ fprintf(stdout,
+ "0x03808: TDLEN (Transmit desc length) 0x%08X\n",
+ regs_buff[219]);
+ fprintf(stdout,
+ "0x03810: TDH (Transmit desc head) 0x%08X\n",
+ regs_buff[223]);
+ fprintf(stdout,
+ "0x03818: TDT (Transmit desc tail) 0x%08X\n",
+ regs_buff[227]);
+
+
+ fprintf(stdout,
+ "0x00018: CTRL_EXT (Extended device control) 0x%08X\n",
+ regs_buff[2]);
+
+ fprintf(stdout,
+ "0x00018: MDIC (MDI control) 0x%08X\n",
+ regs_buff[3]);
+
+ fprintf(stdout,
+ "0x00024: SCTL (SERDES ANA) 0x%08X\n",
+ regs_buff[4]);
+
+ fprintf(stdout,
+ "0x00034: CONNSW (Copper/Fiber switch control) 0x%08X\n",
+ regs_buff[5]);
+
+ fprintf(stdout,
+ "0x00038: VET (VLAN Ether type) 0x%08X\n",
+ regs_buff[6]);
+
+ fprintf(stdout,
+ "0x00E00: LEDCTL (LED control) 0x%08X\n",
+ regs_buff[7]);
+
+ fprintf(stdout,
+ "0x01000: PBA (Packet buffer allocation) 0x%08X\n",
+ regs_buff[8]);
+
+ fprintf(stdout,
+ "0x01008: PBS (Packet buffer size) 0x%08X\n",
+ regs_buff[9]);
+
+ fprintf(stdout,
+ "0x01048: FRTIMER (Free running timer) 0x%08X\n",
+ regs_buff[10]);
+
+ fprintf(stdout,
+ "0x0104C: TCPTIMER (TCP timer) 0x%08X\n",
+ regs_buff[11]);
+
+ fprintf(stdout,
+ "0x00010: EEC (EEPROM/FLASH control) 0x%08X\n",
+ regs_buff[12]);
+
+ fprintf(stdout,
+ "0x01580: EICR (Extended interrupt cause) 0x%08X\n",
+ regs_buff[13]);
+
+ fprintf(stdout,
+ "0x01520: EICS (Extended interrupt cause set) 0x%08X\n",
+ regs_buff[14]);
+
+ fprintf(stdout,
+ "0x01524: EIMS (Extended interrup set/read) 0x%08X\n",
+ regs_buff[15]);
+
+ fprintf(stdout,
+ "0x01528: EIMC (Extended interrupt mask clear) 0x%08X\n",
+ regs_buff[16]);
+
+ fprintf(stdout,
+ "0x0152C: EIAC (Extended interrupt auto clear) 0x%08X\n",
+ regs_buff[17]);
+
+ fprintf(stdout,
+ "0x01530: EIAM (Extended interrupt auto mask) 0x%08X\n",
+ regs_buff[18]);
+
+ fprintf(stdout,
+ "0x01500: ICR (Interrupt cause read) 0x%08X\n",
+ regs_buff[19]);
+
+ fprintf(stdout,
+ "0x01504: ICS (Interrupt cause set) 0x%08X\n",
+ regs_buff[20]);
+
+ fprintf(stdout,
+ "0x01508: IMS (Interrupt mask set/read) 0x%08X\n",
+ regs_buff[21]);
+
+ fprintf(stdout,
+ "0x0150C: IMC (Interrupt mask clear) 0x%08X\n",
+ regs_buff[22]);
+
+ fprintf(stdout,
+ "0x04100: IAC (Interrupt assertion count) 0x%08X\n",
+ regs_buff[23]);
+
+ fprintf(stdout,
+ "0x01510: IAM (Interr acknowledge auto-mask) 0x%08X\n",
+ regs_buff[24]);
+
+ fprintf(stdout,
+ "0x05AC0: IMIRVP (Immed interr rx VLAN priority) 0x%08X\n",
+ regs_buff[25]);
+
+ fprintf(stdout,
+ "0x00028: FCAL (Flow control address low) 0x%08X\n",
+ regs_buff[26]);
+
+ fprintf(stdout,
+ "0x0002C: FCAH (Flow control address high) 0x%08X\n",
+ regs_buff[27]);
+
+ fprintf(stdout,
+ "0x00170: FCTTV (Flow control tx timer value) 0x%08X\n",
+ regs_buff[28]);
+
+ fprintf(stdout,
+ "0x02160: FCRTL (Flow control rx threshold low) 0x%08X\n",
+ regs_buff[29]);
+
+ fprintf(stdout,
+ "0x02168: FCRTH (Flow control rx threshold high) 0x%08X\n",
+ regs_buff[30]);
+
+ fprintf(stdout,
+ "0x02460: FCRTV (Flow control refresh threshold) 0x%08X\n",
+ regs_buff[31]);
+
+ fprintf(stdout,
+ "0x05000: RXCSUM (Receive checksum control) 0x%08X\n",
+ regs_buff[33]);
+
+ fprintf(stdout,
+ "0x05004: RLPML (Receive long packet max length) 0x%08X\n",
+ regs_buff[34]);
+
+ fprintf(stdout,
+ "0x05008: RFCTL (Receive filter control) 0x%08X\n",
+ regs_buff[35]);
+
+ fprintf(stdout,
+ "0x05818: MRQC (Multiple rx queues command) 0x%08X\n",
+ regs_buff[36]);
+
+ fprintf(stdout,
+ "0x0581C: VMD_CTL (VMDq control) 0x%08X\n",
+ regs_buff[37]);
+
+ fprintf(stdout,
+ "0x00404: TCTL_EXT (Transmit control extended) 0x%08X\n",
+ regs_buff[39]);
+
+ fprintf(stdout,
+ "0x00410: TIPG (Transmit IPG) 0x%08X\n",
+ regs_buff[40]);
+
+ fprintf(stdout,
+ "0x03590: DTXCTL (DMA tx control) 0x%08X\n",
+ regs_buff[41]);
+
+ fprintf(stdout,
+ "0x05800: WUC (Wake up control) 0x%08X\n",
+ regs_buff[42]);
+
+ fprintf(stdout,
+ "0x05808: WUFC (Wake up filter control) 0x%08X\n",
+ regs_buff[43]);
+
+ fprintf(stdout,
+ "0x05810: WUS (Wake up status) 0x%08X\n",
+ regs_buff[44]);
+
+ fprintf(stdout,
+ "0x05838: IPAV (IP address valid) 0x%08X\n",
+ regs_buff[45]);
+
+ fprintf(stdout,
+ "0x05900: WUPL (Wake up packet length) 0x%08X\n",
+ regs_buff[46]);
+
+ fprintf(stdout,
+ "0x04200: PCS_CFG (PCS configuration 0) 0x%08X\n",
+ regs_buff[47]);
+
+ fprintf(stdout,
+ "0x04208: PCS_LCTL (PCS link control) 0x%08X\n",
+ regs_buff[48]);
+
+ fprintf(stdout,
+ "0x0420C: PCS_LSTS (PCS link status) 0x%08X\n",
+ regs_buff[49]);
+
+ fprintf(stdout,
+ "0x04218: PCS_ANADV (AN advertisement) 0x%08X\n",
+ regs_buff[50]);
+
+ fprintf(stdout,
+ "0x0421C: PCS_LPAB (Link partner ability) 0x%08X\n",
+ regs_buff[51]);
+
+ fprintf(stdout,
+ "0x04220: PCS_NPTX (Next Page transmit) 0x%08X\n",
+ regs_buff[52]);
+
+ fprintf(stdout,
+ "0x04224: PCS_LPABNP (Link partner ability Next Page) 0x%08X\n",
+ regs_buff[53]);
+
+ fprintf(stdout,
+ "0x04000: CRCERRS (CRC error count) 0x%08X\n",
+ regs_buff[54]);
+
+ fprintf(stdout,
+ "0x04004: ALGNERRC (Alignment error count) 0x%08X\n",
+ regs_buff[55]);
+
+ fprintf(stdout,
+ "0x04008: SYMERRS (Symbol error count) 0x%08X\n",
+ regs_buff[56]);
+
+ fprintf(stdout,
+ "0x0400C: RXERRC (RX error count) 0x%08X\n",
+ regs_buff[57]);
+
+ fprintf(stdout,
+ "0x04010: MPC (Missed packets count) 0x%08X\n",
+ regs_buff[58]);
+
+ fprintf(stdout,
+ "0x04014: SCC (Single collision count) 0x%08X\n",
+ regs_buff[59]);
+
+ fprintf(stdout,
+ "0x04018: ECOL (Excessive collisions count) 0x%08X\n",
+ regs_buff[60]);
+
+ fprintf(stdout,
+ "0x0401C: MCC (Multiple collision count) 0x%08X\n",
+ regs_buff[61]);
+
+ fprintf(stdout,
+ "0x04020: LATECOL (Late collisions count) 0x%08X\n",
+ regs_buff[62]);
+
+ fprintf(stdout,
+ "0x04028: COLC (Collision count) 0x%08X\n",
+ regs_buff[63]);
+
+ fprintf(stdout,
+ "0x04030: DC (Defer count) 0x%08X\n",
+ regs_buff[64]);
+
+ fprintf(stdout,
+ "0x04034: TNCRS (Transmit with no CRS) 0x%08X\n",
+ regs_buff[65]);
+
+ fprintf(stdout,
+ "0x04038: SEC (Sequence error count) 0x%08X\n",
+ regs_buff[66]);
+
+ fprintf(stdout,
+ "0x0403C: HTDPMC (Host tx discrd pkts MAC count) 0x%08X\n",
+ regs_buff[67]);
+
+ fprintf(stdout,
+ "0x04040: RLEC (Receive length error count) 0x%08X\n",
+ regs_buff[68]);
+
+ fprintf(stdout,
+ "0x04048: XONRXC (XON received count) 0x%08X\n",
+ regs_buff[69]);
+
+ fprintf(stdout,
+ "0x0404C: XONTXC (XON transmitted count) 0x%08X\n",
+ regs_buff[70]);
+
+ fprintf(stdout,
+ "0x04050: XOFFRXC (XOFF received count) 0x%08X\n",
+ regs_buff[71]);
+
+ fprintf(stdout,
+ "0x04054: XOFFTXC (XOFF transmitted count) 0x%08X\n",
+ regs_buff[72]);
+
+ fprintf(stdout,
+ "0x04058: FCRUC (FC received unsupported count) 0x%08X\n",
+ regs_buff[73]);
+
+ fprintf(stdout,
+ "0x0405C: PRC64 (Packets rx (64 B) count) 0x%08X\n",
+ regs_buff[74]);
+
+ fprintf(stdout,
+ "0x04060: PRC127 (Packets rx (65-127 B) count) 0x%08X\n",
+ regs_buff[75]);
+
+ fprintf(stdout,
+ "0x04064: PRC255 (Packets rx (128-255 B) count) 0x%08X\n",
+ regs_buff[76]);
+
+ fprintf(stdout,
+ "0x04068: PRC511 (Packets rx (256-511 B) count) 0x%08X\n",
+ regs_buff[77]);
+
+ fprintf(stdout,
+ "0x0406C: PRC1023 (Packets rx (512-1023 B) count) 0x%08X\n",
+ regs_buff[78]);
+
+ fprintf(stdout,
+ "0x04070: PRC1522 (Packets rx (1024-max B) count) 0x%08X\n",
+ regs_buff[79]);
+
+ fprintf(stdout,
+ "0x04074: GPRC (Good packets received count) 0x%08X\n",
+ regs_buff[80]);
+
+ fprintf(stdout,
+ "0x04078: BPRC (Broadcast packets rx count) 0x%08X\n",
+ regs_buff[81]);
+
+ fprintf(stdout,
+ "0x0407C: MPRC (Multicast packets rx count) 0x%08X\n",
+ regs_buff[82]);
+
+ fprintf(stdout,
+ "0x04080: GPTC (Good packets tx count) 0x%08X\n",
+ regs_buff[83]);
+
+ fprintf(stdout,
+ "0x04088: GORCL (Good octets rx count lower) 0x%08X\n",
+ regs_buff[84]);
+
+ fprintf(stdout,
+ "0x0408C: GORCH (Good octets rx count upper) 0x%08X\n",
+ regs_buff[85]);
+
+ fprintf(stdout,
+ "0x04090: GOTCL (Good octets tx count lower) 0x%08X\n",
+ regs_buff[86]);
+
+ fprintf(stdout,
+ "0x04094: GOTCH (Good octets tx count upper) 0x%08X\n",
+ regs_buff[87]);
+
+ fprintf(stdout,
+ "0x040A0: RNBC (Receive no buffers count) 0x%08X\n",
+ regs_buff[88]);
+
+ fprintf(stdout,
+ "0x040A4: RUC (Receive undersize count) 0x%08X\n",
+ regs_buff[89]);
+
+ fprintf(stdout,
+ "0x040A8: RFC (Receive fragment count) 0x%08X\n",
+ regs_buff[90]);
+
+ fprintf(stdout,
+ "0x040AC: ROC (Receive oversize count) 0x%08X\n",
+ regs_buff[91]);
+
+ fprintf(stdout,
+ "0x040B0: RJC (Receive jabber count) 0x%08X\n",
+ regs_buff[92]);
+
+ fprintf(stdout,
+ "0x040B4: MGPRC (Management packets rx count) 0x%08X\n",
+ regs_buff[93]);
+
+ fprintf(stdout,
+ "0x040B8: MGPDC (Management pkts dropped count) 0x%08X\n",
+ regs_buff[94]);
+
+ fprintf(stdout,
+ "0x040BC: MGPTC (Management packets tx count) 0x%08X\n",
+ regs_buff[95]);
+
+ fprintf(stdout,
+ "0x040C0: TORL (Total octets received lower) 0x%08X\n",
+ regs_buff[96]);
+
+ fprintf(stdout,
+ "0x040C4: TORH (Total octets received upper) 0x%08X\n",
+ regs_buff[97]);
+
+ fprintf(stdout,
+ "0x040C8: TOTL (Total octets transmitted lower) 0x%08X\n",
+ regs_buff[98]);
+
+ fprintf(stdout,
+ "0x040CC: TOTH (Total octets transmitted upper) 0x%08X\n",
+ regs_buff[99]);
+
+ fprintf(stdout,
+ "0x040D0: TPR (Total packets received) 0x%08X\n",
+ regs_buff[100]);
+
+ fprintf(stdout,
+ "0x040D4: TPT (Total packets transmitted) 0x%08X\n",
+ regs_buff[101]);
+
+ fprintf(stdout,
+ "0x040D8: PTC64 (Packets tx (64 B) count) 0x%08X\n",
+ regs_buff[102]);
+
+ fprintf(stdout,
+ "0x040DC: PTC127 (Packets tx (65-127 B) count) 0x%08X\n",
+ regs_buff[103]);
+
+ fprintf(stdout,
+ "0x040E0: PTC255 (Packets tx (128-255 B) count) 0x%08X\n",
+ regs_buff[104]);
+
+ fprintf(stdout,
+ "0x040E4: PTC511 (Packets tx (256-511 B) count) 0x%08X\n",
+ regs_buff[105]);
+
+ fprintf(stdout,
+ "0x040E8: PTC1023 (Packets tx (512-1023 B) count) 0x%08X\n",
+ regs_buff[106]);
+
+ fprintf(stdout,
+ "0x040EC: PTC1522 (Packets tx (> 1024 B) count) 0x%08X\n",
+ regs_buff[107]);
+
+ fprintf(stdout,
+ "0x040F0: MPTC (Multicast packets tx count) 0x%08X\n",
+ regs_buff[108]);
+
+ fprintf(stdout,
+ "0x040F4: BPTC (Broadcast packets tx count) 0x%08X\n",
+ regs_buff[109]);
+
+ fprintf(stdout,
+ "0x040F8: TSCTC (TCP segment context tx count) 0x%08X\n",
+ regs_buff[110]);
+
+ fprintf(stdout,
+ "0x04100: IAC (Interrupt assertion count) 0x%08X\n",
+ regs_buff[111]);
+
+ fprintf(stdout,
+ "0x04104: RPTHC (Rx packets to host count) 0x%08X\n",
+ regs_buff[112]);
+
+ fprintf(stdout,
+ "0x04118: HGPTC (Host good packets tx count) 0x%08X\n",
+ regs_buff[113]);
+
+ fprintf(stdout,
+ "0x04128: HGORCL (Host good octets rx cnt lower) 0x%08X\n",
+ regs_buff[114]);
+
+ fprintf(stdout,
+ "0x0412C: HGORCH (Host good octets rx cnt upper) 0x%08X\n",
+ regs_buff[115]);
+
+ fprintf(stdout,
+ "0x04130: HGOTCL (Host good octets tx cnt lower) 0x%08X\n",
+ regs_buff[116]);
+
+ fprintf(stdout,
+ "0x04134: HGOTCH (Host good octets tx cnt upper) 0x%08X\n",
+ regs_buff[117]);
+
+ fprintf(stdout,
+ "0x04138: LENNERS (Length error count) 0x%08X\n",
+ regs_buff[118]);
+
+ fprintf(stdout,
+ "0x04228: SCVPC (SerDes/SGMII code viol pkt cnt) 0x%08X\n",
+ regs_buff[119]);
+
+ fprintf(stdout,
+ "0x0A018: HRMPC (Header redir missed pkt count) 0x%08X\n",
+ regs_buff[120]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: SRRCTL%d (Split and replic rx ctl%d) 0x%08X\n",
+ 0x0280C + (0x100 * i), i, i, regs_buff[121 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: PSRTYPE%d (Packet split receive type%d) 0x%08X\n",
+ 0x05480 + (0x4 * i), i, i, regs_buff[125 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: RDBAL%d (Rx desc base addr low%d) 0x%08X\n",
+ 0x02800 + (0x100 * i), i, i, regs_buff[129 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: RDBAH%d (Rx desc base addr high%d) 0x%08X\n",
+ 0x02804 + (0x100 * i), i, i, regs_buff[133 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: RDLEN%d (Rx descriptor length%d) 0x%08X\n",
+ 0x02808 + (0x100 * i), i, i, regs_buff[137 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: RDH%d (Rx descriptor head%d) 0x%08X\n",
+ 0x02810 + (0x100 * i), i, i, regs_buff[141 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: RDT%d (Rx descriptor tail%d) 0x%08X\n",
+ 0x02818 + (0x100 * i), i, i, regs_buff[145 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: RXDCTL%d (Rx descriptor control%d) 0x%08X\n",
+ 0x02828 + (0x100 * i), i, i, regs_buff[149 + i]);
+
+ for (i = 0; i < 10; i++)
+ fprintf(stdout,
+ "0x0%02X: EITR%d (Interrupt throttle%d) 0x%08X\n",
+ 0x01680 + (0x4 * i), i, i, regs_buff[153 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x0%02X: IMIR%d (Immediate interrupt Rx%d) 0x%08X\n",
+ 0x05A80 + (0x4 * i), i, i, regs_buff[163 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x0%02X: IMIREXT%d (Immediate interr Rx extended%d) 0x%08X\n",
+ 0x05AA0 + (0x4 * i), i, i, regs_buff[171 + i]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x0%02X: RAL%02d (Receive address low%02d) 0x%08X\n",
+ 0x05400 + (0x8 * i), i,i, regs_buff[179 + i]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x0%02X: RAH%02d (Receive address high%02d) 0x%08X\n",
+ 0x05404 + (0x8 * i), i, i, regs_buff[195 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: TDBAL%d (Tx desc base address low%d) 0x%08X\n",
+ 0x03800 + (0x100 * i), i, i, regs_buff[211 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: TDBAH%d (Tx desc base address high%d) 0x%08X\n",
+ 0x03804 + (0x100 * i), i, i, regs_buff[215 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: TDLEN%d (Tx descriptor length%d) 0x%08X\n",
+ 0x03808 + (0x100 * i), i, i, regs_buff[219 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: TDH%d (Transmit descriptor head%d) 0x%08X\n",
+ 0x03810 + (0x100 * i), i, i, regs_buff[223 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: TDT%d (Transmit descriptor tail%d) 0x%08X\n",
+ 0x03818 + (0x100 * i), i, i, regs_buff[227 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: TXDCTL%d (Transmit descriptor control%d) 0x%08X\n",
+ 0x03828 + (0x100 * i), i, i, regs_buff[231 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: TDWBAL%d (Tx desc complete wb addr low%d) 0x%08X\n",
+ 0x03838 + (0x100 * i), i, i, regs_buff[235 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: TDWBAH%d (Tx desc complete wb addr hi%d) 0x%08X\n",
+ 0x0383C + (0x100 * i), i, i, regs_buff[239 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: DCA_TXCTRL%d (Tx DCA control%d) 0x%08X\n",
+ 0x03814 + (0x100 * i), i, i, regs_buff[243 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: IP4AT%d (IPv4 address table%d) 0x%08X\n",
+ 0x05840 + (0x8 * i), i, i, regs_buff[247 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: IP6AT%d (IPv6 address table%d) 0x%08X\n",
+ 0x05880 + (0x4 * i), i, i, regs_buff[251 + i]);
+
+ for (i = 0; i < 32; i++)
+ fprintf(stdout,
+ "0x0%02X: WUPM%02d (Wake up packet memory%02d) 0x%08X\n",
+ 0x05A00 + (0x4 * i), i, i, regs_buff[255 + i]);
+
+ for (i = 0; i < 128; i++)
+ fprintf(stdout,
+ "0x0%02X: FFMT%03d (Flexible filter mask table%03d) 0x%08X\n",
+ 0x09000 + (0x8 * i), i, i, regs_buff[287 + i]);
+
+ for (i = 0; i < 128; i++)
+ fprintf(stdout,
+ "0x0%02X: FFVT%03d (Flexible filter value table%03d) 0x%08X\n",
+ 0x09800 + (0x8 * i), i, i, regs_buff[415 + i]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x0%02X: FFLT%d (Flexible filter length table%d) 0x%08X\n",
+ 0x05F00 + (0x8 * i), i, i, regs_buff[543 + i]);
+
+ fprintf(stdout,
+ "0x03410: TDFH (Tx data FIFO head) 0x%08X\n",
+ regs_buff[547]);
+
+ fprintf(stdout,
+ "0x03418: TDFT (Tx data FIFO tail) 0x%08X\n",
+ regs_buff[548]);
+
+ fprintf(stdout,
+ "0x03420: TDFHS (Tx data FIFO head saved) 0x%08X\n",
+ regs_buff[549]);
+
+ fprintf(stdout,
+ "0x03430: TDFPC (Tx data FIFO packet count) 0x%08X\n",
+ regs_buff[550]);
+
+ /*
+ * Starting from kernel version 5.3 the registers dump buffer grew from
+ * 739 4-byte words to 740 words, and word 740 contains the RR2DCDELAY
+ * register.
+ */
+ if (regs->len < 740)
+ return 0;
+
+ fprintf(stdout,
+ "0x05BF4: RR2DCDELAY (Max. DMA read delay) 0x%08X\n",
+ regs_buff[739]);
+
+ return 0;
+}
+
diff --git a/igc.c b/igc.c
new file mode 100644
index 0000000..1550ac0
--- /dev/null
+++ b/igc.c
@@ -0,0 +1,284 @@
+/* Copyright (c) 2020 Intel Corporation */
+#include <stdio.h>
+#include "internal.h"
+
+#define RAH_RAH 0x0000FFFF
+#define RAH_ASEL 0x00010000
+#define RAH_QSEL 0x000C0000
+#define RAH_QSEL_EN 0x10000000
+#define RAH_AV 0x80000000
+#define RCTL_RXEN 0x00000002
+#define RCTL_SBP 0x00000004
+#define RCTL_UPE 0x00000008
+#define RCTL_MPE 0x00000010
+#define RCTL_LPE 0x00000020
+#define RCTL_LBM 0x000000C0
+#define RCTL_LBM_PHY 0x00000000
+#define RCTL_LBM_MAC 0x00000040
+#define RCTL_HSEL 0x00000300
+#define RCTL_HSEL_MULTICAST 0x00000000
+#define RCTL_HSEL_UNICAST 0x00000100
+#define RCTL_HSEL_BOTH 0x00000200
+#define RCTL_MO 0x00003000
+#define RCTL_MO_47_36 0x00000000
+#define RCTL_MO_43_32 0x00001000
+#define RCTL_MO_39_28 0x00002000
+#define RCTL_BAM 0x00008000
+#define RCTL_BSIZE 0x00030000
+#define RCTL_BSIZE_2048 0x00000000
+#define RCTL_BSIZE_1024 0x00010000
+#define RCTL_BSIZE_512 0x00020000
+#define RCTL_VFE 0x00040000
+#define RCTL_CFIEN 0x00080000
+#define RCTL_CFI 0x00100000
+#define RCTL_PSP 0x00200000
+#define RCTL_DPF 0x00400000
+#define RCTL_PMCF 0x00800000
+#define RCTL_SECRC 0x04000000
+#define VLANPQF_VP0QSEL 0x00000003
+#define VLANPQF_VP0PBSEL 0x00000004
+#define VLANPQF_VLANP0V 0x00000008
+#define VLANPQF_VP1QSEL 0x00000030
+#define VLANPQF_VP1PBSEL 0x00000040
+#define VLANPQF_VLANP1V 0x00000080
+#define VLANPQF_VP2QSEL 0x00000300
+#define VLANPQF_VP2PBSEL 0x00000400
+#define VLANPQF_VLANP2V 0x00000800
+#define VLANPQF_VP3QSEL 0x00003000
+#define VLANPQF_VP3PBSEL 0x00004000
+#define VLANPQF_VLANP3V 0x00008000
+#define VLANPQF_VP4QSEL 0x00030000
+#define VLANPQF_VP4PBSEL 0x00040000
+#define VLANPQF_VLANP4V 0x00080000
+#define VLANPQF_VP5QSEL 0x00300000
+#define VLANPQF_VP5PBSEL 0x00400000
+#define VLANPQF_VLANP5V 0x00800000
+#define VLANPQF_VP6QSEL 0x03000000
+#define VLANPQF_VP6PBSEL 0x04000000
+#define VLANPQF_VLANP6V 0x08000000
+#define VLANPQF_VP7QSEL 0x30000000
+#define VLANPQF_VP7PBSEL 0x40000000
+#define VLANPQF_VLANP7V 0x80000000
+#define ETQF_ETYPE 0x0000FFFF
+#define ETQF_QUEUE 0x00070000
+#define ETQF_ETYPE_LEN 0x01F00000
+#define ETQF_ETYPE_LEN_EN 0x02000000
+#define ETQF_FILTER_EN 0x04000000
+#define ETQF_IMMEDIATE_INTR 0x20000000
+#define ETQF_1588_TIMESTAMP 0x40000000
+#define ETQF_QUEUE_EN 0x80000000
+
+#define RAH_QSEL_SHIFT 18
+#define VLANPQF_VP1QSEL_SHIFT 4
+#define VLANPQF_VP2QSEL_SHIFT 8
+#define VLANPQF_VP3QSEL_SHIFT 12
+#define VLANPQF_VP4QSEL_SHIFT 16
+#define VLANPQF_VP5QSEL_SHIFT 20
+#define VLANPQF_VP6QSEL_SHIFT 24
+#define VLANPQF_VP7QSEL_SHIFT 28
+#define ETQF_QUEUE_SHIFT 16
+#define ETQF_ETYPE_LEN_SHIFT 20
+
+static const char *bit_to_boolean(u32 val)
+{
+ return val ? "yes" : "no";
+}
+
+static const char *bit_to_enable(u32 val)
+{
+ return val ? "enabled" : "disabled";
+}
+
+static const char *bit_to_prio(u32 val)
+{
+ return val ? "low" : "high";
+}
+
+int igc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 reg;
+ int offset, i;
+ u32 *regs_buff = (u32 *)regs->data;
+ u8 version = (u8)(regs->version >> 24);
+
+ if (version != 2)
+ return -1;
+
+ for (offset = 0; offset < 24; offset++) {
+ reg = regs_buff[offset];
+ printf("%04d: 0x%08X\n", offset, reg);
+ }
+
+ offset = 24;
+
+ reg = regs_buff[offset];
+ printf("%04d: RCTL (Receive Control Register) \n"
+ " Receiver: %s\n"
+ " Stop Bad Packets: %s\n"
+ " Unicast Promiscuous: %s\n"
+ " Multicast Promiscuous: %s\n"
+ " Long Packet Reception: %s\n"
+ " Loopback Model: %s\n"
+ " Hash Select for MTA: %s\n"
+ " Multicast/Unicast Table Offset: %s\n"
+ " Broadcast Accept Mode: %s\n"
+ " Receive Buffer Size: %s\n"
+ " VLAN Filter: %s\n"
+ " Canonical Form Indicator: %s\n"
+ " Canonical Form Indicator Bit: %s\n"
+ " Pad Small Receive Packets: %s\n"
+ " Discard Pause Frames: %s\n"
+ " Pass MAC Control Frames: %s\n"
+ " Strip Ethernet CRC: %s\n",
+ offset,
+ bit_to_enable(reg & RCTL_RXEN),
+ bit_to_enable(reg & RCTL_SBP),
+ bit_to_enable(reg & RCTL_UPE),
+ bit_to_enable(reg & RCTL_MPE),
+ bit_to_enable(reg & RCTL_LPE),
+ (reg & RCTL_LBM) == RCTL_LBM_PHY ? "PHY" :
+ (reg & RCTL_LBM) == RCTL_LBM_MAC ? "MAC" :
+ "undefined",
+ (reg & RCTL_HSEL) == RCTL_HSEL_MULTICAST ? "multicast only" :
+ (reg & RCTL_HSEL) == RCTL_HSEL_UNICAST ? "unicast only" :
+ (reg & RCTL_HSEL) == RCTL_HSEL_BOTH ? "multicast and unicast" :
+ "reserved",
+ (reg & RCTL_MO) == RCTL_MO_47_36 ? "bits [47:36]" :
+ (reg & RCTL_MO) == RCTL_MO_43_32 ? "bits [43:32]" :
+ (reg & RCTL_MO) == RCTL_MO_39_28 ? "bits [39:28]" :
+ "bits [35:24]",
+ bit_to_enable(reg & RCTL_BAM),
+ (reg & RCTL_BSIZE) == RCTL_BSIZE_2048 ? "2048 bytes" :
+ (reg & RCTL_BSIZE) == RCTL_BSIZE_1024 ? "1024 bytes" :
+ (reg & RCTL_BSIZE) == RCTL_BSIZE_512 ? "512 bytes" :
+ "256 bytes",
+ bit_to_enable(reg & RCTL_VFE),
+ bit_to_enable(reg & RCTL_CFIEN),
+ reg & RCTL_CFI ? "discarded" : "accepted",
+ bit_to_enable(reg & RCTL_PSP),
+ bit_to_enable(reg & RCTL_DPF),
+ bit_to_enable(reg & RCTL_PMCF),
+ bit_to_enable(reg & RCTL_SECRC));
+
+ for (offset = 25; offset < 172; offset++) {
+ reg = regs_buff[offset];
+ printf("%04d: 0x%08X\n", offset, reg);
+ }
+
+ offset = 172;
+
+ for (i = 0; i < 16; i++) {
+ reg = regs_buff[offset + i];
+ printf("%04d: RAL (Receive Address Low %02d) \n"
+ " Receive Address Low: %08X\n",
+ offset + i, i,
+ reg);
+ }
+
+ offset = 188;
+
+ for (i = 0; i < 16; i++) {
+ reg = regs_buff[offset + i];
+ printf("%04d: RAH (Receive Address High %02d) \n"
+ " Receive Address High: %04X\n"
+ " Address Select: %s\n"
+ " Queue Select: %d\n"
+ " Queue Select Enable: %s\n"
+ " Address Valid: %s\n",
+ offset + i, i,
+ reg & RAH_RAH,
+ reg & RAH_ASEL ? "source" : "destination",
+ (reg & RAH_QSEL) >> RAH_QSEL_SHIFT,
+ bit_to_boolean(reg & RAH_QSEL_EN),
+ bit_to_boolean(reg & RAH_AV));
+ }
+
+ offset = 204;
+
+ reg = regs_buff[offset];
+ printf("%04d: VLANPQF (VLAN Priority Queue Filter) \n"
+ " Priority 0 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 1 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 2 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 3 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 4 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 5 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 6 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n"
+ " Priority 7 \n"
+ " Queue: %d\n"
+ " Packet Buffer: %s\n"
+ " Valid: %s\n",
+ offset,
+ reg & VLANPQF_VP0QSEL,
+ bit_to_prio(reg & VLANPQF_VP0PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP0V),
+ (reg & VLANPQF_VP1QSEL) >> VLANPQF_VP1QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP1PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP1V),
+ (reg & VLANPQF_VP2QSEL) >> VLANPQF_VP2QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP2PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP2V),
+ (reg & VLANPQF_VP3QSEL) >> VLANPQF_VP3QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP3PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP3V),
+ (reg & VLANPQF_VP4QSEL) >> VLANPQF_VP4QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP4PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP4V),
+ (reg & VLANPQF_VP5QSEL) >> VLANPQF_VP5QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP5PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP5V),
+ (reg & VLANPQF_VP6QSEL) >> VLANPQF_VP6QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP6PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP6V),
+ (reg & VLANPQF_VP7QSEL) >> VLANPQF_VP7QSEL_SHIFT,
+ bit_to_prio(reg & VLANPQF_VP7PBSEL),
+ bit_to_boolean(reg & VLANPQF_VLANP7V));
+
+ offset = 205;
+
+ for (i = 0; i < 8; i++) {
+ reg = regs_buff[offset + i];
+ printf("%04d: ETQF (EType Queue Filter %d) \n"
+ " EType: %04X\n"
+ " EType Length: %d\n"
+ " EType Length Enable: %s\n"
+ " Queue: %d\n"
+ " Queue Enable: %s\n"
+ " Immediate Interrupt: %s\n"
+ " 1588 Time Stamp: %s\n"
+ " Filter Enable: %s\n",
+ offset + i, i,
+ reg & ETQF_ETYPE,
+ (reg & ETQF_ETYPE_LEN) >> ETQF_ETYPE_LEN_SHIFT,
+ bit_to_boolean(reg & ETQF_ETYPE_LEN_EN),
+ (reg & ETQF_QUEUE) >> ETQF_QUEUE_SHIFT,
+ bit_to_boolean(reg & ETQF_QUEUE_EN),
+ bit_to_enable(reg & ETQF_IMMEDIATE_INTR),
+ bit_to_enable(reg & ETQF_1588_TIMESTAMP),
+ bit_to_boolean(reg & ETQF_FILTER_EN));
+ }
+
+ return 0;
+}
diff --git a/internal.h b/internal.h
new file mode 100644
index 0000000..4b994f5
--- /dev/null
+++ b/internal.h
@@ -0,0 +1,413 @@
+/* Portions Copyright 2001 Sun Microsystems (thockin@sun.com) */
+/* Portions Copyright 2002 Intel (scott.feldman@intel.com) */
+#ifndef ETHTOOL_INTERNAL_H__
+#define ETHTOOL_INTERNAL_H__
+
+/* Some platforms (eg. ppc64) need __SANE_USERSPACE_TYPES__ before
+ * <linux/types.h> to select 'int-ll64.h' and avoid compile warnings
+ * when printing __u64 with %llu.
+ */
+#define __SANE_USERSPACE_TYPES__
+
+#ifdef HAVE_CONFIG_H
+#include "ethtool-config.h"
+#endif
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <endian.h>
+#include <sys/ioctl.h>
+#define __UAPI_DEF_IF_IFNAMSIZ 1
+#define __UAPI_DEF_IF_IFMAP 1
+#define __UAPI_DEF_IF_IFREQ 1
+#include <linux/if.h>
+
+#include "json_writer.h"
+#include "json_print.h"
+
+#define __maybe_unused __attribute__((__unused__))
+
+/* internal for netlink interface */
+#ifdef ETHTOOL_ENABLE_NETLINK
+struct nl_context;
+#endif
+
+typedef unsigned long long u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int32_t s32;
+
+#include <linux/ethtool.h>
+#include <linux/net_tstamp.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static inline u16 cpu_to_be16(u16 value)
+{
+ return value;
+}
+static inline u32 cpu_to_be32(u32 value)
+{
+ return value;
+}
+static inline u64 cpu_to_be64(u64 value)
+{
+ return value;
+}
+#else
+static inline u16 cpu_to_be16(u16 value)
+{
+ return (value >> 8) | (value << 8);
+}
+static inline u32 cpu_to_be32(u32 value)
+{
+ return cpu_to_be16(value >> 16) | (cpu_to_be16(value) << 16);
+}
+static inline u64 cpu_to_be64(u64 value)
+{
+ return cpu_to_be32(value >> 32) | ((u64)cpu_to_be32(value) << 32);
+}
+#endif
+
+#define ntohll cpu_to_be64
+#define htonll cpu_to_be64
+
+#define BITS_PER_BYTE 8
+#define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long))
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_LONG)
+
+static inline void set_bit(unsigned int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
+}
+
+static inline void clear_bit(unsigned int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG));
+}
+
+static inline int test_bit(unsigned int nr, const unsigned long *addr)
+{
+ return !!((1UL << (nr % BITS_PER_LONG)) &
+ (((unsigned long *)addr)[nr / BITS_PER_LONG]));
+}
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#ifndef SIOCETHTOOL
+#define SIOCETHTOOL 0x8946
+#endif
+
+/* debugging flags */
+enum {
+ DEBUG_PARSE,
+ DEBUG_NL_MSGS, /* incoming/outgoing netlink messages */
+ DEBUG_NL_DUMP_SND, /* dump outgoing netlink messages */
+ DEBUG_NL_DUMP_RCV, /* dump incoming netlink messages */
+ DEBUG_NL_PRETTY_MSG, /* pretty print of messages and errors */
+};
+
+static inline bool debug_on(unsigned long debug, unsigned int bit)
+{
+ return (debug & (1 << bit));
+}
+
+/* Internal values for old-style offload flags. Values and names
+ * must not clash with the flags defined for ETHTOOL_{G,S}FLAGS.
+ */
+#define ETH_FLAG_RXCSUM (1 << 0)
+#define ETH_FLAG_TXCSUM (1 << 1)
+#define ETH_FLAG_SG (1 << 2)
+#define ETH_FLAG_TSO (1 << 3)
+#define ETH_FLAG_UFO (1 << 4)
+#define ETH_FLAG_GSO (1 << 5)
+#define ETH_FLAG_GRO (1 << 6)
+#define ETH_FLAG_INT_MASK (ETH_FLAG_RXCSUM | ETH_FLAG_TXCSUM | \
+ ETH_FLAG_SG | ETH_FLAG_TSO | ETH_FLAG_UFO | \
+ ETH_FLAG_GSO | ETH_FLAG_GRO),
+/* Mask of all flags defined for ETHTOOL_{G,S}FLAGS. */
+#define ETH_FLAG_EXT_MASK (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | \
+ ETH_FLAG_TXVLAN | ETH_FLAG_NTUPLE | \
+ ETH_FLAG_RXHASH)
+
+/* internal API for link mode bitmap interaction with kernel. */
+
+#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 \
+ (SCHAR_MAX)
+#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS \
+ (32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
+#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES \
+ (4 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
+#define ETHTOOL_DECLARE_LINK_MODE_MASK(name) \
+ u32 name[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]
+
+struct ethtool_link_usettings {
+ struct {
+ __u8 transceiver;
+ } deprecated;
+ struct ethtool_link_settings base;
+ struct {
+ ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
+ ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
+ ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
+ } link_modes;
+};
+
+#define ethtool_link_mode_for_each_u32(index) \
+ for ((index) = 0; \
+ (index) < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32; \
+ ++(index))
+
+static inline void ethtool_link_mode_zero(u32 *dst)
+{
+ memset(dst, 0, ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES);
+}
+
+static inline bool ethtool_link_mode_is_empty(const u32 *mask)
+{
+ unsigned int i;
+
+ ethtool_link_mode_for_each_u32(i) {
+ if (mask[i] != 0)
+ return false;
+ }
+
+ return true;
+}
+
+static inline void ethtool_link_mode_copy(u32 *dst, const u32 *src)
+{
+ memcpy(dst, src, ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES);
+}
+
+static inline int ethtool_link_mode_test_bit(unsigned int nr, const u32 *mask)
+{
+ if (nr >= ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS)
+ return !!0;
+ return !!(mask[nr / 32] & (1 << (nr % 32)));
+}
+
+static inline int ethtool_link_mode_set_bit(unsigned int nr, u32 *mask)
+{
+ if (nr >= ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS)
+ return -1;
+ mask[nr / 32] |= (1 << (nr % 32));
+ return 0;
+}
+
+/* Struct for managing module EEPROM pages */
+struct ethtool_module_eeprom {
+ u32 offset;
+ u32 length;
+ u8 page;
+ u8 bank;
+ u8 i2c_address;
+ u8 *data;
+};
+
+/* Context for sub-commands */
+struct cmd_context {
+ const char *devname; /* net device name */
+ int fd; /* socket suitable for ethtool ioctl */
+ struct ifreq ifr; /* ifreq suitable for ethtool ioctl */
+ unsigned int argc; /* number of arguments to the sub-command */
+ char **argp; /* arguments to the sub-command */
+ unsigned long debug; /* debugging mask */
+ bool json; /* Output JSON, if supported */
+ bool show_stats; /* include command-specific stats */
+#ifdef ETHTOOL_ENABLE_NETLINK
+ struct nl_context *nlctx; /* netlink context (opaque) */
+#endif
+};
+
+#ifdef TEST_ETHTOOL
+int test_cmdline(const char *args);
+
+struct cmd_expect {
+ const void *cmd; /* expected command; NULL at end of list */
+ size_t cmd_len; /* length to match (might be < sizeof struct) */
+ int rc; /* kernel return code */
+ const void *resp; /* response to write back; may be NULL */
+ size_t resp_len; /* length to write back */
+};
+int test_ioctl(const struct cmd_expect *expect, void *cmd);
+#define TEST_IOCTL_MISMATCH (-2)
+
+int test_main(int argc, char **argp);
+void test_exit(int rc) __attribute__((noreturn));
+
+#ifndef TEST_NO_WRAPPERS
+#define main(...) test_main(__VA_ARGS__)
+#undef exit
+#define exit(rc) test_exit(rc)
+void *test_malloc(size_t size);
+#undef malloc
+#define malloc(size) test_malloc(size)
+void *test_calloc(size_t nmemb, size_t size);
+#undef calloc
+#define calloc(nmemb, size) test_calloc(nmemb, size)
+char *test_strdup(const char *s);
+#undef strdup
+#define strdup(s) test_strdup(s)
+void test_free(void *ptr);
+#undef free
+#define free(ptr) test_free(ptr)
+void *test_realloc(void *ptr, size_t size);
+#undef realloc
+#define realloc(ptr, size) test_realloc(ptr, size)
+int test_open(const char *pathname, int flag, ...);
+#undef open
+#define open(...) test_open(__VA_ARGS__)
+int test_socket(int domain, int type, int protocol);
+#undef socket
+#define socket(...) test_socket(__VA_ARGS__)
+int test_close(int fd);
+#undef close
+#define close(fd) test_close(fd)
+FILE *test_fopen(const char *path, const char *mode);
+#undef fopen
+#define fopen(path, mode) test_fopen(path, mode)
+int test_fclose(FILE *fh);
+#undef fclose
+#define fclose(fh) test_fclose(fh)
+#endif
+#endif
+
+int send_ioctl(struct cmd_context *ctx, void *cmd);
+
+void dump_hex(FILE *f, const u8 *data, int len, int offset);
+
+/* National Semiconductor DP83815, DP83816 */
+int natsemi_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+int natsemi_dump_eeprom(struct ethtool_drvinfo *info,
+ struct ethtool_eeprom *ee);
+
+/* Digital/Intel 21040 and 21041 */
+int de2104x_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Intel(R) PRO/1000 Gigabit Adapter Family */
+int e1000_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+int igb_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* RealTek PCI */
+int realtek_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Intel(R) PRO/100 Fast Ethernet Adapter Family */
+int e100_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Tigon3 */
+int tg3_dump_eeprom(struct ethtool_drvinfo *info, struct ethtool_eeprom *ee);
+
+/* Advanced Micro Devices AMD8111 based Adapter */
+int amd8111e_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Advanced Micro Devices PCnet32 Adapter */
+int pcnet32_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Motorola 8xx FEC Ethernet controller */
+int fec_8xx_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* PowerPC 4xx on-chip Ethernet controller */
+int ibm_emac_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Intel(R) PRO/10GBe Gigabit Adapter Family */
+int ixgb_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+int ixgbe_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+int ixgbevf_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Broadcom Tigon3 Ethernet controller */
+int tg3_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* SysKonnect Gigabit (Genesis and Yukon) */
+int skge_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* SysKonnect Gigabit (Yukon2) */
+int sky2_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Fabric7 VIOC */
+int vioc_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* SMSC LAN911x/LAN921x embedded ethernet controller */
+int smsc911x_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+int at76c50x_usb_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Solarflare Solarstorm controllers */
+int sfc_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* STMMAC embedded ethernet controller */
+int st_mac100_dump_regs(struct ethtool_drvinfo *info,
+ struct ethtool_regs *regs);
+int st_gmac_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Et131x ethernet controller */
+int et131x_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Altera TSE 10/100/1000 ethernet controller */
+int altera_tse_dump_regs(struct ethtool_drvinfo *info,
+ struct ethtool_regs *regs);
+
+/* VMware vmxnet3 ethernet controller */
+int vmxnet3_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* hns3 ethernet controller */
+int hns3_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Rx flow classification */
+int rxclass_parse_ruleopts(struct cmd_context *ctx,
+ struct ethtool_rx_flow_spec *fsp, __u32 *rss_context);
+int rxclass_rule_getall(struct cmd_context *ctx);
+int rxclass_rule_get(struct cmd_context *ctx, __u32 loc);
+int rxclass_rule_ins(struct cmd_context *ctx,
+ struct ethtool_rx_flow_spec *fsp, __u32 rss_context);
+int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
+
+/* Module EEPROM parsing code */
+void sff8079_show_all_ioctl(const __u8 *id);
+int sff8079_show_all_nl(struct cmd_context *ctx);
+
+/* Optics diagnostics */
+void sff8472_show_all(const __u8 *id);
+
+/* QSFP Optics diagnostics */
+void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len);
+int sff8636_show_all_nl(struct cmd_context *ctx);
+
+/* FUJITSU Extended Socket network device */
+int fjes_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* MICROCHIP LAN78XX USB ETHERNET Controller */
+int lan78xx_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Distributed Switch Architecture */
+int dsa_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* i.MX Fast Ethernet Controller */
+int fec_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Freescale/NXP ENETC Ethernet Controller */
+int fsl_enetc_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Intel(R) Ethernet Controller I225-LM/I225-V adapter family */
+int igc_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Broadcom Ethernet Controller */
+int bnxt_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* TI CPSW Ethernet Switch */
+int cpsw_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+/* Microchip Ethernet Controller */
+int lan743x_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+
+#endif /* ETHTOOL_INTERNAL_H__ */
diff --git a/ixgb.c b/ixgb.c
new file mode 100644
index 0000000..8aec9a9
--- /dev/null
+++ b/ixgb.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2006 Intel Corporation */
+#include <stdio.h>
+#include "internal.h"
+
+/* CTRL0 Bit Masks */
+#define IXGB_CTRL0_LRST 0x00000008
+#define IXGB_CTRL0_VME 0x40000000
+
+/* STATUS Bit Masks */
+#define IXGB_STATUS_LU 0x00000002
+#define IXGB_STATUS_BUS64 0x00001000
+#define IXGB_STATUS_PCIX_MODE 0x00002000
+#define IXGB_STATUS_PCIX_SPD_100 0x00004000
+#define IXGB_STATUS_PCIX_SPD_133 0x00008000
+
+/* RCTL Bit Masks */
+#define IXGB_RCTL_RXEN 0x00000002
+#define IXGB_RCTL_SBP 0x00000004
+#define IXGB_RCTL_UPE 0x00000008
+#define IXGB_RCTL_MPE 0x00000010
+#define IXGB_RCTL_RDMTS_MASK 0x00000300
+#define IXGB_RCTL_RDMTS_1_2 0x00000000
+#define IXGB_RCTL_RDMTS_1_4 0x00000100
+#define IXGB_RCTL_RDMTS_1_8 0x00000200
+#define IXGB_RCTL_BAM 0x00008000
+#define IXGB_RCTL_BSIZE_MASK 0x00030000
+#define IXGB_RCTL_BSIZE_4096 0x00010000
+#define IXGB_RCTL_BSIZE_8192 0x00020000
+#define IXGB_RCTL_BSIZE_16384 0x00030000
+#define IXGB_RCTL_VFE 0x00040000
+#define IXGB_RCTL_CFIEN 0x00080000
+
+/* TCTL Bit Masks */
+#define IXGB_TCTL_TXEN 0x00000002
+
+/* RAH Bit Masks */
+#define IXGB_RAH_ASEL_DEST 0x00000000
+#define IXGB_RAH_ASEL_SRC 0x00010000
+#define IXGB_RAH_AV 0x80000000
+
+int ixgb_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *regs_buff = (u32 *)regs->data;
+ u8 version = (u8)(regs->version >> 24);
+ u32 reg;
+
+ if (version != 1)
+ return -1;
+ fprintf(stdout, "MAC Registers\n");
+ fprintf(stdout, "-------------\n");
+
+ /* Device control register */
+ reg = regs_buff[0];
+ fprintf(stdout,
+ "0x00000: CTRL0 (Device control register) 0x%08X\n"
+ " Link reset: %s\n"
+ " VLAN mode: %s\n",
+ reg,
+ reg & IXGB_CTRL0_LRST ? "reset" : "normal",
+ reg & IXGB_CTRL0_VME ? "enabled" : "disabled");
+
+ /* Device status register */
+ reg = regs_buff[2];
+ fprintf(stdout,
+ "0x00010: STATUS (Device status register) 0x%08X\n"
+ " Link up: %s\n"
+ " Bus type: %s\n"
+ " Bus speed: %s\n"
+ " Bus width: %s\n",
+ reg,
+ (reg & IXGB_STATUS_LU) ? "link config" : "no link config",
+ (reg & IXGB_STATUS_PCIX_MODE) ? "PCI-X" : "PCI",
+ ((reg & IXGB_STATUS_PCIX_SPD_133) ? "133MHz" :
+ (reg & IXGB_STATUS_PCIX_SPD_100) ? "100MHz" :
+ "66MHz"),
+ (reg & IXGB_STATUS_BUS64) ? "64-bit" : "32-bit");
+ /* Receive control register */
+ reg = regs_buff[9];
+ fprintf(stdout,
+ "0x00100: RCTL (Receive control register) 0x%08X\n"
+ " Receiver: %s\n"
+ " Store bad packets: %s\n"
+ " Unicast promiscuous: %s\n"
+ " Multicast promiscuous: %s\n"
+ " Descriptor minimum threshold size: %s\n"
+ " Broadcast accept mode: %s\n"
+ " VLAN filter: %s\n"
+ " Cononical form indicator: %s\n",
+ reg,
+ reg & IXGB_RCTL_RXEN ? "enabled" : "disabled",
+ reg & IXGB_RCTL_SBP ? "enabled" : "disabled",
+ reg & IXGB_RCTL_UPE ? "enabled" : "disabled",
+ reg & IXGB_RCTL_MPE ? "enabled" : "disabled",
+ (reg & IXGB_RCTL_RDMTS_MASK) == IXGB_RCTL_RDMTS_1_2 ? "1/2" :
+ (reg & IXGB_RCTL_RDMTS_MASK) == IXGB_RCTL_RDMTS_1_4 ? "1/4" :
+ (reg & IXGB_RCTL_RDMTS_MASK) == IXGB_RCTL_RDMTS_1_8 ? "1/8" :
+ "reserved",
+ reg & IXGB_RCTL_BAM ? "accept" : "ignore",
+ reg & IXGB_RCTL_VFE ? "enabled" : "disabled",
+ reg & IXGB_RCTL_CFIEN ? "enabled" : "disabled");
+ fprintf(stdout,
+ " Receive buffer size: %s\n",
+ (reg & IXGB_RCTL_BSIZE_MASK) == IXGB_RCTL_BSIZE_16384 ? "16384" :
+ (reg & IXGB_RCTL_BSIZE_MASK) == IXGB_RCTL_BSIZE_8192 ? "8192" :
+ (reg & IXGB_RCTL_BSIZE_MASK) == IXGB_RCTL_BSIZE_4096 ? "4096" :
+ "2048");
+
+ /* Receive descriptor registers */
+ fprintf(stdout,
+ "0x00120: RDLEN (Receive desc length) 0x%08X\n",
+ regs_buff[14]);
+ fprintf(stdout,
+ "0x00128: RDH (Receive desc head) 0x%08X\n",
+ regs_buff[15]);
+ fprintf(stdout,
+ "0x00130: RDT (Receive desc tail) 0x%08X\n",
+ regs_buff[16]);
+ fprintf(stdout,
+ "0x00138: RDTR (Receive delay timer) 0x%08X\n",
+ regs_buff[17]);
+
+ /* Transmit control register */
+ reg = regs_buff[53];
+ fprintf(stdout,
+ "0x00600: TCTL (Transmit ctrl register) 0x%08X\n"
+ " Transmitter: %s\n",
+ reg,
+ reg & IXGB_TCTL_TXEN ? "enabled" : "disabled");
+
+ /* Transmit descriptor registers */
+ fprintf(stdout,
+ "0x00610: TDLEN (Transmit desc length) 0x%08X\n",
+ regs_buff[56]);
+ fprintf(stdout,
+ "0x00618: TDH (Transmit desc head) 0x%08X\n",
+ regs_buff[57]);
+ fprintf(stdout,
+ "0x00620: TDT (Transmit desc tail) 0x%08X\n",
+ regs_buff[58]);
+ fprintf(stdout,
+ "0x00628: TIDV (Transmit delay timer) 0x%08X\n",
+ regs_buff[59]);
+
+ return 0;
+}
+
diff --git a/ixgbe.c b/ixgbe.c
new file mode 100644
index 0000000..6d509c8
--- /dev/null
+++ b/ixgbe.c
@@ -0,0 +1,1296 @@
+/* Copyright (c) 2007 Intel Corporation */
+#include <stdio.h>
+#include "internal.h"
+
+/* Register Bit Masks */
+#define IXGBE_FCTRL_SBP 0x00000002
+#define IXGBE_FCTRL_MPE 0x00000100
+#define IXGBE_FCTRL_UPE 0x00000200
+#define IXGBE_FCTRL_BAM 0x00000400
+#define IXGBE_FCTRL_PMCF 0x00001000
+#define IXGBE_FCTRL_DPF 0x00002000
+#define IXGBE_FCTRL_RPFCE 0x00004000
+#define IXGBE_FCTRL_RFCE 0x00008000
+#define IXGBE_VLNCTRL_VET 0x0000FFFF
+#define IXGBE_VLNCTRL_CFI 0x10000000
+#define IXGBE_VLNCTRL_CFIEN 0x20000000
+#define IXGBE_VLNCTRL_VFE 0x40000000
+#define IXGBE_VLNCTRL_VME 0x80000000
+#define IXGBE_LINKS_UP 0x40000000
+#define IXGBE_LINKS_SPEED 0x20000000
+#define IXGBE_SRRCTL_BSIZEPKT_MASK 0x0000007F
+#define IXGBE_HLREG0_TXCRCEN 0x00000001
+#define IXGBE_HLREG0_RXCRCSTRP 0x00000002
+#define IXGBE_HLREG0_JUMBOEN 0x00000004
+#define IXGBE_HLREG0_TXPADEN 0x00000400
+#define IXGBE_HLREG0_LPBK 0x00008000
+#define IXGBE_RMCS_TFCE_802_3X 0x00000008
+#define IXGBE_RMCS_TFCE_PRIORITY 0x00000010
+#define IXGBE_FCCFG_TFCE_802_3X 0x00000008
+#define IXGBE_FCCFG_TFCE_PRIORITY 0x00000010
+#define IXGBE_MFLCN_PMCF 0x00000001 /* Pass MAC Control Frames */
+#define IXGBE_MFLCN_DPF 0x00000002 /* Discard Pause Frame */
+#define IXGBE_MFLCN_RPFCE 0x00000004 /* Receive Priority FC Enable */
+#define IXGBE_MFLCN_RFCE 0x00000008 /* Receive FC Enable */
+
+/* Device IDs */
+#define IXGBE_DEV_ID_82598 0x10B6
+#define IXGBE_DEV_ID_82598_BX 0x1508
+#define IXGBE_DEV_ID_82598AF_DUAL_PORT 0x10C6
+#define IXGBE_DEV_ID_82598AF_SINGLE_PORT 0x10C7
+#define IXGBE_DEV_ID_82598EB_SFP_LOM 0x10DB
+#define IXGBE_DEV_ID_82598AT 0x10C8
+#define IXGBE_DEV_ID_82598AT2 0x150B
+#define IXGBE_DEV_ID_82598EB_CX4 0x10DD
+#define IXGBE_DEV_ID_82598_CX4_DUAL_PORT 0x10EC
+#define IXGBE_DEV_ID_82598_DA_DUAL_PORT 0x10F1
+#define IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM 0x10E1
+#define IXGBE_DEV_ID_82598EB_XF_LR 0x10F4
+#define IXGBE_DEV_ID_82599_KX4 0x10F7
+#define IXGBE_DEV_ID_82599_KX4_MEZZ 0x1514
+#define IXGBE_DEV_ID_82599_KR 0x1517
+#define IXGBE_DEV_ID_82599_T3_LOM 0x151C
+#define IXGBE_DEV_ID_82599_CX4 0x10F9
+#define IXGBE_DEV_ID_82599_SFP 0x10FB
+#define IXGBE_DEV_ID_82599_BACKPLANE_FCOE 0x152a
+#define IXGBE_DEV_ID_82599_SFP_FCOE 0x1529
+#define IXGBE_SUBDEV_ID_82599_SFP 0x11A9
+#define IXGBE_DEV_ID_82599_SFP_EM 0x1507
+#define IXGBE_DEV_ID_82599_SFP_SF2 0x154D
+#define IXGBE_DEV_ID_82599EN_SFP 0x1557
+#define IXGBE_DEV_ID_82599_XAUI_LOM 0x10FC
+#define IXGBE_DEV_ID_82599_COMBO_BACKPLANE 0x10F8
+#define IXGBE_SUBDEV_ID_82599_KX4_KR_MEZZ 0x000C
+#define IXGBE_DEV_ID_82599_LS 0x154F
+#define IXGBE_DEV_ID_X540T 0x1528
+#define IXGBE_DEV_ID_82599_SFP_SF_QP 0x154A
+#define IXGBE_DEV_ID_82599_QSFP_SF_QP 0x1558
+#define IXGBE_DEV_ID_X540T1 0x1560
+
+#define IXGBE_DEV_ID_X550T 0x1563
+#define IXGBE_DEV_ID_X550T1 0x15D1
+#define IXGBE_DEV_ID_X550EM_X_KX4 0x15AA
+#define IXGBE_DEV_ID_X550EM_X_KR 0x15AB
+#define IXGBE_DEV_ID_X550EM_X_SFP 0x15AC
+#define IXGBE_DEV_ID_X550EM_X_10G_T 0x15AD
+#define IXGBE_DEV_ID_X550EM_X_1G_T 0x15AE
+#define IXGBE_DEV_ID_X550EM_A_KR 0x15C2
+#define IXGBE_DEV_ID_X550EM_A_KR_L 0x15C3
+#define IXGBE_DEV_ID_X550EM_A_SFP_N 0x15C4
+#define IXGBE_DEV_ID_X550EM_A_SGMII 0x15C6
+#define IXGBE_DEV_ID_X550EM_A_SGMII_L 0x15C7
+#define IXGBE_DEV_ID_X550EM_A_SFP 0x15CE
+
+/*
+ * Enumerated types specific to the ixgbe hardware
+ * Media Access Controlers
+ */
+enum ixgbe_mac_type {
+ ixgbe_mac_unknown = 0,
+ ixgbe_mac_82598EB,
+ ixgbe_mac_82599EB,
+ ixgbe_mac_X540,
+ ixgbe_mac_x550,
+ ixgbe_mac_x550em_x,
+ ixgbe_mac_x550em_a,
+ ixgbe_num_macs
+};
+
+static enum ixgbe_mac_type
+ixgbe_get_mac_type(u16 device_id)
+{
+ enum ixgbe_mac_type mac_type = ixgbe_mac_unknown;
+
+ switch (device_id) {
+ case IXGBE_DEV_ID_82598:
+ case IXGBE_DEV_ID_82598_BX:
+ case IXGBE_DEV_ID_82598AF_DUAL_PORT:
+ case IXGBE_DEV_ID_82598AF_SINGLE_PORT:
+ case IXGBE_DEV_ID_82598EB_SFP_LOM:
+ case IXGBE_DEV_ID_82598AT:
+ case IXGBE_DEV_ID_82598AT2:
+ case IXGBE_DEV_ID_82598EB_CX4:
+ case IXGBE_DEV_ID_82598_CX4_DUAL_PORT:
+ case IXGBE_DEV_ID_82598_DA_DUAL_PORT:
+ case IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM:
+ case IXGBE_DEV_ID_82598EB_XF_LR:
+ mac_type = ixgbe_mac_82598EB;
+ break;
+ case IXGBE_DEV_ID_82599_KX4:
+ case IXGBE_DEV_ID_82599_KX4_MEZZ:
+ case IXGBE_DEV_ID_82599_KR:
+ case IXGBE_DEV_ID_82599_T3_LOM:
+ case IXGBE_DEV_ID_82599_CX4:
+ case IXGBE_DEV_ID_82599_SFP:
+ case IXGBE_DEV_ID_82599_BACKPLANE_FCOE:
+ case IXGBE_DEV_ID_82599_SFP_FCOE:
+ case IXGBE_SUBDEV_ID_82599_SFP:
+ case IXGBE_DEV_ID_82599_SFP_EM:
+ case IXGBE_DEV_ID_82599_SFP_SF2:
+ case IXGBE_DEV_ID_82599EN_SFP:
+ case IXGBE_DEV_ID_82599_XAUI_LOM:
+ case IXGBE_DEV_ID_82599_COMBO_BACKPLANE:
+ case IXGBE_SUBDEV_ID_82599_KX4_KR_MEZZ:
+ case IXGBE_DEV_ID_82599_LS:
+ case IXGBE_DEV_ID_82599_SFP_SF_QP:
+ case IXGBE_DEV_ID_82599_QSFP_SF_QP:
+ mac_type = ixgbe_mac_82599EB;
+ break;
+ case IXGBE_DEV_ID_X540T:
+ case IXGBE_DEV_ID_X540T1:
+ mac_type = ixgbe_mac_X540;
+ break;
+ case IXGBE_DEV_ID_X550T:
+ case IXGBE_DEV_ID_X550T1:
+ mac_type = ixgbe_mac_x550;
+ break;
+ case IXGBE_DEV_ID_X550EM_X_KX4:
+ case IXGBE_DEV_ID_X550EM_X_KR:
+ case IXGBE_DEV_ID_X550EM_X_SFP:
+ case IXGBE_DEV_ID_X550EM_X_10G_T:
+ case IXGBE_DEV_ID_X550EM_X_1G_T:
+ mac_type = ixgbe_mac_x550em_x;
+ break;
+ case IXGBE_DEV_ID_X550EM_A_KR:
+ case IXGBE_DEV_ID_X550EM_A_KR_L:
+ case IXGBE_DEV_ID_X550EM_A_SFP_N:
+ case IXGBE_DEV_ID_X550EM_A_SGMII:
+ case IXGBE_DEV_ID_X550EM_A_SGMII_L:
+ case IXGBE_DEV_ID_X550EM_A_SFP:
+ mac_type = ixgbe_mac_x550em_a;
+ break;
+ default:
+ mac_type = ixgbe_mac_82598EB;
+ break;
+ }
+
+ return mac_type;
+}
+
+int
+ixgbe_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *regs_buff = (u32 *)regs->data;
+ u32 regs_buff_len = regs->len / sizeof(*regs_buff);
+ u32 reg;
+ u32 offset;
+ u16 hw_device_id = (u16) regs->version;
+ u8 version = (u8)(regs->version >> 24);
+ u8 i;
+ enum ixgbe_mac_type mac_type;
+
+ if (version == 0)
+ return -1;
+
+ /* The current driver reports the MAC type, but older versions
+ * only report the device ID so we have to infer the MAC type.
+ */
+ mac_type = version > 1 ? version : ixgbe_get_mac_type(hw_device_id);
+
+ reg = regs_buff[1065];
+ fprintf(stdout,
+ "0x042A4: LINKS (Link Status register) 0x%08X\n"
+ " Link Status: %s\n"
+ " Link Speed: %s\n",
+ reg,
+ reg & IXGBE_LINKS_UP ? "up" : "down",
+ reg & IXGBE_LINKS_SPEED ? "10G" : "1G");
+
+ reg = regs_buff[515];
+ fprintf(stdout,
+ "0x05080: FCTRL (Filter Control register) 0x%08X\n"
+ " Broadcast Accept: %s\n"
+ " Unicast Promiscuous: %s\n"
+ " Multicast Promiscuous: %s\n"
+ " Store Bad Packets: %s\n",
+ reg,
+ reg & IXGBE_FCTRL_BAM ? "enabled" : "disabled",
+ reg & IXGBE_FCTRL_UPE ? "enabled" : "disabled",
+ reg & IXGBE_FCTRL_MPE ? "enabled" : "disabled",
+ reg & IXGBE_FCTRL_SBP ? "enabled" : "disabled");
+
+ /* Some FCTRL bits are valid only on 82598 */
+ if (mac_type == ixgbe_mac_82598EB) {
+ fprintf(stdout,
+ " Receive Flow Control Packets: %s\n"
+ " Receive Priority Flow Control Packets: %s\n"
+ " Discard Pause Frames: %s\n"
+ " Pass MAC Control Frames: %s\n",
+ reg & IXGBE_FCTRL_RFCE ? "enabled" : "disabled",
+ reg & IXGBE_FCTRL_RPFCE ? "enabled" : "disabled",
+ reg & IXGBE_FCTRL_DPF ? "enabled" : "disabled",
+ reg & IXGBE_FCTRL_PMCF ? "enabled" : "disabled");
+ }
+
+ reg = regs_buff[1128];
+ if (mac_type >= ixgbe_mac_82599EB) {
+ fprintf(stdout,
+ "0x04294: MFLCN (TabMAC Flow Control register) 0x%08X\n"
+ " Receive Flow Control Packets: %s\n"
+ " Discard Pause Frames: %s\n"
+ " Pass MAC Control Frames: %s\n"
+ " Receive Priority Flow Control Packets: %s\n",
+ reg,
+ reg & IXGBE_MFLCN_RFCE ? "enabled" : "disabled",
+ reg & IXGBE_MFLCN_DPF ? "enabled" : "disabled",
+ reg & IXGBE_MFLCN_PMCF ? "enabled" : "disabled",
+ reg & IXGBE_MFLCN_RPFCE ? "enabled" : "disabled");
+ }
+
+ reg = regs_buff[516];
+ fprintf(stdout,
+ "0x05088: VLNCTRL (VLAN Control register) 0x%08X\n"
+ " VLAN Mode: %s\n"
+ " VLAN Filter: %s\n",
+ reg,
+ reg & IXGBE_VLNCTRL_VME ? "enabled" : "disabled",
+ reg & IXGBE_VLNCTRL_VFE ? "enabled" : "disabled");
+
+ reg = regs_buff[437];
+ fprintf(stdout,
+ "0x02100: SRRCTL0 (Split and Replic Rx Control 0) 0x%08X\n"
+ " Receive Buffer Size: %uKB\n",
+ reg,
+ (reg & IXGBE_SRRCTL_BSIZEPKT_MASK) <= 0x10 ? (reg & IXGBE_SRRCTL_BSIZEPKT_MASK) : 0x10);
+
+ reg = regs_buff[829];
+ if (mac_type == ixgbe_mac_82598EB) {
+ fprintf(stdout,
+ "0x03D00: RMCS (Receive Music Control register) 0x%08X\n"
+ " Transmit Flow Control: %s\n"
+ " Priority Flow Control: %s\n",
+ reg,
+ reg & IXGBE_RMCS_TFCE_802_3X ? "enabled" : "disabled",
+ reg & IXGBE_RMCS_TFCE_PRIORITY ? "enabled" : "disabled");
+ } else if (mac_type >= ixgbe_mac_82599EB) {
+ fprintf(stdout,
+ "0x03D00: FCCFG (Flow Control Configuration) 0x%08X\n"
+ " Transmit Flow Control: %s\n"
+ " Priority Flow Control: %s\n",
+ reg,
+ reg & IXGBE_FCCFG_TFCE_802_3X ? "enabled" : "disabled",
+ reg & IXGBE_FCCFG_TFCE_PRIORITY ? "enabled" : "disabled");
+ }
+
+ reg = regs_buff[1047];
+ fprintf(stdout,
+ "0x04240: HLREG0 (Highlander Control 0 register) 0x%08X\n"
+ " Transmit CRC: %s\n"
+ " Receive CRC Strip: %s\n"
+ " Jumbo Frames: %s\n"
+ " Pad Short Frames: %s\n"
+ " Loopback: %s\n",
+ reg,
+ reg & IXGBE_HLREG0_TXCRCEN ? "enabled" : "disabled",
+ reg & IXGBE_HLREG0_RXCRCSTRP ? "enabled" : "disabled",
+ reg & IXGBE_HLREG0_JUMBOEN ? "enabled" : "disabled",
+ reg & IXGBE_HLREG0_TXPADEN ? "enabled" : "disabled",
+ reg & IXGBE_HLREG0_LPBK ? "enabled" : "disabled");
+
+ /* General Registers */
+ fprintf(stdout,
+ "0x00000: CTRL (Device Control) 0x%08X\n",
+ regs_buff[0]);
+
+ fprintf(stdout,
+ "0x00008: STATUS (Device Status) 0x%08X\n",
+ regs_buff[1]);
+
+ fprintf(stdout,
+ "0x00018: CTRL_EXT (Extended Device Control) 0x%08X\n",
+ regs_buff[2]);
+
+ fprintf(stdout,
+ "0x00020: ESDP (Extended SDP Control) 0x%08X\n",
+ regs_buff[3]);
+
+ fprintf(stdout,
+ "0x00028: EODSDP (Extended OD SDP Control) 0x%08X\n",
+ regs_buff[4]);
+
+ fprintf(stdout,
+ "0x00200: LEDCTL (LED Control) 0x%08X\n",
+ regs_buff[5]);
+
+ fprintf(stdout,
+ "0x00048: FRTIMER (Free Running Timer) 0x%08X\n",
+ regs_buff[6]);
+
+ fprintf(stdout,
+ "0x0004C: TCPTIMER (TCP Timer) 0x%08X\n",
+ regs_buff[7]);
+
+ /* NVM Register */
+ offset = mac_type == ixgbe_mac_x550em_a ? 0x15FF8 : 0x10010;
+ fprintf(stdout,
+ "0x%05X: EEC (EEPROM/Flash Control) 0x%08X\n",
+ offset, regs_buff[8]);
+
+ fprintf(stdout,
+ "0x10014: EERD (EEPROM Read) 0x%08X\n",
+ regs_buff[9]);
+
+ offset = mac_type == ixgbe_mac_x550em_a ? 0x15F6C : 0x1001C;
+ fprintf(stdout,
+ "0x%05X: FLA (Flash Access) 0x%08X\n",
+ offset, regs_buff[10]);
+
+ fprintf(stdout,
+ "0x10110: EEMNGCTL (Manageability EEPROM Control) 0x%08X\n",
+ regs_buff[11]);
+
+ fprintf(stdout,
+ "0x10114: EEMNGDATA (Manageability EEPROM R/W Data) 0x%08X\n",
+ regs_buff[12]);
+
+ fprintf(stdout,
+ "0x10118: FLMNGCTL (Manageability Flash Control) 0x%08X\n",
+ regs_buff[13]);
+
+ fprintf(stdout,
+ "0x1011C: FLMNGDATA (Manageability Flash Read Data) 0x%08X\n",
+ regs_buff[14]);
+
+ fprintf(stdout,
+ "0x10120: FLMNGCNT (Manageability Flash Read Count) 0x%08X\n",
+ regs_buff[15]);
+
+ fprintf(stdout,
+ "0x1013C: FLOP (Flash Opcode) 0x%08X\n",
+ regs_buff[16]);
+
+ offset = mac_type == ixgbe_mac_x550em_a ? 0x15F64 : 0x10200;
+ fprintf(stdout,
+ "0x%05X: GRC (General Receive Control) 0x%08X\n",
+ offset, regs_buff[17]);
+
+ /* Interrupt */
+ fprintf(stdout,
+ "0x00800: EICR (Extended Interrupt Cause) 0x%08X\n",
+ regs_buff[18]);
+
+ fprintf(stdout,
+ "0x00808: EICS (Extended Interrupt Cause Set) 0x%08X\n",
+ regs_buff[19]);
+
+ fprintf(stdout,
+ "0x00880: EIMS (Extended Interr. Mask Set/Read) 0x%08X\n",
+ regs_buff[20]);
+
+ fprintf(stdout,
+ "0x00888: EIMC (Extended Interrupt Mask Clear) 0x%08X\n",
+ regs_buff[21]);
+
+ fprintf(stdout,
+ "0x00810: EIAC (Extended Interrupt Auto Clear) 0x%08X\n",
+ regs_buff[22]);
+
+ fprintf(stdout,
+ "0x00890: EIAM (Extended Interr. Auto Mask EN) 0x%08X\n",
+ regs_buff[23]);
+
+ fprintf(stdout,
+ "0x00820: EITR0 (Extended Interrupt Throttle 0) 0x%08X\n",
+ regs_buff[24]);
+
+ fprintf(stdout,
+ "0x00900: IVAR0 (Interrupt Vector Allocation 0) 0x%08X\n",
+ regs_buff[25]);
+
+ fprintf(stdout,
+ "0x00000: MSIXT (MSI-X Table) 0x%08X\n",
+ regs_buff[26]);
+
+ if (mac_type == ixgbe_mac_82598EB)
+ fprintf(stdout,
+ "0x02000: MSIXPBA (MSI-X Pending Bit Array) 0x%08X\n",
+ regs_buff[27]);
+
+ fprintf(stdout,
+ "0x11068: PBACL (MSI-X PBA Clear) 0x%08X\n",
+ regs_buff[28]);
+
+ fprintf(stdout,
+ "0x00898: GPIE (General Purpose Interrupt EN) 0x%08X\n",
+ regs_buff[29]);
+
+ /* Flow Control */
+ fprintf(stdout,
+ "0x03008: PFCTOP (Priority Flow Ctrl Type Opcode) 0x%08X\n",
+ regs_buff[30]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x%05X: FCCTV%d (Flow Ctrl Tx Timer Value %d) 0x%08X\n",
+ 0x03200 + (4 * i), i, i, regs_buff[31 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: FCRTL%d (Flow Ctrl Rx Threshold low %d) 0x%08X\n",
+ 0x3220 + (8 * i), i, i, regs_buff[35 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: FCRTH%d (Flow Ctrl Rx Threshold High %d) 0x%08X\n",
+ 0x3260 + (8 * i), i, i, regs_buff[43 + i]);
+
+ fprintf(stdout,
+ "0x032A0: FCRTV (Flow Control Refresh Threshold) 0x%08X\n",
+ regs_buff[51]);
+
+ fprintf(stdout,
+ "0x0CE00: TFCS (Transmit Flow Control Status) 0x%08X\n",
+ regs_buff[52]);
+
+ /* Receive DMA */
+ for (i = 0; i < 64; i++)
+ fprintf(stdout,
+ "0x%05X: RDBAL%02d (Rx Desc Base Addr Low %02d) 0x%08X\n",
+ 0x01000 + (0x40 * i), i, i, regs_buff[53 + i]);
+
+ for (i = 0; i < 64; i++)
+ fprintf(stdout,
+ "0x%05X: RDBAH%02d (Rx Desc Base Addr High %02d) 0x%08X\n",
+ 0x01004 + (0x40 * i), i, i, regs_buff[117 + i]);
+
+ for (i = 0; i < 64; i++)
+ fprintf(stdout,
+ "0x%05X: RDLEN%02d (Receive Descriptor Length %02d) 0x%08X\n",
+ 0x01008 + (0x40 * i), i, i, regs_buff[181 + i]);
+
+ for (i = 0; i < 64; i++)
+ fprintf(stdout,
+ "0x%05X: RDH%02d (Receive Descriptor Head %02d) 0x%08X\n",
+ 0x01010 + (0x40 * i), i, i, regs_buff[245 + i]);
+
+ for (i = 0; i < 64; i++)
+ fprintf(stdout,
+ "0x%05X: RDT%02d (Receive Descriptor Tail %02d) 0x%08X\n",
+ 0x01018 + (0x40 * i), i, i, regs_buff[309 + i]);
+
+ for (i = 0; i < 64; i++)
+ fprintf(stdout,
+ "0x%05X: RXDCTL%02d (Receive Descriptor Control %02d) 0x%08X\n",
+ 0x01028 + (0x40 * i), i, i, regs_buff[373 + i]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x%05X: SRRCTL%02d (Split and Replic Rx Control %02d) 0x%08X\n",
+ 0x02100 + (4 * i), i, i, regs_buff[437 + i]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x%05X: DCA_RXCTRL%02d (Rx DCA Control %02d) 0x%08X\n",
+ 0x02200 + (4 * i), i, i, regs_buff[453 + i]);
+
+ fprintf(stdout,
+ "0x02F00: RDRXCTL (Receive DMA Control) 0x%08X\n",
+ regs_buff[469]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RXPBSIZE%d (Receive Packet Buffer Size %d) 0x%08X\n",
+ 0x3C00 + (4 * i), i, i, regs_buff[470 + i]);
+
+ fprintf(stdout,
+ "0x03000: RXCTRL (Receive Control) 0x%08X\n",
+ regs_buff[478]);
+
+ if (mac_type == ixgbe_mac_82598EB)
+ fprintf(stdout,
+ "0x03D04: DROPEN (Drop Enable Control) 0x%08X\n",
+ regs_buff[479]);
+
+ /* Receive */
+ fprintf(stdout,
+ "0x05000: RXCSUM (Receive Checksum Control) 0x%08X\n",
+ regs_buff[480]);
+
+ fprintf(stdout,
+ "0x05008: RFCTL (Receive Filter Control) 0x%08X\n",
+ regs_buff[481]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x%05X: RAL%02d (Receive Address Low%02d) 0x%08X\n",
+ 0x05400 + (8 * i), i, i, regs_buff[482 + i]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x%05X: RAH%02d (Receive Address High %02d) 0x%08X\n",
+ 0x05404 + (8 * i), i, i, regs_buff[498 + i]);
+
+ fprintf(stdout,
+ "0x05480: PSRTYPE (Packet Split Receive Type) 0x%08X\n",
+ regs_buff[514]);
+
+ fprintf(stdout,
+ "0x05090: MCSTCTRL (Multicast Control) 0x%08X\n",
+ regs_buff[517]);
+
+ fprintf(stdout,
+ "0x05818: MRQC (Multiple Rx Queues Command) 0x%08X\n",
+ regs_buff[518]);
+
+ fprintf(stdout,
+ "0x0581C: VMD_CTL (VMDq Control) 0x%08X\n",
+ regs_buff[519]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: IMIR%d (Immediate Interrupt Rx %d) 0x%08X\n",
+ 0x05A80 + (4 * i), i, i, regs_buff[520 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: IMIREXT%d (Immed. Interr. Rx Extended %d) 0x%08X\n",
+ 0x05AA0 + (4 * i), i, i, regs_buff[528 + i]);
+
+ fprintf(stdout,
+ "0x05AC0: IMIRVP (Immed. Interr. Rx VLAN Prior.) 0x%08X\n",
+ regs_buff[536]);
+
+ /* Transmit */
+ for (i = 0; i < 32; i++)
+ fprintf(stdout,
+ "0x%05X: TDBAL%02d (Tx Desc Base Addr Low %02d) 0x%08X\n",
+ 0x06000 + (0x40 * i), i, i, regs_buff[537 + i]);
+
+ for (i = 0; i < 32; i++)
+ fprintf(stdout,
+ "0x%05X: TDBAH%02d (Tx Desc Base Addr High %02d) 0x%08X\n",
+ 0x06004 + (0x40 * i), i, i, regs_buff[569 + i]);
+
+ for (i = 0; i < 32; i++)
+ fprintf(stdout,
+ "0x%05X: TDLEN%02d (Tx Descriptor Length %02d) 0x%08X\n",
+ 0x06008 + (0x40 * i), i, i, regs_buff[601 + i]);
+
+ for (i = 0; i < 32; i++)
+ fprintf(stdout,
+ "0x%05X: TDH%02d (Transmit Descriptor Head %02d) 0x%08X\n",
+ 0x06010 + (0x40 * i), i, i, regs_buff[633 + i]);
+
+ for (i = 0; i < 32; i++)
+ fprintf(stdout,
+ "0x%05X: TDT%02d (Transmit Descriptor Tail %02d) 0x%08X\n",
+ 0x06018 + (0x40 * i), i, i, regs_buff[665 + i]);
+
+ for (i = 0; i < 32; i++)
+ fprintf(stdout,
+ "0x%05X: TXDCTL%02d (Tx Descriptor Control %02d) 0x%08X\n",
+ 0x06028 + (0x40 * i), i, i, regs_buff[697 + i]);
+
+ for (i = 0; i < 32; i++)
+ fprintf(stdout,
+ "0x%05X: TDWBAL%02d (Tx Desc Compl. WB Addr low %02d) 0x%08X\n",
+ 0x06038 + (0x40 * i), i, i, regs_buff[729 + i]);
+
+ for (i = 0; i < 32; i++)
+ fprintf(stdout,
+ "0x%05X: TDWBAH%02d (Tx Desc Compl. WB Addr High %02d) 0x%08X\n",
+ 0x0603C + (0x40 * i), i, i, regs_buff[761 + i]);
+
+ fprintf(stdout,
+ "0x07E00: DTXCTL (DMA Tx Control) 0x%08X\n",
+ regs_buff[793]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x%05X: DCA_TXCTRL%02d (Tx DCA Control %02d) 0x%08X\n",
+ 0x07200 + (4 * i), i, i, regs_buff[794 + i]);
+
+ if (mac_type == ixgbe_mac_82598EB)
+ fprintf(stdout,
+ "0x0CB00: TIPG (Transmit IPG Control) 0x%08X\n",
+ regs_buff[810]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: TXPBSIZE%d (Transmit Packet Buffer Size %d) 0x%08X\n",
+ 0x0CC00 + (4 * i), i, i, regs_buff[811 + i]);
+
+ fprintf(stdout,
+ "0x0CD10: MNGTXMAP (Manageability Tx TC Mapping) 0x%08X\n",
+ regs_buff[819]);
+
+ /* Wake Up */
+ fprintf(stdout,
+ "0x05800: WUC (Wake up Control) 0x%08X\n",
+ regs_buff[820]);
+
+ fprintf(stdout,
+ "0x05808: WUFC (Wake Up Filter Control) 0x%08X\n",
+ regs_buff[821]);
+
+ fprintf(stdout,
+ "0x05810: WUS (Wake Up Status) 0x%08X\n",
+ regs_buff[822]);
+
+ fprintf(stdout,
+ "0x05838: IPAV (IP Address Valid) 0x%08X\n",
+ regs_buff[823]);
+
+ fprintf(stdout,
+ "0x05840: IP4AT (IPv4 Address Table) 0x%08X\n",
+ regs_buff[824]);
+
+ fprintf(stdout,
+ "0x05880: IP6AT (IPv6 Address Table) 0x%08X\n",
+ regs_buff[825]);
+
+ fprintf(stdout,
+ "0x05900: WUPL (Wake Up Packet Length) 0x%08X\n",
+ regs_buff[826]);
+
+ fprintf(stdout,
+ "0x05A00: WUPM (Wake Up Packet Memory) 0x%08X\n",
+ regs_buff[827]);
+
+ fprintf(stdout,
+ "0x09000: FHFT (Flexible Host Filter Table) 0x%08X\n",
+ regs_buff[828]);
+
+ /* DCB */
+ if (mac_type == ixgbe_mac_82598EB) {
+ fprintf(stdout,
+ "0x07F40: DPMCS (Desc. Plan Music Ctrl Status) 0x%08X\n",
+ regs_buff[830]);
+
+ fprintf(stdout,
+ "0x0CD00: PDPMCS (Pkt Data Plan Music ctrl Stat) 0x%08X\n",
+ regs_buff[831]);
+
+ fprintf(stdout,
+ "0x050A0: RUPPBMR (Rx User Prior to Pkt Buff Map) 0x%08X\n",
+ regs_buff[832]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RT2CR%d (Receive T2 Configure %d) 0x%08X\n",
+ 0x03C20 + (4 * i), i, i, regs_buff[833 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RT2SR%d (Receive T2 Status %d) 0x%08X\n",
+ 0x03C40 + (4 * i), i, i, regs_buff[841 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: TDTQ2TCCR%d (Tx Desc TQ2 TC Config %d) 0x%08X\n",
+ 0x0602C + (0x40 * i), i, i, regs_buff[849 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: TDTQ2TCSR%d (Tx Desc TQ2 TC Status %d) 0x%08X\n",
+ 0x0622C + (0x40 * i), i, i, regs_buff[857 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: TDPT2TCCR%d (Tx Data Plane T2 TC Config %d) 0x%08X\n",
+ 0x0CD20 + (4 * i), i, i, regs_buff[865 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: TDPT2TCSR%d (Tx Data Plane T2 TC Status %d) 0x%08X\n",
+ 0x0CD40 + (4 * i), i, i, regs_buff[873 + i]);
+ } else if (mac_type >= ixgbe_mac_82599EB && mac_type <= ixgbe_mac_x550) {
+ fprintf(stdout,
+ "0x04900: RTTDCS (Tx Descr Plane Ctrl&Status) 0x%08X\n",
+ regs_buff[830]);
+
+ fprintf(stdout,
+ "0x0CD00: RTTPCS (Tx Pkt Plane Ctrl&Status) 0x%08X\n",
+ regs_buff[831]);
+
+ fprintf(stdout,
+ "0x02430: RTRPCS (Rx Packet Plane Ctrl&Status) 0x%08X\n",
+ regs_buff[832]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RTRPT4C%d (Rx Packet Plane T4 Config %d) 0x%08X\n",
+ 0x02140 + (4 * i), i, i, regs_buff[833 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RTRPT4S%d (Rx Packet Plane T4 Status %d) 0x%08X\n",
+ 0x02160 + (4 * i), i, i, regs_buff[841 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RTTDT2C%d (Tx Descr Plane T2 Config %d) 0x%08X\n",
+ 0x04910 + (4 * i), i, i, regs_buff[849 + i]);
+
+ if (mac_type < ixgbe_mac_x550)
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RTTDT2S%d (Tx Descr Plane T2 Status %d) 0x%08X\n",
+ 0x04930 + (4 * i), i, i, regs_buff[857 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RTTPT2C%d (Tx Packet Plane T2 Config %d) 0x%08X\n",
+ 0x0CD20 + (4 * i), i, i, regs_buff[865]);
+
+ if (mac_type < ixgbe_mac_x550)
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RTTPT2S%d (Tx Packet Plane T2 Status %d) 0x%08X\n",
+ 0x0CD40 + (4 * i), i, i, regs_buff[873 + i]);
+ }
+
+ if (regs_buff_len > 1129 && mac_type != ixgbe_mac_82598EB) {
+ fprintf(stdout,
+ "0x03020: RTRUP2TC (Rx User Prio to Traffic Classes)0x%08X\n",
+ regs_buff[1129]);
+
+ fprintf(stdout,
+ "0x0C800: RTTUP2TC (Tx User Prio to Traffic Classes)0x%08X\n",
+ regs_buff[1130]);
+
+ if (mac_type <= ixgbe_mac_x550)
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x%05X: TXLLQ%d (Strict Low Lat Tx Queues %d) 0x%08X\n",
+ 0x082E0 + (4 * i), i, i, regs_buff[1131 + i]);
+
+ if (mac_type == ixgbe_mac_82599EB) {
+ fprintf(stdout,
+ "0x04980: RTTBCNRM (DCB TX Rate Sched MMW) 0x%08X\n",
+ regs_buff[1135]);
+
+ fprintf(stdout,
+ "0x0498C: RTTBCNRD (DCB TX Rate-Scheduler Drift) 0x%08X\n",
+ regs_buff[1136]);
+ } else if (mac_type <= ixgbe_mac_x550) {
+ fprintf(stdout,
+ "0x04980: RTTQCNRM (DCB TX QCN Rate Sched MMW) 0x%08X\n",
+ regs_buff[1135]);
+
+ fprintf(stdout,
+ "0x0498C: RTTQCNRR (DCB TX QCN Rate Reset) 0x%08X\n",
+ regs_buff[1136]);
+
+ if (mac_type < ixgbe_mac_x550)
+ fprintf(stdout,
+ "0x08B00: RTTQCNCR (DCB TX QCN Control) 0x%08X\n",
+ regs_buff[1137]);
+
+ fprintf(stdout,
+ "0x04A90: RTTQCNTG (DCB TX QCN Tagging) 0x%08X\n",
+ regs_buff[1138]);
+ }
+ }
+
+ /* Statistics */
+ fprintf(stdout,
+ "0x04000: crcerrs (CRC Error Count) 0x%08X\n",
+ regs_buff[881]);
+
+ fprintf(stdout,
+ "0x04004: illerrc (Illegal Byte Error Count) 0x%08X\n",
+ regs_buff[882]);
+
+ fprintf(stdout,
+ "0x04008: errbc (Error Byte Count) 0x%08X\n",
+ regs_buff[883]);
+
+ fprintf(stdout,
+ "0x04010: mspdc (MAC Short Packet Discard Count) 0x%08X\n",
+ regs_buff[884]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: mpc%d (Missed Packets Count %d) 0x%08X\n",
+ 0x03FA0 + (4 * i), i, i, regs_buff[885 + i]);
+
+ fprintf(stdout,
+ "0x04034: mlfc (MAC Local Fault Count) 0x%08X\n",
+ regs_buff[893]);
+
+ fprintf(stdout,
+ "0x04038: mrfc (MAC Remote Fault Count) 0x%08X\n",
+ regs_buff[894]);
+
+ fprintf(stdout,
+ "0x04040: rlec (Receive Length Error Count) 0x%08X\n",
+ regs_buff[895]);
+
+ fprintf(stdout,
+ "0x03F60: lxontxc (Link XON Transmitted Count) 0x%08X\n",
+ regs_buff[896]);
+
+ fprintf(stdout,
+ "0x0CF60: lxonrxc (Link XON Received Count) 0x%08X\n",
+ regs_buff[897]);
+
+ fprintf(stdout,
+ "0x03F68: lxofftxc (Link XOFF Transmitted Count) 0x%08X\n",
+ regs_buff[898]);
+
+ fprintf(stdout,
+ "0x0CF68: lxoffrxc (Link XOFF Received Count) 0x%08X\n",
+ regs_buff[899]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: pxontxc%d (Priority XON Tx Count %d) 0x%08X\n",
+ 0x03F00 + (4 * i), i, i, regs_buff[900 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: pxonrxc%d (Priority XON Received Count %d) 0x%08X\n",
+ 0x0CF00 + (4 * i), i, i, regs_buff[908 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: pxofftxc%d (Priority XOFF Tx Count %d) 0x%08X\n",
+ 0x03F20 + (4 * i), i, i, regs_buff[916 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: pxoffrxc%d (Priority XOFF Received Count %d) 0x%08X\n",
+ 0x0CF20 + (4 * i), i, i, regs_buff[924 + i]);
+
+ fprintf(stdout,
+ "0x0405C: prc64 (Packets Received (64B) Count) 0x%08X\n",
+ regs_buff[932]);
+
+ fprintf(stdout,
+ "0x04060: prc127 (Packets Rx (65-127B) Count) 0x%08X\n",
+ regs_buff[933]);
+
+ fprintf(stdout,
+ "0x04064: prc255 (Packets Rx (128-255B) Count) 0x%08X\n",
+ regs_buff[934]);
+
+ fprintf(stdout,
+ "0x04068: prc511 (Packets Rx (256-511B) Count) 0x%08X\n",
+ regs_buff[935]);
+
+ fprintf(stdout,
+ "0x0406C: prc1023 (Packets Rx (512-1023B) Count) 0x%08X\n",
+ regs_buff[936]);
+
+ fprintf(stdout,
+ "0x04070: prc1522 (Packets Rx (1024-Max) Count) 0x%08X\n",
+ regs_buff[937]);
+
+ fprintf(stdout,
+ "0x04074: gprc (Good Packets Received Count) 0x%08X\n",
+ regs_buff[938]);
+
+ fprintf(stdout,
+ "0x04078: bprc (Broadcast Packets Rx Count) 0x%08X\n",
+ regs_buff[939]);
+
+ fprintf(stdout,
+ "0x0407C: mprc (Multicast Packets Rx Count) 0x%08X\n",
+ regs_buff[940]);
+
+ fprintf(stdout,
+ "0x04080: gptc (Good Packets Transmitted Count) 0x%08X\n",
+ regs_buff[941]);
+
+ fprintf(stdout,
+ "0x04088: gorcl (Good Octets Rx Count Low) 0x%08X\n",
+ regs_buff[942]);
+
+ fprintf(stdout,
+ "0x0408C: gorch (Good Octets Rx Count High) 0x%08X\n",
+ regs_buff[943]);
+
+ fprintf(stdout,
+ "0x04090: gotcl (Good Octets Tx Count Low) 0x%08X\n",
+ regs_buff[944]);
+
+ fprintf(stdout,
+ "0x04094: gotch (Good Octets Tx Count High) 0x%08X\n",
+ regs_buff[945]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: rnbc%d (Receive No Buffers Count %d) 0x%08X\n",
+ 0x03FC0 + (4 * i), i, i, regs_buff[946 + i]);
+
+ fprintf(stdout,
+ "0x040A4: ruc (Receive Undersize count) 0x%08X\n",
+ regs_buff[954]);
+
+ fprintf(stdout,
+ "0x040A8: rfc (Receive Fragment Count) 0x%08X\n",
+ regs_buff[955]);
+
+ fprintf(stdout,
+ "0x040AC: roc (Receive Oversize Count) 0x%08X\n",
+ regs_buff[956]);
+
+ fprintf(stdout,
+ "0x040B0: rjc (Receive Jabber Count) 0x%08X\n",
+ regs_buff[957]);
+
+ fprintf(stdout,
+ "0x040B4: mngprc (Management Packets Rx Count) 0x%08X\n",
+ regs_buff[958]);
+
+ fprintf(stdout,
+ "0x040B8: mngpdc (Management Pkts Dropped Count) 0x%08X\n",
+ regs_buff[959]);
+
+ fprintf(stdout,
+ "0x0CF90: mngptc (Management Packets Tx Count) 0x%08X\n",
+ regs_buff[960]);
+
+ fprintf(stdout,
+ "0x040C0: torl (Total Octets Rx Count Low) 0x%08X\n",
+ regs_buff[961]);
+
+ fprintf(stdout,
+ "0x040C4: torh (Total Octets Rx Count High) 0x%08X\n",
+ regs_buff[962]);
+
+ fprintf(stdout,
+ "0x040D0: tpr (Total Packets Received) 0x%08X\n",
+ regs_buff[963]);
+
+ fprintf(stdout,
+ "0x040D4: tpt (Total Packets Transmitted) 0x%08X\n",
+ regs_buff[964]);
+
+ fprintf(stdout,
+ "0x040D8: ptc64 (Packets Tx (64B) Count) 0x%08X\n",
+ regs_buff[965]);
+
+ fprintf(stdout,
+ "0x040DC: ptc127 (Packets Tx (65-127B) Count) 0x%08X\n",
+ regs_buff[966]);
+
+ fprintf(stdout,
+ "0x040E0: ptc255 (Packets Tx (128-255B) Count) 0x%08X\n",
+ regs_buff[967]);
+
+ fprintf(stdout,
+ "0x040E4: ptc511 (Packets Tx (256-511B) Count) 0x%08X\n",
+ regs_buff[968]);
+
+ fprintf(stdout,
+ "0x040E8: ptc1023 (Packets Tx (512-1023B) Count) 0x%08X\n",
+ regs_buff[969]);
+
+ fprintf(stdout,
+ "0x040EC: ptc1522 (Packets Tx (1024-Max) Count) 0x%08X\n",
+ regs_buff[970]);
+
+ fprintf(stdout,
+ "0x040F0: mptc (Multicast Packets Tx Count) 0x%08X\n",
+ regs_buff[971]);
+
+ fprintf(stdout,
+ "0x040F4: bptc (Broadcast Packets Tx Count) 0x%08X\n",
+ regs_buff[972]);
+
+ fprintf(stdout,
+ "0x04120: xec (XSUM Error Count) 0x%08X\n",
+ regs_buff[973]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x%05X: qprc%02d (Queue Packets Rx Count %02d) 0x%08X\n",
+ 0x01030 + (0x40 * i), i, i, regs_buff[974 + i]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x%05X: qptc%02d (Queue Packets Tx Count %02d) 0x%08X\n",
+ 0x06030 + (0x40 * i), i, i, regs_buff[990 + i]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x%05X: qbrc%02d (Queue Bytes Rx Count %02d) 0x%08X\n",
+ 0x01034 + (0x40 * i), i, i, regs_buff[1006 + i]);
+
+ for (i = 0; i < 16; i++)
+ fprintf(stdout,
+ "0x%05X: qbtc%02d (Queue Bytes Tx Count %02d) 0x%08X\n",
+ 0x06034 + (0x40 * i), i, i, regs_buff[1022 + i]);
+
+ /* MAC */
+ if (mac_type < ixgbe_mac_X540) {
+ fprintf(stdout,
+ "0x04200: PCS1GCFIG (PCS_1G Gloabal Config 1) 0x%08X\n",
+ regs_buff[1038]);
+
+ fprintf(stdout,
+ "0x04208: PCS1GLCTL (PCS_1G Link Control) 0x%08X\n",
+ regs_buff[1039]);
+
+ fprintf(stdout,
+ "0x0420C: PCS1GLSTA (PCS_1G Link Status) 0x%08X\n",
+ regs_buff[1040]);
+
+ fprintf(stdout,
+ "0x04210: PCS1GDBG0 (PCS_1G Debug 0) 0x%08X\n",
+ regs_buff[1041]);
+
+ fprintf(stdout,
+ "0x04214: PCS1GDBG1 (PCS_1G Debug 1) 0x%08X\n",
+ regs_buff[1042]);
+
+ fprintf(stdout,
+ "0x04218: PCS1GANA (PCS-1G Auto Neg. Adv.) 0x%08X\n",
+ regs_buff[1043]);
+
+ fprintf(stdout,
+ "0x0421C: PCS1GANLP (PCS-1G AN LP Ability) 0x%08X\n",
+ regs_buff[1044]);
+
+ fprintf(stdout,
+ "0x04220: PCS1GANNP (PCS_1G Auto Neg Next Page Tx) 0x%08X\n",
+ regs_buff[1045]);
+
+ fprintf(stdout,
+ "0x04224: PCS1GANLPNP (PCS_1G Auto Neg LPs Next Page) 0x%08X\n",
+ regs_buff[1046]);
+ }
+
+ fprintf(stdout,
+ "0x04244: HLREG1 (Highlander Status 1) 0x%08X\n",
+ regs_buff[1048]);
+
+ fprintf(stdout,
+ "0x04248: PAP (Pause and Pace) 0x%08X\n",
+ regs_buff[1049]);
+
+ fprintf(stdout,
+ "0x0424C: MACA (MDI Auto-Scan Command and Addr) 0x%08X\n",
+ regs_buff[1050]);
+
+ fprintf(stdout,
+ "0x04250: APAE (Auto-Scan PHY Address Enable) 0x%08X\n",
+ regs_buff[1051]);
+
+ fprintf(stdout,
+ "0x04254: ARD (Auto-Scan Read Data) 0x%08X\n",
+ regs_buff[1052]);
+
+ fprintf(stdout,
+ "0x04258: AIS (Auto-Scan Interrupt Status) 0x%08X\n",
+ regs_buff[1053]);
+
+ fprintf(stdout,
+ "0x0425C: MSCA (MDI Single Command and Addr) 0x%08X\n",
+ regs_buff[1054]);
+
+ fprintf(stdout,
+ "0x04260: MSRWD (MDI Single Read and Write Data) 0x%08X\n",
+ regs_buff[1055]);
+
+ fprintf(stdout,
+ "0x04264: MLADD (MAC Address Low) 0x%08X\n",
+ regs_buff[1056]);
+
+ fprintf(stdout,
+ "0x04268: MHADD (MAC Addr High/Max Frame size) 0x%08X\n",
+ regs_buff[1057]);
+
+ fprintf(stdout,
+ "0x0426C: TREG (Test Register) 0x%08X\n",
+ regs_buff[1058]);
+
+ if (mac_type < ixgbe_mac_X540) {
+ fprintf(stdout,
+ "0x04288: PCSS1 (XGXS Status 1) 0x%08X\n",
+ regs_buff[1059]);
+
+ fprintf(stdout,
+ "0x0428C: PCSS2 (XGXS Status 2) 0x%08X\n",
+ regs_buff[1060]);
+
+ fprintf(stdout,
+ "0x04290: XPCSS (10GBASE-X PCS Status) 0x%08X\n",
+ regs_buff[1061]);
+
+ fprintf(stdout,
+ "0x04298: SERDESC (SERDES Interface Control) 0x%08X\n",
+ regs_buff[1062]);
+
+ fprintf(stdout,
+ "0x0429C: MACS (FIFO Status/CNTL Report) 0x%08X\n",
+ regs_buff[1063]);
+
+ fprintf(stdout,
+ "0x042A0: AUTOC (Auto Negotiation Control) 0x%08X\n",
+ regs_buff[1064]);
+
+ fprintf(stdout,
+ "0x042A8: AUTOC2 (Auto Negotiation Control 2) 0x%08X\n",
+ regs_buff[1066]);
+
+ fprintf(stdout,
+ "0x042AC: AUTOC3 (Auto Negotiation Control 3) 0x%08X\n",
+ regs_buff[1067]);
+
+ fprintf(stdout,
+ "0x042B0: ANLP1 (Auto Neg Lnk Part. Ctrl Word 1) 0x%08X\n",
+ regs_buff[1068]);
+
+ fprintf(stdout,
+ "0x042B4: ANLP2 (Auto Neg Lnk Part. Ctrl Word 2) 0x%08X\n",
+ regs_buff[1069]);
+ }
+
+ if (mac_type == ixgbe_mac_82598EB) {
+ fprintf(stdout,
+ "0x04800: ATLASCTL (Atlas Analog Configuration) 0x%08X\n",
+ regs_buff[1070]);
+
+ /* Diagnostic */
+ fprintf(stdout,
+ "0x02C20: RDSTATCTL (Rx DMA Statistic Control) 0x%08X\n",
+ regs_buff[1071]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: RDSTAT%d (Rx DMA Statistics %d) 0x%08X\n",
+ 0x02C00 + (4 * i), i, i, regs_buff[1072 + i]);
+
+ fprintf(stdout,
+ "0x02F08: RDHMPN (Rx Desc Handler Mem Page num) 0x%08X\n",
+ regs_buff[1080]);
+
+ fprintf(stdout,
+ "0x02F10: RIC_DW0 (Rx Desc Hand. Mem Read Data 0) 0x%08X\n",
+ regs_buff[1081]);
+
+ fprintf(stdout,
+ "0x02F14: RIC_DW1 (Rx Desc Hand. Mem Read Data 1) 0x%08X\n",
+ regs_buff[1082]);
+
+ fprintf(stdout,
+ "0x02F18: RIC_DW2 (Rx Desc Hand. Mem Read Data 2) 0x%08X\n",
+ regs_buff[1083]);
+
+ fprintf(stdout,
+ "0x02F1C: RIC_DW3 (Rx Desc Hand. Mem Read Data 3) 0x%08X\n",
+ regs_buff[1084]);
+ }
+
+ if (mac_type < ixgbe_mac_X540)
+ fprintf(stdout,
+ "0x02F20: RDPROBE (Rx Probe Mode Status) 0x%08X\n",
+ regs_buff[1085]);
+
+ fprintf(stdout,
+ "0x07C20: TDSTATCTL (Tx DMA Statistic Control) 0x%08X\n",
+ regs_buff[1086]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: TDSTAT%d (Tx DMA Statistics %d) 0x%08X\n",
+ 0x07C00 + (4 * i), i, i, regs_buff[1087 + i]);
+
+ fprintf(stdout,
+ "0x07F08: TDHMPN (Tx Desc Handler Mem Page Num) 0x%08X\n",
+ regs_buff[1095]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x%05X: TIC_DW%d (Tx Desc Hand. Mem Read Data %d) 0x%08X\n",
+ 0x07F10 + (4 * i), i, i, regs_buff[1096 + i]);
+
+ fprintf(stdout,
+ "0x07F20: TDPROBE (Tx Probe Mode Status) 0x%08X\n",
+ regs_buff[1100]);
+
+ fprintf(stdout,
+ "0x0C600: TXBUFCTRL (TX Buffer Access Control) 0x%08X\n",
+ regs_buff[1101]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x%05X: TXBUFDATA%d (TX Buffer DATA %d) 0x%08X\n",
+ 0x0C610 + (4 * i), i, i, regs_buff[1102 + i]);
+
+ fprintf(stdout,
+ "0x03600: RXBUFCTRL (RX Buffer Access Control) 0x%08X\n",
+ regs_buff[1106]);
+
+ for (i = 0; i < 4; i++)
+ fprintf(stdout,
+ "0x%05X: RXBUFDATA%d (RX Buffer DATA %d) 0x%08X\n",
+ 0x03610 + (4 * i), i, i, regs_buff[1107 + i]);
+
+ for (i = 0; i < 8; i++)
+ fprintf(stdout,
+ "0x%05X: PCIE_DIAG%d (PCIe Diagnostic %d) 0x%08X\n",
+ 0x11090 + (4 * i), i, i, regs_buff[1111 + i]);
+
+ fprintf(stdout,
+ "0x050A4: RFVAL (Receive Filter Validation) 0x%08X\n",
+ regs_buff[1119]);
+
+ if (mac_type < ixgbe_mac_X540) {
+ fprintf(stdout,
+ "0x042B8: MDFTC1 (MAC DFT Control 1) 0x%08X\n",
+ regs_buff[1120]);
+
+ fprintf(stdout,
+ "0x042C0: MDFTC2 (MAC DFT Control 2) 0x%08X\n",
+ regs_buff[1121]);
+
+ fprintf(stdout,
+ "0x042C4: MDFTFIFO1 (MAC DFT FIFO 1) 0x%08X\n",
+ regs_buff[1122]);
+
+ fprintf(stdout,
+ "0x042C8: MDFTFIFO2 (MAC DFT FIFO 2) 0x%08X\n",
+ regs_buff[1123]);
+
+ fprintf(stdout,
+ "0x042CC: MDFTS (MAC DFT Status) 0x%08X\n",
+ regs_buff[1124]);
+ }
+
+ if (mac_type == ixgbe_mac_82598EB) {
+ fprintf(stdout,
+ "0x1106C: PCIEECCCTL (PCIe ECC Control) 0x%08X\n",
+ regs_buff[1125]);
+
+ fprintf(stdout,
+ "0x0C300: PBTXECC (Packet Buffer Tx ECC) 0x%08X\n",
+ regs_buff[1126]);
+
+ fprintf(stdout,
+ "0x03300: PBRXECC (Packet Buffer Rx ECC) 0x%08X\n",
+ regs_buff[1127]);
+ }
+
+ if (regs_buff_len > 1139 && mac_type != ixgbe_mac_82598EB) {
+ fprintf(stdout,
+ "0x08800: SECTXCTRL (Security Tx Control) 0x%08X\n",
+ regs_buff[1139]);
+
+ fprintf(stdout,
+ "0x08804: SECTXSTAT (Security Tx Status) 0x%08X\n",
+ regs_buff[1140]);
+
+ fprintf(stdout,
+ "0x08808: SECTXBUFFAF (Security Tx Buffer Almost Full) 0x%08X\n",
+ regs_buff[1141]);
+
+ fprintf(stdout,
+ "0x08800: SECTXMINIFG (Security Tx Buffer Minimum IFG) 0x%08X\n",
+ regs_buff[1142]);
+
+ fprintf(stdout,
+ "0x08800: SECRXCTRL (Security Rx Control) 0x%08X\n",
+ regs_buff[1143]);
+
+ fprintf(stdout,
+ "0x08800: SECRXSTAT (Security Rx Status) 0x%08X\n",
+ regs_buff[1144]);
+ }
+
+ return 0;
+}
diff --git a/ixgbevf.c b/ixgbevf.c
new file mode 100644
index 0000000..91c2b2c
--- /dev/null
+++ b/ixgbevf.c
@@ -0,0 +1,181 @@
+/* Copyright (c) 2013 Intel Corporation */
+#include <stdio.h>
+#include "internal.h"
+
+int
+ixgbevf_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *regs_buff = (u32 *)regs->data;
+ u8 version = (u8)(regs->version >> 24);
+ u8 i;
+
+ if (version == 0)
+ return -1;
+
+ fprintf(stdout,
+ "0x00000: VFCTRL (VF Control Register) (Write Only) N/A\n");
+
+ fprintf(stdout,
+ "0x00008: VFSTATUS (VF Status Register) 0x%08X\n",
+ regs_buff[1]);
+
+ fprintf(stdout,
+ "0x00010: VFLINKS (VF Link Status Register) 0x%08X\n",
+ regs_buff[2]);
+
+ fprintf(stdout,
+ "0x03190: VFRXMEMWRAP (Rx Packet Buffer Flush Detect) 0x%08X\n",
+ regs_buff[3]);
+
+ fprintf(stdout,
+ "0x00048: VFFRTIMER (VF Free Running Timer) 0x%08X\n",
+ regs_buff[4]);
+
+ fprintf(stdout,
+ "0x00100: VFEICR (VF Extended Interrupt Cause) 0x%08X\n",
+ regs_buff[5]);
+
+ fprintf(stdout,
+ "0x00104: VFEICS (VF Extended Interrupt Cause Set) 0x%08X\n",
+ regs_buff[6]);
+
+ fprintf(stdout,
+ "0x00108: VFEIMS (VF Extended Interrupt Mask Set) 0x%08X\n",
+ regs_buff[7]);
+
+ fprintf(stdout,
+ "0x0010C: VFEIMC (VF Extended Interrupt Mask Clear) 0x%08X\n",
+ regs_buff[8]);
+
+ fprintf(stdout,
+ "0x00110: VFEIAC (VF Extended Interrupt Auto Clear) 0x%08X\n",
+ regs_buff[9]);
+
+ fprintf(stdout,
+ "0x00114: VFEIAM (VF Extended Interrupt Auto Mask) 0x%08X\n",
+ regs_buff[10]);
+
+ fprintf(stdout,
+ "0x00820: VFEITR(0) (VF Extended Interrupt Throttle) 0x%08X\n",
+ regs_buff[11]);
+
+ fprintf(stdout,
+ "0x00120: VFIVAR(0) (VF Interrupt Vector Allocation) 0x%08X\n",
+ regs_buff[12]);
+
+ fprintf(stdout,
+ "0x00140: VFIVAR_MISC (VF Interrupt Vector Misc) 0x%08X\n",
+ regs_buff[13]);
+
+ fprintf(stdout,
+ "0x00104: VFPSRTYPE (VF Replication Packet Split Type) 0x%08X\n",
+ regs_buff[28]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFRDBAL(%d) (VF Rx Desc. Base Addr Low %d) 0x%08X\n",
+ 0x1000 + 0x40*i,
+ i, i,
+ regs_buff[14+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFRDBAH(%d) (VF Rx Desc. Base Addr High %d) 0x%08X\n",
+ 0x1004 + 0x40*i,
+ i, i,
+ regs_buff[16+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFRDLEN(%d) (VF Rx Desc. Length %d) 0x%08X\n",
+ 0x1008 + 0x40*i,
+ i, i,
+ regs_buff[18+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFRDH(%d) (VF Rx Desc. Head %d) 0x%08X\n",
+ 0x1010 + 0x40*i,
+ i, i,
+ regs_buff[20+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFRDT(%d) (VF Rx Desc. Tail %d) 0x%08X\n",
+ 0x1018 + 0x40*i,
+ i, i,
+ regs_buff[22+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFRDT(%d) (VF Rx Desc. Control %d), 0x%08X\n",
+ 0x1028 + 0x40*i,
+ i, i,
+ regs_buff[24+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFSRRCTL(%d) (VF Split Rx Control %d) 0x%08X\n",
+ 0x1014 + 0x40*i,
+ i, i,
+ regs_buff[26+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFTDBAL(%d) (VF Tx Desc. Base Addr Low %d) 0x%08X\n",
+ 0x2000 + 0x40*i,
+ i, i,
+ regs_buff[29+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFTDBAH(%d) (VF Tx Desc. Base Addr High %d) 0x%08X\n",
+ 0x2004 + 0x40*i,
+ i, i,
+ regs_buff[31+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFTDLEN(%d) (VF Tx Desc. Length %d) 0x%08X\n",
+ 0x2008 + 0x40*i,
+ i, i,
+ regs_buff[33+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFTDH(%d) (VF Tx Desc. Head %d) 0x%08X\n",
+ 0x2010 + 0x40*i,
+ i, i,
+ regs_buff[35+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFTDT(%d) (VF Tx Desc. Tail %d) 0x%08X\n",
+ 0x2018 + 0x40*i,
+ i, i,
+ regs_buff[37+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFTDT(%d) (VF Tx Desc. Control %d) 0x%08X\n",
+ 0x2028 + 0x40*i,
+ i, i,
+ regs_buff[39+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFTDWBAL(%d) (VF Tx Desc. Write Back Addr Lo %d) 0x%08X\n",
+ 0x2038 + 0x40*i,
+ i, i,
+ regs_buff[41+i]);
+
+ for (i = 0; i < 2; i++)
+ fprintf(stdout,
+ "0x%05x: VFTDWBAH(%d) (VF Tx Desc. Write Back Addr Hi %d) 0x%08X\n",
+ 0x203C + 0x40*i,
+ i, i,
+ regs_buff[43+i]);
+
+ return 0;
+}
diff --git a/json_print.c b/json_print.c
new file mode 100644
index 0000000..4f62767
--- /dev/null
+++ b/json_print.c
@@ -0,0 +1,228 @@
+/*
+ * json_print.c "print regular or json output, based on json_writer".
+ *
+ * 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.
+ *
+ * Authors: Julien Fortin, <julien@cumulusnetworks.com>
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "json_print.h"
+
+#define SPRINT_BSIZE 64
+#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
+
+static json_writer_t *_jw;
+
+#define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw)
+#define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY))
+
+void new_json_obj(int json)
+{
+ if (json) {
+ _jw = jsonw_new(stdout);
+ if (!_jw) {
+ perror("json object");
+ exit(1);
+ }
+ jsonw_pretty(_jw, true);
+ jsonw_start_array(_jw);
+ }
+}
+
+void delete_json_obj(void)
+{
+ if (_jw) {
+ jsonw_end_array(_jw);
+ jsonw_destroy(&_jw);
+ }
+}
+
+bool is_json_context(void)
+{
+ return _jw != NULL;
+}
+
+json_writer_t *get_json_writer(void)
+{
+ return _jw;
+}
+
+void open_json_object(const char *str)
+{
+ if (_IS_JSON_CONTEXT(PRINT_JSON)) {
+ if (str)
+ jsonw_name(_jw, str);
+ jsonw_start_object(_jw);
+ }
+}
+
+void close_json_object(void)
+{
+ if (_IS_JSON_CONTEXT(PRINT_JSON))
+ jsonw_end_object(_jw);
+}
+
+/*
+ * Start json array or string array using
+ * the provided string as json key (if not null)
+ * or array delimiter in non-json context.
+ */
+void open_json_array(const char *key, const char *str)
+{
+ if (is_json_context()) {
+ if (key)
+ jsonw_name(_jw, key);
+ jsonw_start_array(_jw);
+ } else {
+ printf("%s", str);
+ }
+}
+
+/*
+ * End json array or string array
+ */
+void close_json_array(const char *delim)
+{
+ if (is_json_context())
+ jsonw_end_array(_jw);
+ else
+ printf("%s", delim);
+}
+
+/*
+ * pre-processor directive to generate similar
+ * functions handling different types
+ */
+#define _PRINT_FUNC(type_name, type) \
+ __attribute__((format(printf, 3, 0))) \
+ void print_##type_name(enum output_type t, \
+ const char *key, \
+ const char *fmt, \
+ type value) \
+ { \
+ if (_IS_JSON_CONTEXT(t)) { \
+ if (!key) \
+ jsonw_##type_name(_jw, value); \
+ else \
+ jsonw_##type_name##_field(_jw, key, value); \
+ } else if (_IS_FP_CONTEXT(t)) { \
+ fprintf(stdout, fmt, value); \
+ } \
+ }
+_PRINT_FUNC(int, int);
+_PRINT_FUNC(s64, int64_t);
+_PRINT_FUNC(hhu, unsigned char);
+_PRINT_FUNC(hu, unsigned short);
+_PRINT_FUNC(uint, unsigned int);
+_PRINT_FUNC(u64, uint64_t);
+_PRINT_FUNC(luint, unsigned long);
+_PRINT_FUNC(lluint, unsigned long long);
+_PRINT_FUNC(float, double);
+#undef _PRINT_FUNC
+
+void print_string(enum output_type type,
+ const char *key,
+ const char *fmt,
+ const char *value)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ if (key && !value)
+ jsonw_name(_jw, key);
+ else if (!key && value)
+ jsonw_string(_jw, value);
+ else
+ jsonw_string_field(_jw, key, value);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, value);
+ }
+}
+
+/*
+ * value's type is bool. When using this function in FP context you can't pass
+ * a value to it, you will need to use "is_json_context()" to have different
+ * branch for json and regular output. grep -r "print_bool" for example
+ */
+void print_bool(enum output_type type,
+ const char *key,
+ const char *fmt,
+ bool value)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ if (key)
+ jsonw_bool_field(_jw, key, value);
+ else
+ jsonw_bool(_jw, value);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, value ? "true" : "false");
+ }
+}
+
+/*
+ * In JSON context uses hardcode %#x format: 42 -> 0x2a
+ */
+void print_0xhex(enum output_type type,
+ const char *key,
+ const char *fmt,
+ unsigned long long hex)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ SPRINT_BUF(b1);
+
+ snprintf(b1, sizeof(b1), "%#llx", hex);
+ print_string(PRINT_JSON, key, NULL, b1);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, hex);
+ }
+}
+
+void print_hex(enum output_type type,
+ const char *key,
+ const char *fmt,
+ unsigned int hex)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ SPRINT_BUF(b1);
+
+ snprintf(b1, sizeof(b1), "%x", hex);
+ if (key)
+ jsonw_string_field(_jw, key, b1);
+ else
+ jsonw_string(_jw, b1);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, hex);
+ }
+}
+
+/*
+ * In JSON context we don't use the argument "value" we simply call jsonw_null
+ * whereas FP context can use "value" to output anything
+ */
+void print_null(enum output_type type,
+ const char *key,
+ const char *fmt,
+ const char *value)
+{
+ if (_IS_JSON_CONTEXT(type)) {
+ if (key)
+ jsonw_null_field(_jw, key);
+ else
+ jsonw_null(_jw);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, value);
+ }
+}
+
+/* Print line separator (if not in JSON mode) */
+void print_nl(void)
+{
+ if (!_jw)
+ printf("%s", "\n");
+}
diff --git a/json_print.h b/json_print.h
new file mode 100644
index 0000000..df15314
--- /dev/null
+++ b/json_print.h
@@ -0,0 +1,67 @@
+/*
+ * json_print.h "print regular or json output, based on json_writer".
+ *
+ * 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.
+ *
+ * Authors: Julien Fortin, <julien@cumulusnetworks.com>
+ */
+
+#ifndef _JSON_PRINT_H_
+#define _JSON_PRINT_H_
+
+#include "json_writer.h"
+
+json_writer_t *get_json_writer(void);
+
+/*
+ * use:
+ * - PRINT_ANY for context based output
+ * - PRINT_FP for non json specific output
+ * - PRINT_JSON for json specific output
+ */
+enum output_type {
+ PRINT_FP = 1,
+ PRINT_JSON = 2,
+ PRINT_ANY = 4,
+};
+
+void new_json_obj(int json);
+void delete_json_obj(void);
+
+bool is_json_context(void);
+
+void fflush_fp(void);
+
+void open_json_object(const char *str);
+void close_json_object(void);
+void open_json_array(const char *key, const char *str);
+void close_json_array(const char *delim);
+
+void print_nl(void);
+
+#define _PRINT_FUNC(type_name, type) \
+ void print_##type_name(enum output_type t, \
+ const char *key, \
+ const char *fmt, \
+ type value) \
+
+_PRINT_FUNC(int, int);
+_PRINT_FUNC(s64, int64_t);
+_PRINT_FUNC(bool, bool);
+_PRINT_FUNC(null, const char*);
+_PRINT_FUNC(string, const char*);
+_PRINT_FUNC(uint, unsigned int);
+_PRINT_FUNC(u64, uint64_t);
+_PRINT_FUNC(hhu, unsigned char);
+_PRINT_FUNC(hu, unsigned short);
+_PRINT_FUNC(hex, unsigned int);
+_PRINT_FUNC(0xhex, unsigned long long);
+_PRINT_FUNC(luint, unsigned long);
+_PRINT_FUNC(lluint, unsigned long long);
+_PRINT_FUNC(float, double);
+#undef _PRINT_FUNC
+
+#endif /* _JSON_PRINT_H_ */
diff --git a/json_writer.c b/json_writer.c
new file mode 100644
index 0000000..e8b3926
--- /dev/null
+++ b/json_writer.c
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) //
+/*
+ * Simple streaming JSON writer
+ *
+ * This takes care of the annoying bits of JSON syntax like the commas
+ * after elements
+ *
+ * Authors: Stephen Hemminger <stephen@networkplumber.org>
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <malloc.h>
+#include <inttypes.h>
+#include <stdint.h>
+
+#include "json_writer.h"
+
+struct json_writer {
+ FILE *out; /* output file */
+ unsigned int depth; /* nesting */
+ bool pretty; /* optional whitepace */
+ char sep; /* either nul or comma */
+};
+
+/* indentation for pretty print */
+static void jsonw_indent(json_writer_t *self)
+{
+ unsigned int i;
+
+ for (i = 0; i < self->depth; ++i)
+ fputs(" ", self->out);
+}
+
+/* end current line and indent if pretty printing */
+static void jsonw_eol(json_writer_t *self)
+{
+ if (!self->pretty)
+ return;
+
+ putc('\n', self->out);
+ jsonw_indent(self);
+}
+
+/* If current object is not empty print a comma */
+static void jsonw_eor(json_writer_t *self)
+{
+ if (self->sep != '\0')
+ putc(self->sep, self->out);
+ self->sep = ',';
+}
+
+
+/* Output JSON encoded string */
+/* Handles C escapes, does not do Unicode */
+static void jsonw_puts(json_writer_t *self, const char *str)
+{
+ putc('"', self->out);
+ for (; *str; ++str)
+ switch (*str) {
+ case '\t':
+ fputs("\\t", self->out);
+ break;
+ case '\n':
+ fputs("\\n", self->out);
+ break;
+ case '\r':
+ fputs("\\r", self->out);
+ break;
+ case '\f':
+ fputs("\\f", self->out);
+ break;
+ case '\b':
+ fputs("\\b", self->out);
+ break;
+ case '\\':
+ fputs("\\\\", self->out);
+ break;
+ case '"':
+ fputs("\\\"", self->out);
+ break;
+ case '\'':
+ fputs("\\\'", self->out);
+ break;
+ default:
+ putc(*str, self->out);
+ }
+ putc('"', self->out);
+}
+
+/* Create a new JSON stream */
+json_writer_t *jsonw_new(FILE *f)
+{
+ json_writer_t *self = malloc(sizeof(*self));
+
+ if (self) {
+ self->out = f;
+ self->depth = 0;
+ self->pretty = false;
+ self->sep = '\0';
+ }
+ return self;
+}
+
+/* End output to JSON stream */
+void jsonw_destroy(json_writer_t **self_p)
+{
+ json_writer_t *self = *self_p;
+
+ assert(self->depth == 0);
+ fputs("\n", self->out);
+ fflush(self->out);
+ free(self);
+ *self_p = NULL;
+}
+
+void jsonw_pretty(json_writer_t *self, bool on)
+{
+ self->pretty = on;
+}
+
+/* Basic blocks */
+static void jsonw_begin(json_writer_t *self, int c)
+{
+ jsonw_eor(self);
+ putc(c, self->out);
+ ++self->depth;
+ self->sep = '\0';
+}
+
+static void jsonw_end(json_writer_t *self, int c)
+{
+ assert(self->depth > 0);
+
+ --self->depth;
+ if (self->sep != '\0')
+ jsonw_eol(self);
+ putc(c, self->out);
+ self->sep = ',';
+}
+
+
+/* Add a JSON property name */
+void jsonw_name(json_writer_t *self, const char *name)
+{
+ jsonw_eor(self);
+ jsonw_eol(self);
+ self->sep = '\0';
+ jsonw_puts(self, name);
+ putc(':', self->out);
+ if (self->pretty)
+ putc(' ', self->out);
+}
+
+__attribute__((format(printf, 2, 3)))
+void jsonw_printf(json_writer_t *self, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ jsonw_eor(self);
+ vfprintf(self->out, fmt, ap);
+ va_end(ap);
+}
+
+/* Collections */
+void jsonw_start_object(json_writer_t *self)
+{
+ jsonw_begin(self, '{');
+}
+
+void jsonw_end_object(json_writer_t *self)
+{
+ jsonw_end(self, '}');
+}
+
+void jsonw_start_array(json_writer_t *self)
+{
+ jsonw_begin(self, '[');
+ if (self->pretty)
+ putc(' ', self->out);
+}
+
+void jsonw_end_array(json_writer_t *self)
+{
+ if (self->pretty && self->sep)
+ putc(' ', self->out);
+ self->sep = '\0';
+ jsonw_end(self, ']');
+}
+
+/* JSON value types */
+void jsonw_string(json_writer_t *self, const char *value)
+{
+ jsonw_eor(self);
+ jsonw_puts(self, value);
+}
+
+void jsonw_bool(json_writer_t *self, bool val)
+{
+ jsonw_printf(self, "%s", val ? "true" : "false");
+}
+
+void jsonw_null(json_writer_t *self)
+{
+ jsonw_printf(self, "null");
+}
+
+void jsonw_float(json_writer_t *self, double num)
+{
+ jsonw_printf(self, "%g", num);
+}
+
+void jsonw_hhu(json_writer_t *self, unsigned char num)
+{
+ jsonw_printf(self, "%hhu", num);
+}
+
+void jsonw_hu(json_writer_t *self, unsigned short num)
+{
+ jsonw_printf(self, "%hu", num);
+}
+
+void jsonw_uint(json_writer_t *self, unsigned int num)
+{
+ jsonw_printf(self, "%u", num);
+}
+
+void jsonw_u64(json_writer_t *self, uint64_t num)
+{
+ jsonw_printf(self, "%"PRIu64, num);
+}
+
+void jsonw_xint(json_writer_t *self, uint64_t num)
+{
+ jsonw_printf(self, "%"PRIx64, num);
+}
+
+void jsonw_luint(json_writer_t *self, unsigned long num)
+{
+ jsonw_printf(self, "%lu", num);
+}
+
+void jsonw_lluint(json_writer_t *self, unsigned long long num)
+{
+ jsonw_printf(self, "%llu", num);
+}
+
+void jsonw_int(json_writer_t *self, int num)
+{
+ jsonw_printf(self, "%d", num);
+}
+
+void jsonw_s64(json_writer_t *self, int64_t num)
+{
+ jsonw_printf(self, "%"PRId64, num);
+}
+
+/* Basic name/value objects */
+void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
+{
+ jsonw_name(self, prop);
+ jsonw_string(self, val);
+}
+
+void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
+{
+ jsonw_name(self, prop);
+ jsonw_bool(self, val);
+}
+
+void jsonw_float_field(json_writer_t *self, const char *prop, double val)
+{
+ jsonw_name(self, prop);
+ jsonw_float(self, val);
+}
+
+void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num)
+{
+ jsonw_name(self, prop);
+ jsonw_uint(self, num);
+}
+
+void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num)
+{
+ jsonw_name(self, prop);
+ jsonw_u64(self, num);
+}
+
+void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num)
+{
+ jsonw_name(self, prop);
+ jsonw_xint(self, num);
+}
+
+void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num)
+{
+ jsonw_name(self, prop);
+ jsonw_hhu(self, num);
+}
+
+void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
+{
+ jsonw_name(self, prop);
+ jsonw_hu(self, num);
+}
+
+void jsonw_luint_field(json_writer_t *self,
+ const char *prop,
+ unsigned long num)
+{
+ jsonw_name(self, prop);
+ jsonw_luint(self, num);
+}
+
+void jsonw_lluint_field(json_writer_t *self,
+ const char *prop,
+ unsigned long long num)
+{
+ jsonw_name(self, prop);
+ jsonw_lluint(self, num);
+}
+
+void jsonw_int_field(json_writer_t *self, const char *prop, int num)
+{
+ jsonw_name(self, prop);
+ jsonw_int(self, num);
+}
+
+void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num)
+{
+ jsonw_name(self, prop);
+ jsonw_s64(self, num);
+}
+
+void jsonw_null_field(json_writer_t *self, const char *prop)
+{
+ jsonw_name(self, prop);
+ jsonw_null(self);
+}
+
+#ifdef TEST
+int main(int argc, char **argv)
+{
+ json_writer_t *wr = jsonw_new(stdout);
+
+ jsonw_start_object(wr);
+ jsonw_pretty(wr, true);
+ jsonw_name(wr, "Vyatta");
+ jsonw_start_object(wr);
+ jsonw_string_field(wr, "url", "http://vyatta.com");
+ jsonw_uint_field(wr, "downloads", 2000000ul);
+ jsonw_float_field(wr, "stock", 8.16);
+
+ jsonw_name(wr, "ARGV");
+ jsonw_start_array(wr);
+ while (--argc)
+ jsonw_string(wr, *++argv);
+ jsonw_end_array(wr);
+
+ jsonw_name(wr, "empty");
+ jsonw_start_array(wr);
+ jsonw_end_array(wr);
+
+ jsonw_name(wr, "NIL");
+ jsonw_start_object(wr);
+ jsonw_end_object(wr);
+
+ jsonw_null_field(wr, "my_null");
+
+ jsonw_name(wr, "special chars");
+ jsonw_start_array(wr);
+ jsonw_string_field(wr, "slash", "/");
+ jsonw_string_field(wr, "newline", "\n");
+ jsonw_string_field(wr, "tab", "\t");
+ jsonw_string_field(wr, "ff", "\f");
+ jsonw_string_field(wr, "quote", "\"");
+ jsonw_string_field(wr, "tick", "\'");
+ jsonw_string_field(wr, "backslash", "\\");
+ jsonw_end_array(wr);
+
+ jsonw_end_object(wr);
+
+ jsonw_end_object(wr);
+ jsonw_destroy(&wr);
+ return 0;
+}
+
+#endif
diff --git a/json_writer.h b/json_writer.h
new file mode 100644
index 0000000..b52dc2d
--- /dev/null
+++ b/json_writer.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
+/*
+ * Simple streaming JSON writer
+ *
+ * This takes care of the annoying bits of JSON syntax like the commas
+ * after elements
+ *
+ * Authors: Stephen Hemminger <stephen@networkplumber.org>
+ */
+
+#ifndef _JSON_WRITER_H_
+#define _JSON_WRITER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/* Opaque class structure */
+typedef struct json_writer json_writer_t;
+
+/* Create a new JSON stream */
+json_writer_t *jsonw_new(FILE *f);
+/* End output to JSON stream */
+void jsonw_destroy(json_writer_t **self_p);
+
+/* Cause output to have pretty whitespace */
+void jsonw_pretty(json_writer_t *self, bool on);
+
+/* Add property name */
+void jsonw_name(json_writer_t *self, const char *name);
+
+/* Add value */
+__attribute__((format(printf, 2, 3)))
+void jsonw_printf(json_writer_t *self, const char *fmt, ...);
+void jsonw_string(json_writer_t *self, const char *value);
+void jsonw_bool(json_writer_t *self, bool value);
+void jsonw_float(json_writer_t *self, double number);
+void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
+void jsonw_uint(json_writer_t *self, unsigned int number);
+void jsonw_u64(json_writer_t *self, uint64_t number);
+void jsonw_xint(json_writer_t *self, uint64_t number);
+void jsonw_hhu(json_writer_t *self, unsigned char num);
+void jsonw_hu(json_writer_t *self, unsigned short number);
+void jsonw_int(json_writer_t *self, int number);
+void jsonw_s64(json_writer_t *self, int64_t number);
+void jsonw_null(json_writer_t *self);
+void jsonw_luint(json_writer_t *self, unsigned long num);
+void jsonw_lluint(json_writer_t *self, unsigned long long num);
+
+/* Useful Combinations of name and value */
+void jsonw_string_field(json_writer_t *self, const char *prop, const char *val);
+void jsonw_bool_field(json_writer_t *self, const char *prop, bool value);
+void jsonw_float_field(json_writer_t *self, const char *prop, double num);
+void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num);
+void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num);
+void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
+void jsonw_int_field(json_writer_t *self, const char *prop, int num);
+void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num);
+void jsonw_null_field(json_writer_t *self, const char *prop);
+void jsonw_luint_field(json_writer_t *self, const char *prop,
+ unsigned long num);
+void jsonw_lluint_field(json_writer_t *self, const char *prop,
+ unsigned long long num);
+
+/* Collections */
+void jsonw_start_object(json_writer_t *self);
+void jsonw_end_object(json_writer_t *self);
+
+void jsonw_start_array(json_writer_t *self);
+void jsonw_end_array(json_writer_t *self);
+
+/* Override default exception handling */
+typedef void (jsonw_err_handler_fn)(const char *);
+
+#endif /* _JSON_WRITER_H_ */
diff --git a/lan743x.c b/lan743x.c
new file mode 100644
index 0000000..f430ee8
--- /dev/null
+++ b/lan743x.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. */
+
+#include <stdio.h>
+#include <string.h>
+#include "internal.h"
+
+#define LAN743X_ETH_REG_VERSION 1
+
+enum {
+ ETH_PRIV_FLAGS,
+ ETH_ID_REV,
+ ETH_FPGA_REV,
+ ETH_STRAP_READ,
+ ETH_INT_STS,
+ ETH_HW_CFG,
+ ETH_PMT_CTL,
+ ETH_E2P_CMD,
+ ETH_E2P_DATA,
+ ETH_MAC_CR,
+ ETH_MAC_RX,
+ ETH_MAC_TX,
+ ETH_FLOW,
+ ETH_MII_ACC,
+ ETH_MII_DATA,
+ ETH_EEE_TX_LPI_REQ_DLY,
+ ETH_WUCSR,
+ ETH_WK_SRC,
+
+ /* Add new registers above */
+ MAX_LAN743X_ETH_REGS
+};
+
+void lan743x_comm_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *lan743x_reg = (u32 *)regs->data;
+
+ fprintf(stdout, "LAN743x Registers:\n");
+ fprintf(stdout, "------------------\n");
+ fprintf(stdout, "CHIP_ID_REV = 0x%08X\n", lan743x_reg[ETH_ID_REV]);
+ fprintf(stdout, "FPGA_REV = 0x%08X\n", lan743x_reg[ETH_FPGA_REV]);
+ fprintf(stdout, "STRAP_READ = 0x%08X\n", lan743x_reg[ETH_STRAP_READ]);
+ fprintf(stdout, "INT_STS = 0x%08X\n", lan743x_reg[ETH_INT_STS]);
+ fprintf(stdout, "HW_CFG = 0x%08X\n", lan743x_reg[ETH_HW_CFG]);
+ fprintf(stdout, "PMT_CTRL = 0x%08X\n", lan743x_reg[ETH_PMT_CTL]);
+ fprintf(stdout, "E2P_CMD = 0x%08X\n", lan743x_reg[ETH_E2P_CMD]);
+ fprintf(stdout, "E2P_DATA = 0x%08X\n", lan743x_reg[ETH_E2P_DATA]);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "MAC Registers:\n");
+ fprintf(stdout, "--------------\n");
+ fprintf(stdout, "MAC_CR = 0x%08X\n", lan743x_reg[ETH_MAC_CR]);
+ fprintf(stdout, "MAC_RX = 0x%08X\n", lan743x_reg[ETH_MAC_RX]);
+ fprintf(stdout, "MAC_TX = 0x%08X\n", lan743x_reg[ETH_MAC_TX]);
+ fprintf(stdout, "FLOW = 0x%08X\n", lan743x_reg[ETH_FLOW]);
+ fprintf(stdout, "MII_ACC = 0x%08X\n", lan743x_reg[ETH_MII_ACC]);
+ fprintf(stdout, "MII_DATA = 0x%08X\n", lan743x_reg[ETH_MII_DATA]);
+ fprintf(stdout, "WUCSR = 0x%08X\n", lan743x_reg[ETH_WUCSR]);
+ fprintf(stdout, "WK_SRC = 0x%08X\n", lan743x_reg[ETH_WK_SRC]);
+ fprintf(stdout, "EEE_TX_LPI_REQ_DLY = 0x%08X\n",
+ lan743x_reg[ETH_EEE_TX_LPI_REQ_DLY]);
+ fprintf(stdout, "\n");
+}
+
+int lan743x_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+
+ lan743x_comm_dump_regs(info, regs);
+
+ return 0;
+}
diff --git a/lan78xx.c b/lan78xx.c
new file mode 100644
index 0000000..75ee048
--- /dev/null
+++ b/lan78xx.c
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <string.h>
+#include "internal.h"
+
+int lan78xx_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ unsigned int *lan78xx_reg = (unsigned int *)regs->data;
+
+ fprintf(stdout, "LAN78xx Registers:\n");
+ fprintf(stdout, "------------------\n");
+ fprintf(stdout, "ID_REV = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "INT_STS = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "HW_CFG = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "PMT_CTRL = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "E2P_CMD = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "E2P_DATA = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "USB_STATUS = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "VLAN_TYPE = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "MAC Registers:\n");
+ fprintf(stdout, "--------------\n");
+ fprintf(stdout, "MAC_CR = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "MAC_RX = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "MAC_TX = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "FLOW = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "ERR_STS = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "MII_ACC = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "MII_DATA = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "EEE_TX_LPI_REQ_DLY = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "EEE_TW_TX_SYS = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "EEE_TX_LPI_REM_DLY = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "WUCSR = 0x%08X\n", *lan78xx_reg++);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "PHY Registers:\n");
+ fprintf(stdout, "--------------\n");
+ fprintf(stdout, "Mode Control = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Mode Status = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Device identifier1 = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Device identifier2 = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Auto-Neg Advertisement = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout, "Auto-Neg Link Partner Ability = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout, "Auto-Neg Expansion = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Auto-Neg Next Page TX = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Auto-Neg Link Partner Next Page RX = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout, "1000BASE-T Control = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "1000BASE-T Status = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Reserved = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Reserved = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "MMD Access Control = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "MMD Access Address/Data = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "1000BASE-T Status Extension1 = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout, "1000BASE-TX Status Extension = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout, "1000BASE-T Status Extension2 = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout, "Bypass Control = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout,
+ "100BASE-TX/1000BASE-T Rx Error Counter = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout,
+ "100BASE-TX/1000BASE-T FC Err Counter = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout,
+ "10BASE-T/100BASE-TX/1000BASE-T LD Counter = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout, "Extended 10BASE-T Control and Status = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout, "Extended PHY Control1 = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Extended PHY Control2 = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Interrupt Mask = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Interrupt Status = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Reserved = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Auxiliary Control and Status = 0x%04X\n",
+ *lan78xx_reg++);
+ fprintf(stdout, "LED Mode Select = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "LED Behavior = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "Extended Page Access = 0x%04X\n", *lan78xx_reg++);
+ fprintf(stdout, "\n");
+
+ return 0;
+}
diff --git a/list.h b/list.h
new file mode 100644
index 0000000..aa97fdd
--- /dev/null
+++ b/list.h
@@ -0,0 +1,34 @@
+#ifndef ETHTOOL_LIST_H__
+#define ETHTOOL_LIST_H__
+
+#include <unistd.h>
+
+/* Generic list utilities */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ head->next->prev = new;
+ new->next = head->next;
+ new->prev = head;
+ head->next = new;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ entry->next->prev = entry->prev;
+ entry->prev->next = entry->next;
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#endif
diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4
new file mode 100644
index 0000000..e8c5312
--- /dev/null
+++ b/m4/ax_append_flag.m4
@@ -0,0 +1,71 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_append_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
+#
+# DESCRIPTION
+#
+# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
+# added in between.
+#
+# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
+# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains
+# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly
+# FLAG.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 7
+
+AC_DEFUN([AX_APPEND_FLAG],
+[dnl
+AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
+AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
+AS_VAR_SET_IF(FLAGS,[
+ AS_CASE([" AS_VAR_GET(FLAGS) "],
+ [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
+ [
+ AS_VAR_APPEND(FLAGS,[" $1"])
+ AC_RUN_LOG([: FLAGS="$FLAGS"])
+ ])
+ ],
+ [
+ AS_VAR_SET(FLAGS,[$1])
+ AC_RUN_LOG([: FLAGS="$FLAGS"])
+ ])
+AS_VAR_POPDEF([FLAGS])dnl
+])dnl AX_APPEND_FLAG
diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
new file mode 100644
index 0000000..dcabb92
--- /dev/null
+++ b/m4/ax_check_compile_flag.m4
@@ -0,0 +1,74 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the current language's compiler
+# or gives an error. (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the current language's default
+# flags (e.g. CFLAGS) when the check is done. The check is thus made with
+# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
+# force the compiler to issue an error when a bad flag is given.
+#
+# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 5
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+ _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+ AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/marvell.c b/marvell.c
new file mode 100644
index 0000000..3f3aed8
--- /dev/null
+++ b/marvell.c
@@ -0,0 +1,455 @@
+/*
+ * Code to dump Marvell SysKonnect registers for skge and sky2 drivers.
+ *
+ * Copyright (C) 2004, 2006
+ * Stephen Hemminger <shemminger@osdl.org>
+ */
+
+#include <stdio.h>
+
+#include "internal.h"
+
+static void dump_addr(int n, const u8 *a)
+{
+ int i;
+
+ printf("Addr %d ", n);
+ for (i = 0; i < 6; i++)
+ printf("%02X%c", a[i], i == 5 ? '\n' : ' ');
+}
+
+static void dump_timer(const char *name, const void *p)
+{
+ const u8 *a = p;
+ const u32 *r = p;
+
+ printf("%s\n", name);
+ printf("\tInit 0x%08X Value 0x%08X\n", r[0], r[1]);
+ printf("\tTest 0x%02X Control 0x%02X\n", a[8], a[9]);
+}
+
+static void dump_queue(const char *name, const void *a, int rx)
+{
+ struct desc {
+ uint32_t ctl;
+ uint32_t next;
+ uint32_t data_lo;
+ uint32_t data_hi;
+ uint32_t status;
+ uint32_t timestamp;
+ uint16_t csum2;
+ uint16_t csum1;
+ uint16_t csum2_start;
+ uint16_t csum1_start;
+ uint32_t addr_lo;
+ uint32_t addr_hi;
+ uint32_t count_lo;
+ uint32_t count_hi;
+ uint32_t byte_count;
+ uint32_t csr;
+ uint32_t flag;
+ };
+ const struct desc *d = a;
+
+ /* is reset bit set? */
+ if (!(d->ctl & 2)) {
+ printf("\n%s (disabled)\n", name);
+ return;
+ }
+
+ printf("\n%s\n", name);
+ printf("---------------\n");
+ printf("Descriptor Address 0x%08X%08X\n",
+ d->addr_hi, d->addr_lo);
+ printf("Address Counter 0x%08X%08X\n",
+ d->count_hi, d->count_lo);
+ printf("Current Byte Counter %d\n", d->byte_count);
+ printf("BMU Control/Status 0x%08X\n", d->csr);
+ printf("Flag & FIFO Address 0x%08X\n", d->flag);
+ printf("\n");
+ printf("Control 0x%08X\n", d->ctl);
+ printf("Next 0x%08X\n", d->next);
+ printf("Data 0x%08X%08X\n",
+ d->data_hi, d->data_lo);
+ printf("Status 0x%08X\n", d->status);
+ printf("Timestamp 0x%08X\n", d->timestamp);
+ if (rx) {
+ printf("Csum1 Offset %4d Position %d\n",
+ d->csum1, d->csum1_start);
+ printf("Csum2 Offset %4d Position %d\n",
+ d->csum2, d->csum2_start);
+ } else
+ printf("Csum Start 0x%04X Pos %4d Write %d\n",
+ d->csum1, d->csum2_start, d->csum1_start);
+
+}
+
+static void dump_ram(const char *name, const void *p)
+{
+ const u32 *r = p;
+
+ if (!(r[10] & 2)) {
+ printf("\n%s (disabled)\n", name);
+ return;
+ }
+
+ printf("\n%s\n", name);
+ printf("---------------\n");
+ printf("Start Address 0x%08X\n", r[0]);
+ printf("End Address 0x%08X\n", r[1]);
+ printf("Write Pointer 0x%08X\n", r[2]);
+ printf("Read Pointer 0x%08X\n", r[3]);
+
+ if (*name == 'R') { /* Receive only */
+ printf("Upper Threshold/Pause Packets 0x%08X\n", r[4]);
+ printf("Lower Threshold/Pause Packets 0x%08X\n", r[5]);
+ printf("Upper Threshold/High Priority 0x%08X\n", r[6]);
+ printf("Lower Threshold/High Priority 0x%08X\n", r[7]);
+ }
+ printf("Packet Counter 0x%08X\n", r[8]);
+ printf("Level 0x%08X\n", r[9]);
+ printf("Control 0x%08X\n", r[10]);
+}
+
+static void dump_fifo(const char *name, const void *p)
+{
+ const u32 *r = p;
+
+ printf("\n%s\n", name);
+ printf("---------------\n");
+ printf("End Address 0x%08X\n", r[0]);
+ printf("Write Pointer 0x%08X\n", r[1]);
+ printf("Read Pointer 0x%08X\n", r[2]);
+ printf("Packet Counter 0x%08X\n", r[3]);
+ printf("Level 0x%08X\n", r[4]);
+ printf("Control 0x%08X\n", r[5]);
+ printf("Control/Test 0x%08X\n", r[6]);
+ dump_timer("LED", r + 8);
+}
+
+static void dump_gmac_fifo(const char *name, const void *p)
+{
+ const u32 *r = p;
+ unsigned int i;
+ static const char *regs[] = {
+ "End Address",
+ "Almost Full Thresh",
+ "Control/Test",
+ "FIFO Flush Mask",
+ "FIFO Flush Threshold",
+ "Truncation Threshold",
+ "Upper Pause Threshold",
+ "Lower Pause Threshold",
+ "VLAN Tag",
+ "FIFO Write Pointer",
+ "FIFO Write Level",
+ "FIFO Read Pointer",
+ "FIFO Read Level",
+ };
+
+ printf("\n%s\n", name);
+ for (i = 0; i < sizeof(regs)/sizeof(regs[0]); ++i)
+ printf("%-32s 0x%08X\n", regs[i], r[i]);
+
+}
+
+static void dump_mac(const u8 *r)
+{
+ u8 id;
+
+ printf("\nMAC Addresses\n");
+ printf("---------------\n");
+ dump_addr(1, r + 0x100);
+ dump_addr(2, r + 0x108);
+ dump_addr(3, r + 0x110);
+ printf("\n");
+
+ printf("Connector type 0x%02X (%c)\n",
+ r[0x118], (char)r[0x118]);
+ printf("PMD type 0x%02X (%c)\n",
+ r[0x119], (char)r[0x119]);
+ printf("PHY type 0x%02X\n", r[0x11d]);
+
+ id = r[0x11b];
+ printf("Chip Id 0x%02X ", id);
+
+ switch (id) {
+ case 0x0a: printf("Genesis"); break;
+ case 0xb0: printf("Yukon"); break;
+ case 0xb1: printf("Yukon-Lite"); break;
+ case 0xb2: printf("Yukon-LP"); break;
+ case 0xb3: printf("Yukon-2 XL"); break;
+ case 0xb5: printf("Yukon Extreme"); break;
+ case 0xb4: printf("Yukon-2 EC Ultra"); break;
+ case 0xb6: printf("Yukon-2 EC"); break;
+ case 0xb7: printf("Yukon-2 FE"); break;
+ case 0xb8: printf("Yukon-2 FE Plus"); break;
+ case 0xb9: printf("Yukon Supreme"); break;
+ case 0xba: printf("Yukon Ultra 2"); break;
+ case 0xbc: printf("Yukon Optima"); break;
+ default: printf("(Unknown)"); break;
+ }
+
+ printf(" (rev %d)\n", (r[0x11a] & 0xf0) >> 4);
+
+ printf("Ram Buffer 0x%02X\n", r[0x11c]);
+
+}
+
+static void dump_gma(const char *name, const u8 *r)
+{
+ int i;
+
+ printf("%12s address: ", name);
+ for (i = 0; i < 3; i++) {
+ u16 a = *(u16 *)(r + i * 4);
+ printf(" %02X %02X", a & 0xff, (a >> 8) & 0xff);
+ }
+ printf("\n");
+}
+
+static void dump_gmac(const char *name, const u8 *data)
+{
+ printf("\n%s\n", name);
+
+ printf("Status 0x%04X\n", *(u16 *) data);
+ printf("Control 0x%04X\n", *(u16 *) (data + 4));
+ printf("Transmit 0x%04X\n", *(u16 *) (data + 8));
+ printf("Receive 0x%04X\n", *(u16 *) (data + 0xc));
+ printf("Transmit flow control 0x%04X\n", *(u16 *) (data + 0x10));
+ printf("Transmit parameter 0x%04X\n", *(u16 *) (data + 0x14));
+ printf("Serial mode 0x%04X\n", *(u16 *) (data + 0x18));
+
+ dump_gma("Source", data + 0x1c);
+ dump_gma("Physical", data + 0x28);
+}
+
+static void dump_pci(const u8 *cfg)
+{
+ int i;
+
+ printf("\nPCI config\n----------\n");
+ for(i = 0; i < 0x80; i++) {
+ if (!(i & 15))
+ printf("%02x:", i);
+ printf(" %02x", cfg[i]);
+ if ((i & 15) == 15)
+ putchar('\n');
+ }
+ putchar('\n');
+}
+
+static void dump_control(u8 *r)
+{
+ printf("Control Registers\n");
+ printf("-----------------\n");
+
+ printf("Register Access Port 0x%02X\n", *r);
+ printf("LED Control/Status 0x%08X\n", *(u32 *) (r + 4));
+
+ printf("Interrupt Source 0x%08X\n", *(u32 *) (r + 8));
+ printf("Interrupt Mask 0x%08X\n", *(u32 *) (r + 0xc));
+ printf("Interrupt Hardware Error Source 0x%08X\n", *(u32 *) (r + 0x10));
+ printf("Interrupt Hardware Error Mask 0x%08X\n", *(u32 *) (r + 0x14));
+ printf("Interrupt Control 0x%08X\n", *(u32 *) (r + 0x2c));
+ printf("Interrupt Moderation Mask 0x%08X\n", *(u32 *) (r + 0x14c));
+ printf("Hardware Moderation Mask 0x%08X\n", *(u32 *) (r + 0x150));
+ dump_timer("Moderation Timer", r + 0x140);
+
+ printf("General Purpose I/O 0x%08X\n", *(u32 *) (r + 0x15c));
+}
+
+int skge_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ const u32 *r = (const u32 *) regs->data;
+ int dual = !(regs->data[0x11a] & 1);
+
+ dump_pci(regs->data + 0x380);
+
+ dump_control(regs->data);
+
+ printf("\nBus Management Unit\n");
+ printf("-------------------\n");
+ printf("CSR Receive Queue 1 0x%08X\n", r[24]);
+ printf("CSR Sync Queue 1 0x%08X\n", r[26]);
+ printf("CSR Async Queue 1 0x%08X\n", r[27]);
+ if (dual) {
+ printf("CSR Receive Queue 2 0x%08X\n", r[25]);
+ printf("CSR Async Queue 2 0x%08X\n", r[29]);
+ printf("CSR Sync Queue 2 0x%08X\n", r[28]);
+ }
+
+ dump_mac(regs->data);
+ dump_gmac("GMAC 1", regs->data + 0x2800);
+
+ dump_timer("Timer", regs->data + 0x130);
+ dump_timer("Blink Source", regs->data +0x170);
+
+ dump_queue("Receive Queue 1", regs->data +0x400, 1);
+ dump_queue("Sync Transmit Queue 1", regs->data +0x600, 0);
+ dump_queue("Async Transmit Queue 1", regs->data +0x680, 0);
+
+ dump_ram("Receive RAMbuffer 1", regs->data+0x800);
+ dump_ram("Sync Transmit RAMbuffer 1", regs->data+0xa00);
+ dump_ram("Async Transmit RAMbuffer 1", regs->data+0xa80);
+
+ dump_fifo("Receive MAC FIFO 1", regs->data+0xc00);
+ dump_fifo("Transmit MAC FIFO 1", regs->data+0xd00);
+ if (dual) {
+ dump_gmac("GMAC 1", regs->data + 0x2800);
+
+ dump_queue("Receive Queue 2", regs->data +0x480, 1);
+ dump_queue("Async Transmit Queue 2", regs->data +0x780, 0);
+ dump_queue("Sync Transmit Queue 2", regs->data +0x700, 0);
+
+ dump_ram("Receive RAMbuffer 2", regs->data+0x880);
+ dump_ram("Sync Transmit RAMbuffer 2", regs->data+0xb00);
+ dump_ram("Async Transmit RAMbuffer 21", regs->data+0xb80);
+
+ dump_fifo("Receive MAC FIFO 2", regs->data+0xc80);
+ dump_fifo("Transmit MAC FIFO 2", regs->data+0xd80);
+ }
+
+ dump_timer("Descriptor Poll", regs->data+0xe00);
+ return 0;
+
+}
+
+static void dump_queue2(const char *name, void *a, int rx)
+{
+ struct sky2_queue {
+ u16 buf_control;
+ u16 byte_count;
+ u32 rss;
+ u32 addr_lo, addr_hi;
+ u32 status;
+ u32 timestamp;
+ u16 csum1, csum2;
+ u16 csum1_start, csum2_start;
+ u16 length;
+ u16 vlan;
+ u16 rsvd1;
+ u16 done;
+ u32 req_lo, req_hi;
+ u16 rsvd2;
+ u16 req_count;
+ u32 csr;
+ } *d = a;
+
+ printf("\n%s\n", name);
+ printf("---------------\n");
+
+ printf("Buffer control 0x%04X\n", d->buf_control);
+
+ printf("Byte Counter %d\n", d->byte_count);
+ printf("Descriptor Address 0x%08X%08X\n",
+ d->addr_hi, d->addr_lo);
+ printf("Status 0x%08X\n", d->status);
+ printf("Timestamp 0x%08X\n", d->timestamp);
+ printf("BMU Control/Status 0x%08X\n", d->csr);
+ printf("Done 0x%04X\n", d->done);
+ printf("Request 0x%08X%08X\n",
+ d->req_hi, d->req_lo);
+ if (rx) {
+ printf("Csum1 Offset %4d Position %d\n",
+ d->csum1, d->csum1_start);
+ printf("Csum2 Offset %4d Position %d\n",
+ d->csum2, d->csum2_start);
+ } else
+ printf("Csum Start 0x%04X Pos %4d Write %d\n",
+ d->csum1, d->csum2_start, d->csum1_start);
+}
+
+static void dump_prefetch(const char *name, const void *r)
+{
+ const u32 *reg = r;
+
+ printf("\n%s Prefetch\n", name);
+ printf("Control 0x%08X\n", reg[0]);
+ printf("Last Index %u\n", reg[1]);
+ printf("Start Address 0x%08x%08x\n", reg[3], reg[2]);
+ if (*name == 'S') { /* Status unit */
+ printf("TX1 report %u\n", reg[4]);
+ printf("TX2 report %u\n", reg[5]);
+ printf("TX threshold %u\n", reg[6]);
+ printf("Put Index %u\n", reg[7]);
+ } else {
+ printf("Get Index %u\n", reg[4]);
+ printf("Put Index %u\n", reg[5]);
+ }
+}
+
+int sky2_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ const u16 *r16 = (const u16 *) regs->data;
+ const u32 *r32 = (const u32 *) regs->data;
+ int dual;
+
+ dump_pci(regs->data + 0x1c00);
+
+ dump_control(regs->data);
+
+ printf("\nBus Management Unit\n");
+ printf("-------------------\n");
+ printf("CSR Receive Queue 1 0x%08X\n", r32[24]);
+ printf("CSR Sync Queue 1 0x%08X\n", r32[26]);
+ printf("CSR Async Queue 1 0x%08X\n", r32[27]);
+
+ dual = (regs->data[0x11e] & 2) != 0;
+ if (dual) {
+ printf("CSR Receive Queue 2 0x%08X\n", r32[25]);
+ printf("CSR Async Queue 2 0x%08X\n", r32[29]);
+ printf("CSR Sync Queue 2 0x%08X\n", r32[28]);
+ }
+
+ dump_mac(regs->data);
+
+ dump_prefetch("Status", regs->data + 0xe80);
+ dump_prefetch("Receive 1", regs->data + 0x450);
+ dump_prefetch("Transmit 1", regs->data + 0x450 + 0x280);
+
+ if (dual) {
+ dump_prefetch("Receive 2", regs->data + 0x450 + 0x80);
+ dump_prefetch("Transmit 2", regs->data + 0x450 + 0x380);
+ }
+
+ printf("\nStatus FIFO\n");
+ printf("\tWrite Pointer 0x%02X\n", regs->data[0xea0]);
+ printf("\tRead Pointer 0x%02X\n", regs->data[0xea4]);
+ printf("\tLevel 0x%02X\n", regs->data[0xea8]);
+ printf("\tWatermark 0x%02X\n", regs->data[0xeac]);
+ printf("\tISR Watermark 0x%02X\n", regs->data[0xead]);
+
+ dump_timer("Status level", regs->data + 0xeb0);
+ dump_timer("TX status", regs->data + 0xec0);
+ dump_timer("ISR", regs->data + 0xed0);
+
+ printf("\nGMAC control 0x%04X\n", r32[0xf00 >> 2]);
+ printf("GPHY control 0x%04X\n", r32[0xf04 >> 2]);
+ printf("LINK control 0x%02hX\n", r16[0xf10 >> 1]);
+
+ dump_gmac("GMAC 1", regs->data + 0x2800);
+ dump_gmac_fifo("Rx GMAC 1", regs->data + 0xc40);
+ dump_gmac_fifo("Tx GMAC 1", regs->data + 0xd40);
+
+ dump_queue2("Receive Queue 1", regs->data +0x400, 1);
+ dump_queue("Sync Transmit Queue 1", regs->data +0x600, 0);
+ dump_queue2("Async Transmit Queue 1", regs->data +0x680, 0);
+
+ dump_ram("Receive RAMbuffer 1", regs->data+0x800);
+ dump_ram("Sync Transmit RAMbuffer 1", regs->data+0xa00);
+ dump_ram("Async Transmit RAMbuffer 1", regs->data+0xa80);
+
+ if (dual) {
+ dump_ram("Receive RAMbuffer 2", regs->data+0x880);
+ dump_ram("Sync Transmit RAMbuffer 2", regs->data+0xb00);
+ dump_ram("Async Transmit RAMbuffer 21", regs->data+0xb80);
+ dump_gmac("GMAC 2", regs->data + 0x3800);
+ dump_gmac_fifo("Rx GMAC 2", regs->data + 0xc40 + 128);
+ dump_gmac_fifo("Tx GMAC 2", regs->data + 0xd40 + 128);
+ }
+
+ return 0;
+}
diff --git a/natsemi.c b/natsemi.c
new file mode 100644
index 0000000..4d9fc09
--- /dev/null
+++ b/natsemi.c
@@ -0,0 +1,987 @@
+/* Copyright 2001 Sun Microsystems (thockin@sun.com) */
+#include <stdio.h>
+#include "internal.h"
+
+#define PCI_VENDOR_NATSEMI 0x100b
+#define PCI_DEVICE_DP83815 0x0020
+#define NATSEMI_MAGIC (PCI_VENDOR_NATSEMI | \
+ (PCI_DEVICE_DP83815<<16))
+
+/* register indices in the ethtool_regs->data */
+#define REG_CR 0
+#define BIT_CR_TXE (1<<0)
+#define BIT_CR_RXE (1<<2)
+#define BIT_CR_RST (1<<8)
+#define REG_CFG 1
+#define BIT_CFG_BEM (1<<0)
+#define BIT_CFG_BROM_DIS (1<<2)
+#define BIT_CFG_PHY_DIS (1<<9)
+#define BIT_CFG_PHY_RST (1<<10)
+#define BIT_CFG_EXT_PHY (1<<12)
+#define BIT_CFG_ANEG_EN (1<<13)
+#define BIT_CFG_ANEG_100 (1<<14)
+#define BIT_CFG_ANEG_FDUP (1<<15)
+#define BIT_CFG_PINT_ACEN (1<<17)
+#define BIT_CFG_PHY_CFG (0x3f<<18)
+#define BIT_CFG_ANEG_DN (1<<27)
+#define BIT_CFG_POL (1<<28)
+#define BIT_CFG_FDUP (1<<29)
+#define BIT_CFG_SPEED100 (1<<30)
+#define BIT_CFG_LNKSTS (1<<31)
+
+#define REG_MEAR 2
+#define REG_PTSCR 3
+#define BIT_PTSCR_EEBIST_FAIL (1<<0)
+#define BIT_PTSCR_EELOAD_EN (1<<2)
+#define BIT_PTSCR_RBIST_RXFFAIL (1<<3)
+#define BIT_PTSCR_RBIST_TXFAIL (1<<4)
+#define BIT_PTSCR_RBIST_RXFAIL (1<<5)
+#define REG_ISR 4
+#define REG_IMR 5
+#define BIT_INTR_RXOK (1<<0)
+#define NAME_INTR_RXOK "Rx Complete"
+#define BIT_INTR_RXDESC (1<<1)
+#define NAME_INTR_RXDESC "Rx Descriptor"
+#define BIT_INTR_RXERR (1<<2)
+#define NAME_INTR_RXERR "Rx Packet Error"
+#define BIT_INTR_RXEARLY (1<<3)
+#define NAME_INTR_RXEARLY "Rx Early Threshold"
+#define BIT_INTR_RXIDLE (1<<4)
+#define NAME_INTR_RXIDLE "Rx Idle"
+#define BIT_INTR_RXORN (1<<5)
+#define NAME_INTR_RXORN "Rx Overrun"
+#define BIT_INTR_TXOK (1<<6)
+#define NAME_INTR_TXOK "Tx Packet OK"
+#define BIT_INTR_TXDESC (1<<7)
+#define NAME_INTR_TXDESC "Tx Descriptor"
+#define BIT_INTR_TXERR (1<<8)
+#define NAME_INTR_TXERR "Tx Packet Error"
+#define BIT_INTR_TXIDLE (1<<9)
+#define NAME_INTR_TXIDLE "Tx Idle"
+#define BIT_INTR_TXURN (1<<10)
+#define NAME_INTR_TXURN "Tx Underrun"
+#define BIT_INTR_MIB (1<<11)
+#define NAME_INTR_MIB "MIB Service"
+#define BIT_INTR_SWI (1<<12)
+#define NAME_INTR_SWI "Software"
+#define BIT_INTR_PME (1<<13)
+#define NAME_INTR_PME "Power Management Event"
+#define BIT_INTR_PHY (1<<14)
+#define NAME_INTR_PHY "Phy"
+#define BIT_INTR_HIBERR (1<<15)
+#define NAME_INTR_HIBERR "High Bits Error"
+#define BIT_INTR_RXSOVR (1<<16)
+#define NAME_INTR_RXSOVR "Rx Status FIFO Overrun"
+#define BIT_INTR_RTABT (1<<20)
+#define NAME_INTR_RTABT "Received Target Abort"
+#define BIT_INTR_RMABT (1<<20)
+#define NAME_INTR_RMABT "Received Master Abort"
+#define BIT_INTR_SSERR (1<<20)
+#define NAME_INTR_SSERR "Signaled System Error"
+#define BIT_INTR_DPERR (1<<20)
+#define NAME_INTR_DPERR "Detected Parity Error"
+#define BIT_INTR_RXRCMP (1<<20)
+#define NAME_INTR_RXRCMP "Rx Reset Complete"
+#define BIT_INTR_TXRCMP (1<<20)
+#define NAME_INTR_TXRCMP "Tx Reset Complete"
+#define REG_IER 6
+#define BIT_IER_IE (1<<0)
+#define REG_TXDP 8
+#define REG_TXCFG 9
+#define BIT_TXCFG_DRTH (0x3f<<0)
+#define BIT_TXCFG_FLTH (0x3f<<8)
+#define BIT_TXCFG_MXDMA (0x7<<20)
+#define BIT_TXCFG_ATP (1<<28)
+#define BIT_TXCFG_MLB (1<<29)
+#define BIT_TXCFG_HBI (1<<30)
+#define BIT_TXCFG_CSI (1<<31)
+#define REG_RXDP 12
+#define REG_RXCFG 13
+#define BIT_RXCFG_DRTH (0x1f<<1)
+#define BIT_RXCFG_MXDMA (0x7<<20)
+#define BIT_RXCFG_ALP (1<<27)
+#define BIT_RXCFG_ATX (1<<28)
+#define BIT_RXCFG_ARP (1<<30)
+#define BIT_RXCFG_AEP (1<<31)
+#define REG_CCSR 15
+#define BIT_CCSR_CLKRUN_EN (1<<0)
+#define BIT_CCSR_PMEEN (1<<8)
+#define BIT_CCSR_PMESTS (1<<15)
+#define REG_WCSR 16
+#define BIT_WCSR_WKPHY (1<<0)
+#define BIT_WCSR_WKUCP (1<<1)
+#define BIT_WCSR_WKMCP (1<<2)
+#define BIT_WCSR_WKBCP (1<<3)
+#define BIT_WCSR_WKARP (1<<4)
+#define BIT_WCSR_WKPAT0 (1<<5)
+#define BIT_WCSR_WKPAT1 (1<<6)
+#define BIT_WCSR_WKPAT2 (1<<7)
+#define BIT_WCSR_WKPAT3 (1<<8)
+#define BIT_WCSR_WKMAG (1<<9)
+#define BIT_WCSR_MPSOE (1<<10)
+#define BIT_WCSR_SOHACK (1<<20)
+#define BIT_WCSR_PHYINT (1<<22)
+#define BIT_WCSR_UCASTR (1<<23)
+#define BIT_WCSR_MCASTR (1<<24)
+#define BIT_WCSR_BCASTR (1<<25)
+#define BIT_WCSR_ARPR (1<<26)
+#define BIT_WCSR_PATM0 (1<<27)
+#define BIT_WCSR_PATM1 (1<<28)
+#define BIT_WCSR_PATM2 (1<<29)
+#define BIT_WCSR_PATM3 (1<<30)
+#define BIT_WCSR_MPR (1<<31)
+#define REG_PCR 17
+#define BIT_PCR_PAUSE_CNT (0xffff<<0)
+#define BIT_PCR_PSNEG (1<<21)
+#define BIT_PCR_PS_RCVD (1<<22)
+#define BIT_PCR_PS_DA (1<<29)
+#define BIT_PCR_PSMCAST (1<<30)
+#define BIT_PCR_PSEN (1<<31)
+#define REG_RFCR 18
+#define BIT_RFCR_UHEN (1<<20)
+#define BIT_RFCR_MHEN (1<<21)
+#define BIT_RFCR_AARP (1<<22)
+#define BIT_RFCR_APAT0 (1<<23)
+#define BIT_RFCR_APAT1 (1<<24)
+#define BIT_RFCR_APAT2 (1<<25)
+#define BIT_RFCR_APAT3 (1<<26)
+#define BIT_RFCR_APM (1<<27)
+#define BIT_RFCR_AAU (1<<28)
+#define BIT_RFCR_AAM (1<<29)
+#define BIT_RFCR_AAB (1<<30)
+#define BIT_RFCR_RFEN (1<<31)
+#define REG_RFDR 19
+#define REG_BRAR 20
+#define BIT_BRAR_AUTOINC (1<<31)
+#define REG_BRDR 21
+#define REG_SRR 22
+#define REG_MIBC 23
+#define BIT_MIBC_WRN (1<<0)
+#define BIT_MIBC_FRZ (1<<1)
+#define REG_MIB0 24
+#define REG_MIB1 25
+#define REG_MIB2 26
+#define REG_MIB3 27
+#define REG_MIB4 28
+#define REG_MIB5 29
+#define REG_MIB6 30
+#define REG_BMCR 32
+#define BIT_BMCR_FDUP (1<<8)
+#define BIT_BMCR_ANRST (1<<9)
+#define BIT_BMCR_ISOL (1<<10)
+#define BIT_BMCR_PDOWN (1<<11)
+#define BIT_BMCR_ANEN (1<<12)
+#define BIT_BMCR_SPEED (1<<13)
+#define BIT_BMCR_LOOP (1<<14)
+#define BIT_BMCR_RST (1<<15)
+#define REG_BMSR 33
+#define BIT_BMSR_JABBER (1<<1)
+#define BIT_BMSR_LNK (1<<2)
+#define BIT_BMSR_ANCAP (1<<3)
+#define BIT_BMSR_RFAULT (1<<4)
+#define BIT_BMSR_ANDONE (1<<5)
+#define BIT_BMSR_PREAMBLE (1<<6)
+#define BIT_BMSR_10HCAP (1<<11)
+#define BIT_BMSR_10FCAP (1<<12)
+#define BIT_BMSR_100HCAP (1<<13)
+#define BIT_BMSR_100FCAP (1<<14)
+#define BIT_BMSR_100T4CAP (1<<15)
+#define REG_PHYIDR1 34
+#define REG_PHYIDR2 35
+#define BIT_PHYIDR2_OUILSB (0x3f<<10)
+#define BIT_PHYIDR2_MODEL (0x3f<<4)
+#define BIT_PHYIDR2_REV (0xf)
+#define REG_ANAR 36
+#define BIT_ANAR_PROTO (0x1f<<0)
+#define BIT_ANAR_10 (1<<5)
+#define BIT_ANAR_10_FD (1<<6)
+#define BIT_ANAR_TX (1<<7)
+#define BIT_ANAR_TXFD (1<<8)
+#define BIT_ANAR_T4 (1<<9)
+#define BIT_ANAR_PAUSE (1<<10)
+#define BIT_ANAR_RF (1<<13)
+#define BIT_ANAR_NP (1<<15)
+#define REG_ANLPAR 37
+#define BIT_ANLPAR_PROTO (0x1f<<0)
+#define BIT_ANLPAR_10 (1<<5)
+#define BIT_ANLPAR_10_FD (1<<6)
+#define BIT_ANLPAR_TX (1<<7)
+#define BIT_ANLPAR_TXFD (1<<8)
+#define BIT_ANLPAR_T4 (1<<9)
+#define BIT_ANLPAR_PAUSE (1<<10)
+#define BIT_ANLPAR_RF (1<<13)
+#define BIT_ANLPAR_ACK (1<<14)
+#define BIT_ANLPAR_NP (1<<15)
+#define REG_ANER 38
+#define BIT_ANER_LP_AN_ENABLE (1<<0)
+#define BIT_ANER_PAGE_RX (1<<1)
+#define BIT_ANER_NP_ABLE (1<<2)
+#define BIT_ANER_LP_NP_ABLE (1<<3)
+#define BIT_ANER_PDF (1<<4)
+#define REG_ANNPTR 39
+#define REG_PHYSTS 48
+#define BIT_PHYSTS_LNK (1<<0)
+#define BIT_PHYSTS_SPD10 (1<<1)
+#define BIT_PHYSTS_FDUP (1<<2)
+#define BIT_PHYSTS_LOOP (1<<3)
+#define BIT_PHYSTS_ANDONE (1<<4)
+#define BIT_PHYSTS_JABBER (1<<5)
+#define BIT_PHYSTS_RF (1<<6)
+#define BIT_PHYSTS_MINT (1<<7)
+#define BIT_PHYSTS_FC (1<<11)
+#define BIT_PHYSTS_POL (1<<12)
+#define BIT_PHYSTS_RXERR (1<<13)
+#define REG_MICR 49
+#define BIT_MICR_INTEN (1<<1)
+#define REG_MISR 50
+#define BIT_MISR_MSK_RHF (1<<9)
+#define BIT_MISR_MSK_FHF (1<<10)
+#define BIT_MISR_MSK_ANC (1<<11)
+#define BIT_MISR_MSK_RF (1<<12)
+#define BIT_MISR_MSK_JAB (1<<13)
+#define BIT_MISR_MSK_LNK (1<<14)
+#define BIT_MISR_MINT (1<<15)
+#define REG_PGSEL 51
+#define REG_FCSCR 52
+#define REG_RECR 53
+#define REG_PCSR 54
+#define BIT_PCSR_NRZI (1<<2)
+#define BIT_PCSR_FORCE_100 (1<<5)
+#define BIT_PCSR_SDOPT (1<<8)
+#define BIT_PCSR_SDFORCE (1<<9)
+#define BIT_PCSR_TQM (1<<10)
+#define BIT_PCSR_CLK (1<<11)
+#define BIT_PCSR_4B5B (1<<12)
+#define REG_PHYCR 57
+#define BIT_PHYCR_PHYADDR (0x1f<<0)
+#define BIT_PHYCR_PAUSE_STS (1<<7)
+#define BIT_PHYCR_STRETCH (1<<8)
+#define BIT_PHYCR_BIST (1<<9)
+#define BIT_PHYCR_BIST_STAT (1<<10)
+#define BIT_PHYCR_PSR15 (1<<11)
+#define REG_TBTSCR 58
+#define BIT_TBTSCR_JAB (1<<0)
+#define BIT_TBTSCR_BEAT (1<<1)
+#define BIT_TBTSCR_AUTOPOL (1<<3)
+#define BIT_TBTSCR_POL (1<<4)
+#define BIT_TBTSCR_FPOL (1<<5)
+#define BIT_TBTSCR_FORCE_10 (1<<6)
+#define BIT_TBTSCR_PULSE (1<<7)
+#define BIT_TBTSCR_LOOP (1<<8)
+#define REG_PMDCSR 64
+#define REG_TSTDAT 65
+#define REG_DSPCFG 66
+#define REG_SDCFG 67
+#define REG_PMATCH0 68
+#define REG_PMATCH1 69
+#define REG_PMATCH2 70
+#define REG_PCOUNT0 71
+#define REG_PCOUNT1 72
+#define REG_SOPASS0 73
+#define REG_SOPASS1 74
+#define REG_SOPASS2 75
+
+static void __print_intr(int d, int intr, const char *name,
+ const char *s1, const char *s2)
+{
+ if ((d) & intr)
+ fprintf(stdout, " %s Interrupt: %s\n", name, s1);
+ else if (s2)
+ fprintf(stdout, " %s Interrupt: %s\n", name, s2);
+}
+
+#define PRINT_INTR(d, i, s1, s2) do { \
+ int intr = BIT_INTR_ ## i; \
+ const char *name = NAME_INTR_ ## i; \
+ __print_intr(d, intr, name, s1, s2); \
+} while (0)
+
+#define PRINT_INTRS(d, s1, s2) do { \
+ PRINT_INTR((d), RXOK, s1, s2); \
+ PRINT_INTR((d), RXDESC, s1, s2); \
+ PRINT_INTR((d), RXERR, s1, s2); \
+ PRINT_INTR((d), RXEARLY, s1, s2); \
+ PRINT_INTR((d), RXIDLE, s1, s2); \
+ PRINT_INTR((d), RXORN, s1, s2); \
+ PRINT_INTR((d), TXOK, s1, s2); \
+ PRINT_INTR((d), TXDESC, s1, s2); \
+ PRINT_INTR((d), TXERR, s1, s2); \
+ PRINT_INTR((d), TXIDLE, s1, s2); \
+ PRINT_INTR((d), TXURN, s1, s2); \
+ PRINT_INTR((d), MIB, s1, s2); \
+ PRINT_INTR((d), SWI, s1, s2); \
+ PRINT_INTR((d), PME, s1, s2); \
+ PRINT_INTR((d), PHY, s1, s2); \
+ PRINT_INTR((d), HIBERR, s1, s2); \
+ PRINT_INTR((d), RXSOVR, s1, s2); \
+ PRINT_INTR((d), RTABT, s1, s2); \
+ PRINT_INTR((d), RMABT, s1, s2); \
+ PRINT_INTR((d), SSERR, s1, s2); \
+ PRINT_INTR((d), DPERR, s1, s2); \
+ PRINT_INTR((d), RXRCMP, s1, s2); \
+ PRINT_INTR((d), TXRCMP, s1, s2); \
+} while (0)
+
+int
+natsemi_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *data = (u32 *)regs->data;
+ u32 tmp;
+
+ fprintf(stdout, "Mac/BIU Registers\n");
+ fprintf(stdout, "-----------------\n");
+
+ /* command register */
+ fprintf(stdout,
+ "0x00: CR (Command): 0x%08x\n",
+ data[REG_CR]);
+ fprintf(stdout,
+ " Transmit %s\n"
+ " Receive %s\n",
+ data[REG_CR] & BIT_CR_TXE ? "Active" : "Idle",
+ data[REG_CR] & BIT_CR_RXE ? "Active" : "Idle");
+ if (data[REG_CR] & BIT_CR_RST) fprintf(stdout,
+ " Reset In Progress\n");
+
+ /* configuration register */
+ fprintf(stdout,
+ "0x04: CFG (Configuration): 0x%08x\n",
+ data[REG_CFG]);
+ fprintf(stdout,
+ " %s Endian\n"
+ " Boot ROM %s\n"
+ " Internal Phy %s\n"
+ " Phy Reset %s\n"
+ " External Phy %s\n"
+ " Default Auto-Negotiation %s, %s %s Mb %s Duplex\n"
+ " Phy Interrupt %sAuto-Cleared\n"
+ " Phy Configuration = 0x%02x\n"
+ " Auto-Negotiation %s\n"
+ " %s Polarity\n"
+ " %s Duplex\n"
+ " %d Mb/s\n"
+ " Link %s\n",
+ data[REG_CFG] & BIT_CFG_BEM ? "Big" : "Little",
+ data[REG_CFG] & BIT_CFG_BROM_DIS ? "Disabled" : "Enabled",
+ data[REG_CFG] & BIT_CFG_PHY_DIS ? "Disabled" : "Enabled",
+ data[REG_CFG] & BIT_CFG_PHY_RST ? "In Progress" : "Idle",
+ data[REG_CFG] & BIT_CFG_EXT_PHY ? "Enabled" : "Disabled",
+ data[REG_CFG] & BIT_CFG_ANEG_EN ? "Enabled" : "Disabled",
+ data[REG_CFG] & BIT_CFG_ANEG_EN ? "Advertise" : "Force",
+ data[REG_CFG] & BIT_CFG_ANEG_100 ?
+ (data[REG_CFG] & BIT_CFG_ANEG_EN ? "10/100" : "100")
+ : "10",
+ data[REG_CFG] & BIT_CFG_ANEG_FDUP ?
+ (data[REG_CFG] & BIT_CFG_ANEG_EN ? "Half/Full" : "Full")
+ : "Half",
+ data[REG_CFG] & BIT_CFG_PINT_ACEN ? "" : "Not ",
+ data[REG_CFG] & BIT_CFG_PHY_CFG >> 18,
+ data[REG_CFG] & BIT_CFG_ANEG_DN ? "Done" : "Not Done",
+ data[REG_CFG] & BIT_CFG_POL ? "Reversed" : "Normal",
+ data[REG_CFG] & BIT_CFG_FDUP ? "Full" : "Half",
+ data[REG_CFG] & BIT_CFG_SPEED100 ? 100 : 10,
+ data[REG_CFG] & BIT_CFG_LNKSTS ? "Up" : "Down");
+
+ /* EEPROM access register */
+ fprintf(stdout,
+ "0x08: MEAR (EEPROM Access): 0x%08x\n",
+ data[REG_MEAR]);
+
+ /* PCI test control register */
+ fprintf(stdout,
+ "0x0c: PTSCR (PCI Test Control): 0x%08x\n",
+ data[REG_PTSCR]);
+ fprintf(stdout,
+ " EEPROM Self Test %s\n"
+ " Rx Filter Self Test %s\n"
+ " Tx FIFO Self Test %s\n"
+ " Rx FIFO Self Test %s\n",
+ data[REG_PTSCR] & BIT_PTSCR_EEBIST_FAIL ? "Failed" : "Passed",
+ data[REG_PTSCR] & BIT_PTSCR_RBIST_RXFFAIL ? "Failed" : "Passed",
+ data[REG_PTSCR] & BIT_PTSCR_RBIST_TXFAIL ? "Failed" : "Passed",
+ data[REG_PTSCR] & BIT_PTSCR_RBIST_RXFAIL ? "Failed" : "Passed");
+ if (data[REG_PTSCR] & BIT_PTSCR_EELOAD_EN) fprintf(stdout,
+ " EEPROM Reload In Progress\n");
+
+ /* Interrupt status register */
+ fprintf(stdout,
+ "0x10: ISR (Interrupt Status): 0x%08x\n",
+ data[REG_ISR]);
+ if (data[REG_ISR])
+ PRINT_INTRS(data[REG_ISR], "Active", (char *)NULL);
+ else
+ fprintf(stdout, " No Interrupts Active\n");
+
+ /* Interrupt mask register */
+ fprintf(stdout,
+ "0x14: IMR (Interrupt Mask): 0x%08x\n",
+ data[REG_IMR]);
+ PRINT_INTRS(data[REG_IMR], "Enabled", "Masked");
+
+ /* Interrupt enable register */
+ fprintf(stdout,
+ "0x18: IER (Interrupt Enable): 0x%08x\n",
+ data[REG_IER]);
+ fprintf(stdout,
+ " Interrupts %s\n",
+ data[REG_IER] & BIT_IER_IE ? "Enabled" : "Disabled");
+
+ /* Tx descriptor pointer register */
+ fprintf(stdout,
+ "0x20: TXDP (Tx Descriptor Pointer): 0x%08x\n",
+ data[REG_TXDP]);
+
+ /* Tx configuration register */
+ fprintf(stdout,
+ "0x24: TXCFG (Tx Config): 0x%08x\n",
+ data[REG_TXCFG]);
+ tmp = (data[REG_TXCFG] & BIT_TXCFG_MXDMA)>>20;
+ fprintf(stdout,
+ " Drain Threshold = %d bytes (%d)\n"
+ " Fill Threshold = %d bytes (%d)\n"
+ " Max DMA Burst per Tx = %d bytes\n"
+ " Automatic Tx Padding %s\n"
+ " Mac Loopback %s\n"
+ " Heartbeat Ignore %s\n"
+ " Carrier Sense Ignore %s\n",
+ (data[REG_TXCFG] & BIT_TXCFG_DRTH) * 32,
+ data[REG_TXCFG] & BIT_TXCFG_DRTH,
+ ((data[REG_TXCFG] & BIT_TXCFG_FLTH)>>8) * 32,
+ data[REG_TXCFG] & BIT_TXCFG_FLTH,
+ tmp ? (1<<(tmp-1))*4 : 512,
+ data[REG_TXCFG] & BIT_TXCFG_ATP ? "Enabled" : "Disabled",
+ data[REG_TXCFG] & BIT_TXCFG_MLB ? "Enabled" : "Disabled",
+ data[REG_TXCFG] & BIT_TXCFG_HBI ? "Enabled" : "Disabled",
+ data[REG_TXCFG] & BIT_TXCFG_CSI ? "Enabled" : "Disabled");
+
+
+ /* Rx descriptor pointer register */
+ fprintf(stdout,
+ "0x30: RXDP (Rx Descriptor Pointer): 0x%08x\n",
+ data[REG_RXDP]);
+
+ /* Rx configuration register */
+ fprintf(stdout,
+ "0x34: RXCFG (Rx Config): 0x%08x\n",
+ data[REG_RXCFG]);
+ tmp = (data[REG_RXCFG] & BIT_RXCFG_MXDMA)>>20;
+ fprintf(stdout,
+ " Drain Threshold = %d bytes (%d)\n"
+ " Max DMA Burst per Rx = %d bytes\n"
+ " Long Packets %s\n"
+ " Tx Packets %s\n"
+ " Runt Packets %s\n"
+ " Error Packets %s\n",
+ ((data[REG_RXCFG] & BIT_RXCFG_DRTH) >> 1) * 8,
+ (data[REG_RXCFG] & BIT_RXCFG_DRTH) >> 1,
+ tmp ? (1<<(tmp-1))*4 : 512,
+ data[REG_RXCFG] & BIT_RXCFG_ALP ? "Accepted" : "Rejected",
+ data[REG_RXCFG] & BIT_RXCFG_ATX ? "Accepted" : "Rejected",
+ data[REG_RXCFG] & BIT_RXCFG_ARP ? "Accepted" : "Rejected",
+ data[REG_RXCFG] & BIT_RXCFG_AEP ? "Accepted" : "Rejected");
+
+ /* CLKRUN control/status register */
+ fprintf(stdout,
+ "0x3c: CCSR (CLKRUN Control/Status): 0x%08x\n",
+ data[REG_CCSR]);
+ fprintf(stdout,
+ " CLKRUNN %s\n"
+ " Power Management %s\n",
+ data[REG_CCSR] & BIT_CCSR_CLKRUN_EN ? "Enabled" : "Disabled",
+ data[REG_CCSR] & BIT_CCSR_PMEEN ? "Enabled" : "Disabled");
+ if (data[REG_CCSR] & BIT_CCSR_PMESTS) fprintf(stdout,
+ " Power Management Event Pending\n");
+
+ /* WoL control/status register */
+ fprintf(stdout,
+ "0x40: WCSR (Wake-on-LAN Control/Status): 0x%08x\n",
+ data[REG_WCSR]);
+ if (data[REG_WCSR] & BIT_WCSR_WKPHY) fprintf(stdout,
+ " Wake on Phy Interrupt Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_WKUCP) fprintf(stdout,
+ " Wake on Unicast Packet Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_WKMCP) fprintf(stdout,
+ " Wake on Multicast Packet Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_WKBCP) fprintf(stdout,
+ " Wake on Broadcast Packet Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_WKARP) fprintf(stdout,
+ " Wake on Arp Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_WKPAT0) fprintf(stdout,
+ " Wake on Pattern 0 Match Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_WKPAT1) fprintf(stdout,
+ " Wake on Pattern 1 Match Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_WKPAT2) fprintf(stdout,
+ " Wake on Pattern 2 Match Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_WKPAT3) fprintf(stdout,
+ " Wake on Pattern 3 Match Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_WKMAG) fprintf(stdout,
+ " Wake on Magic Packet Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_MPSOE) fprintf(stdout,
+ " Magic Packet SecureOn Enabled\n");
+ if (data[REG_WCSR] & BIT_WCSR_SOHACK) fprintf(stdout,
+ " SecureOn Hack Detected\n");
+ if (data[REG_WCSR] & BIT_WCSR_PHYINT) fprintf(stdout,
+ " Phy Interrupt Received\n");
+ if (data[REG_WCSR] & BIT_WCSR_UCASTR) fprintf(stdout,
+ " Unicast Packet Received\n");
+ if (data[REG_WCSR] & BIT_WCSR_MCASTR) fprintf(stdout,
+ " Multicast Packet Received\n");
+ if (data[REG_WCSR] & BIT_WCSR_BCASTR) fprintf(stdout,
+ " Broadcast Packet Received\n");
+ if (data[REG_WCSR] & BIT_WCSR_ARPR) fprintf(stdout,
+ " Arp Received\n");
+ if (data[REG_WCSR] & BIT_WCSR_PATM0) fprintf(stdout,
+ " Pattern 0 Received\n");
+ if (data[REG_WCSR] & BIT_WCSR_PATM1) fprintf(stdout,
+ " Pattern 1 Received\n");
+ if (data[REG_WCSR] & BIT_WCSR_PATM2) fprintf(stdout,
+ " Pattern 2 Received\n");
+ if (data[REG_WCSR] & BIT_WCSR_PATM3) fprintf(stdout,
+ " Pattern 3 Received\n");
+ if (data[REG_WCSR] & BIT_WCSR_MPR) fprintf(stdout,
+ " Magic Packet Received\n");
+
+ /* Pause control/status register */
+ fprintf(stdout,
+ "0x44: PCR (Pause Control/Status): 0x%08x\n",
+ data[REG_PCR]);
+ fprintf(stdout,
+ " Pause Counter = %d\n"
+ " Pause %sNegotiated\n"
+ " Pause on DA %s\n"
+ " Pause on Mulitcast %s\n"
+ " Pause %s\n",
+ data[REG_PCR] & BIT_PCR_PAUSE_CNT,
+ data[REG_PCR] & BIT_PCR_PSNEG ? "" : "Not ",
+ data[REG_PCR] & BIT_PCR_PS_DA ? "Enabled" : "Disabled",
+ data[REG_PCR] & BIT_PCR_PSMCAST ? "Enabled" : "Disabled",
+ data[REG_PCR] & BIT_PCR_PSEN ? "Enabled" : "Disabled");
+ if (data[REG_PCR] & BIT_PCR_PS_RCVD) fprintf(stdout,
+ " PS_RCVD: Pause Frame Received\n");
+
+ /* Rx Filter Control */
+ fprintf(stdout,
+ "0x48: RFCR (Rx Filter Control): 0x%08x\n",
+ data[REG_RFCR]);
+ fprintf(stdout,
+ " Unicast Hash %s\n"
+ " Multicast Hash %s\n"
+ " Arp %s\n"
+ " Pattern 0 Match %s\n"
+ " Pattern 1 Match %s\n"
+ " Pattern 2 Match %s\n"
+ " Pattern 3 Match %s\n"
+ " Perfect Match %s\n"
+ " All Unicast %s\n"
+ " All Multicast %s\n"
+ " All Broadcast %s\n"
+ " Rx Filter %s\n",
+ data[REG_RFCR] & BIT_RFCR_UHEN ? "Enabled" : "Disabled",
+ data[REG_RFCR] & BIT_RFCR_MHEN ? "Enabled" : "Disabled",
+ data[REG_RFCR] & BIT_RFCR_AARP ? "Accepted" : "Rejected",
+ data[REG_RFCR] & BIT_RFCR_APAT0 ? "Accepted" : "Rejected",
+ data[REG_RFCR] & BIT_RFCR_APAT1 ? "Accepted" : "Rejected",
+ data[REG_RFCR] & BIT_RFCR_APAT2 ? "Accepted" : "Rejected",
+ data[REG_RFCR] & BIT_RFCR_APAT3 ? "Accepted" : "Rejected",
+ data[REG_RFCR] & BIT_RFCR_APM ? "Accepted" : "Rejected",
+ data[REG_RFCR] & BIT_RFCR_AAU ? "Accepted" : "Rejected",
+ data[REG_RFCR] & BIT_RFCR_AAM ? "Accepted" : "Rejected",
+ data[REG_RFCR] & BIT_RFCR_AAB ? "Accepted" : "Rejected",
+ data[REG_RFCR] & BIT_RFCR_RFEN ? "Enabled" : "Disabled");
+
+ /* Rx filter data register */
+ fprintf(stdout,
+ "0x4c: RFDR (Rx Filter Data): 0x%08x\n",
+ data[REG_RFDR]);
+ if (regs->version >= 1) fprintf(stdout,
+ " PMATCH 1-0 = 0x%08x\n"
+ " PMATCH 3-2 = 0x%08x\n"
+ " PMATCH 5-4 = 0x%08x\n"
+ " PCOUNT 1-0 = 0x%08x\n"
+ " PCOUNT 3-2 = 0x%08x\n"
+ " SOPASS 1-0 = 0x%08x\n"
+ " SOPASS 3-2 = 0x%08x\n"
+ " SOPASS 5-4 = 0x%08x\n",
+ data[REG_PMATCH0], data[REG_PMATCH1], data[REG_PMATCH2],
+ data[REG_PCOUNT0], data[REG_PCOUNT1],
+ data[REG_SOPASS0], data[REG_SOPASS1], data[REG_SOPASS2]);
+
+
+ /* Boot ROM address register */
+ fprintf(stdout,
+ "0x50: BRAR (Boot ROM Address): 0x%08x\n",
+ data[REG_BRAR]);
+ if (data[REG_BRAR] & BIT_BRAR_AUTOINC) fprintf(stdout,
+ " Automatically Increment Address\n");
+
+ /* Boot ROM data register */
+ fprintf(stdout,
+ "0x54: BRDR (Boot ROM Data): 0x%08x\n",
+ data[REG_BRDR]);
+
+ /* Silicon revison register */
+ fprintf(stdout,
+ "0x58: SRR (Silicon Revision): 0x%08x\n",
+ data[REG_SRR]);
+
+ /* Management information base control register */
+ fprintf(stdout,
+ "0x5c: MIBC (Mgmt Info Base Control): 0x%08x\n",
+ data[REG_MIBC]);
+ if (data[REG_MIBC] & BIT_MIBC_WRN) fprintf(stdout,
+ " Counter Overflow Warning\n");
+ if (data[REG_MIBC] & BIT_MIBC_FRZ) fprintf(stdout,
+ " Counters Frozen\n");
+
+ /* MIB registers */
+ fprintf(stdout,
+ "0x60: MIB[0] (Rx Errored Packets): 0x%04x\n",
+ data[REG_MIB0]);
+ fprintf(stdout, " Value = %d\n", data[REG_MIB0]);
+ fprintf(stdout,
+ "0x64: MIB[1] (Rx Frame Sequence Errors): 0x%02x\n",
+ data[REG_MIB1]);
+ fprintf(stdout, " Value = %d\n", data[REG_MIB1]);
+ fprintf(stdout,
+ "0x68: MIB[2] (Rx Missed Packets): 0x%02x\n",
+ data[REG_MIB2]);
+ fprintf(stdout, " Value = %d\n", data[REG_MIB2]);
+ fprintf(stdout,
+ "0x6c: MIB[3] (Rx Alignment Errors): 0x%02x\n",
+ data[REG_MIB3]);
+ fprintf(stdout, " Value = %d\n", data[REG_MIB3]);
+ fprintf(stdout,
+ "0x70: MIB[4] (Rx Symbol Errors): 0x%02x\n",
+ data[REG_MIB4]);
+ fprintf(stdout, " Value = %d\n", data[REG_MIB4]);
+ fprintf(stdout,
+ "0x74: MIB[5] (Rx Long Frame Errors): 0x%02x\n",
+ data[REG_MIB5]);
+ fprintf(stdout, " Value = %d\n", data[REG_MIB5]);
+ fprintf(stdout,
+ "0x78: MIB[6] (Tx Heartbeat Errors): 0x%02x\n",
+ data[REG_MIB6]);
+ fprintf(stdout, " Value = %d\n", data[REG_MIB6]);
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "Internal Phy Registers\n");
+ fprintf(stdout, "----------------------\n");
+
+ /* Basic mode control register */
+ fprintf(stdout,
+ "0x80: BMCR (Basic Mode Control): 0x%04x\n",
+ data[REG_BMCR]);
+ fprintf(stdout,
+ " %s Duplex\n"
+ " Port is Powered %s\n"
+ " Auto-Negotiation %s\n"
+ " %d Mb/s\n",
+ data[REG_BMCR] & BIT_BMCR_FDUP ? "Full" : "Half",
+ data[REG_BMCR] & BIT_BMCR_PDOWN ? "Down" : "Up",
+ data[REG_BMCR] & BIT_BMCR_ANEN ? "Enabled" : "Disabled",
+ data[REG_BMCR] & BIT_BMCR_SPEED ? 100 : 10);
+ if (data[REG_BMCR] & BIT_BMCR_ANRST) fprintf(stdout,
+ " Auto-Negotiation Restarting\n");
+ if (data[REG_BMCR] & BIT_BMCR_ISOL) fprintf(stdout,
+ " Port Isolated\n");
+ if (data[REG_BMCR] & BIT_BMCR_LOOP) fprintf(stdout,
+ " Loopback Enabled\n");
+ if (data[REG_BMCR] & BIT_BMCR_RST) fprintf(stdout,
+ " Reset In Progress\n");
+
+ /* Basic mode status register */
+ fprintf(stdout,
+ "0x84: BMSR (Basic Mode Status): 0x%04x\n",
+ data[REG_BMSR]);
+ fprintf(stdout,
+ " Link %s\n"
+ " %sCapable of Auto-Negotiation\n"
+ " Auto-Negotiation %sComplete\n"
+ " %sCapable of Preamble Suppression\n"
+ " %sCapable of 10Base-T Half Duplex\n"
+ " %sCapable of 10Base-T Full Duplex\n"
+ " %sCapable of 100Base-TX Half Duplex\n"
+ " %sCapable of 100Base-TX Full Duplex\n"
+ " %sCapable of 100Base-T4\n",
+ data[REG_BMSR] & BIT_BMSR_LNK ? "Up" : "Down",
+ data[REG_BMSR] & BIT_BMSR_ANCAP ? "" : "Not ",
+ data[REG_BMSR] & BIT_BMSR_ANDONE ? "" : "Not ",
+ data[REG_BMSR] & BIT_BMSR_PREAMBLE ? "" : "Not ",
+ data[REG_BMSR] & BIT_BMSR_10HCAP ? "" : "Not ",
+ data[REG_BMSR] & BIT_BMSR_10FCAP ? "" : "Not ",
+ data[REG_BMSR] & BIT_BMSR_100HCAP ? "" : "Not ",
+ data[REG_BMSR] & BIT_BMSR_100FCAP ? "" : "Not ",
+ data[REG_BMSR] & BIT_BMSR_100T4CAP ? "" : "Not ");
+ if (data[REG_BMSR] & BIT_BMSR_JABBER) fprintf(stdout,
+ " Jabber Condition Detected\n");
+ if (data[REG_BMSR] & BIT_BMSR_RFAULT) fprintf(stdout,
+ " Remote Fault Detected\n");
+
+ /* PHY identification registers */
+ fprintf(stdout,
+ "0x88: PHYIDR1 (PHY ID #1): 0x%04x\n",
+ data[REG_PHYIDR1]);
+ fprintf(stdout,
+ "0x8c: PHYIDR2 (PHY ID #2): 0x%04x\n",
+ data[REG_PHYIDR2]);
+ fprintf(stdout,
+ " OUI = 0x%06x\n"
+ " Model = 0x%02x (%d)\n"
+ " Revision = 0x%01x (%d)\n",
+ (data[REG_PHYIDR1] << 6) | (data[REG_PHYIDR2] >> 10),
+ (data[REG_PHYIDR2] & BIT_PHYIDR2_MODEL) >> 4 & 0x3f,
+ (data[REG_PHYIDR2] & BIT_PHYIDR2_MODEL) >> 4 & 0x3f,
+ data[REG_PHYIDR2] & BIT_PHYIDR2_REV,
+ data[REG_PHYIDR2] & BIT_PHYIDR2_REV);
+
+ /* autonegotiation advertising register */
+ fprintf(stdout,
+ "0x90: ANAR (Autoneg Advertising): 0x%04x\n",
+ data[REG_ANAR]);
+ fprintf(stdout,
+ " Protocol Selector = 0x%02x (%d)\n",
+ data[REG_ANAR] & BIT_ANAR_PROTO,
+ data[REG_ANAR] & BIT_ANAR_PROTO);
+ if (data[REG_ANAR] & BIT_ANAR_10) fprintf(stdout,
+ " Advertising 10Base-T Half Duplex\n");
+ if (data[REG_ANAR] & BIT_ANAR_10_FD) fprintf(stdout,
+ " Advertising 10Base-T Full Duplex\n");
+ if (data[REG_ANAR] & BIT_ANAR_TX) fprintf(stdout,
+ " Advertising 100Base-TX Half Duplex\n");
+ if (data[REG_ANAR] & BIT_ANAR_TXFD) fprintf(stdout,
+ " Advertising 100Base-TX Full Duplex\n");
+ if (data[REG_ANAR] & BIT_ANAR_T4) fprintf(stdout,
+ " Advertising 100Base-T4\n");
+ if (data[REG_ANAR] & BIT_ANAR_PAUSE) fprintf(stdout,
+ " Advertising Pause\n");
+ if (data[REG_ANAR] & BIT_ANAR_RF) fprintf(stdout,
+ " Indicating Remote Fault\n");
+ if (data[REG_ANAR] & BIT_ANAR_NP) fprintf(stdout,
+ " Next Page Desired\n");
+
+ /* Autonegotiation link partner ability register */
+ fprintf(stdout,
+ "0x94: ANLPAR (Autoneg Partner): 0x%04x\n",
+ data[REG_ANLPAR]);
+ fprintf(stdout,
+ " Protocol Selector = 0x%02x (%d)\n",
+ data[REG_ANLPAR] & BIT_ANLPAR_PROTO,
+ data[REG_ANLPAR] & BIT_ANLPAR_PROTO);
+ if (data[REG_ANLPAR] & BIT_ANLPAR_10) fprintf(stdout,
+ " Supports 10Base-T Half Duplex\n");
+ if (data[REG_ANLPAR] & BIT_ANLPAR_10_FD) fprintf(stdout,
+ " Supports 10Base-T Full Duplex\n");
+ if (data[REG_ANLPAR] & BIT_ANLPAR_TX) fprintf(stdout,
+ " Supports 100Base-TX Half Duplex\n");
+ if (data[REG_ANLPAR] & BIT_ANLPAR_TXFD) fprintf(stdout,
+ " Supports 100Base-TX Full Duplex\n");
+ if (data[REG_ANLPAR] & BIT_ANLPAR_T4) fprintf(stdout,
+ " Supports 100Base-T4\n");
+ if (data[REG_ANLPAR] & BIT_ANLPAR_PAUSE) fprintf(stdout,
+ " Supports Pause\n");
+ if (data[REG_ANLPAR] & BIT_ANLPAR_RF) fprintf(stdout,
+ " Indicates Remote Fault\n");
+ if (data[REG_ANLPAR] & BIT_ANLPAR_ACK) fprintf(stdout,
+ " Indicates Acknowledgement\n");
+ if (data[REG_ANLPAR] & BIT_ANLPAR_NP) fprintf(stdout,
+ " Next Page Desired\n");
+
+ /* Autonegotiation expansion register */
+ fprintf(stdout,
+ "0x98: ANER (Autoneg Expansion): 0x%04x\n",
+ data[REG_ANER]);
+ fprintf(stdout,
+ " Link Partner Can %sAuto-Negotiate\n"
+ " Link Code Word %sReceived\n"
+ " Next Page %sSupported\n"
+ " Link Partner Next Page %sSupported\n",
+ data[REG_ANER] & BIT_ANER_LP_AN_ENABLE ? "" : "Not ",
+ data[REG_ANER] & BIT_ANER_PAGE_RX ? "" : "Not ",
+ data[REG_ANER] & BIT_ANER_NP_ABLE ? "" : "Not ",
+ data[REG_ANER] & BIT_ANER_LP_NP_ABLE ? "" : "Not ");
+ if (data[REG_ANER] & BIT_ANER_PDF) fprintf(stdout,
+ " Parallel Detection Fault\n");
+
+ /* Autonegotiation next-page tx register */
+ fprintf(stdout,
+ "0x9c: ANNPTR (Autoneg Next Page Tx): 0x%04x\n",
+ data[REG_ANNPTR]);
+
+ /* Phy status register */
+ fprintf(stdout,
+ "0xc0: PHYSTS (Phy Status): 0x%04x\n",
+ data[REG_PHYSTS]);
+ fprintf(stdout,
+ " Link %s\n"
+ " %d Mb/s\n"
+ " %s Duplex\n"
+ " Auto-Negotiation %sComplete\n"
+ " %s Polarity\n",
+ data[REG_PHYSTS] & BIT_PHYSTS_LNK ? "Up" : "Down",
+ data[REG_PHYSTS] & BIT_PHYSTS_SPD10 ? 10 : 100,
+ data[REG_PHYSTS] & BIT_PHYSTS_FDUP ? "Full" : "Half",
+ data[REG_PHYSTS] & BIT_PHYSTS_ANDONE ? "" : "Not ",
+ data[REG_PHYSTS] & BIT_PHYSTS_POL ? "Reverse" : "Normal");
+ if (data[REG_PHYSTS] & BIT_PHYSTS_LOOP) fprintf(stdout,
+ " Loopback Enabled\n");
+ if (data[REG_PHYSTS] & BIT_PHYSTS_JABBER) fprintf(stdout,
+ " Jabber Condition Detected\n");
+ if (data[REG_PHYSTS] & BIT_PHYSTS_RF) fprintf(stdout,
+ " Remote Fault Detected\n");
+ if (data[REG_PHYSTS] & BIT_PHYSTS_MINT) fprintf(stdout,
+ " MII Interrupt Detected\n");
+ if (data[REG_PHYSTS] & BIT_PHYSTS_FC) fprintf(stdout,
+ " False Carrier Detected\n");
+ if (data[REG_PHYSTS] & BIT_PHYSTS_RXERR) fprintf(stdout,
+ " Rx Error Detected\n");
+
+ fprintf(stdout,
+ "0xc4: MICR (MII Interrupt Control): 0x%04x\n",
+ data[REG_MICR]);
+ fprintf(stdout,
+ " MII Interrupts %s\n",
+ data[REG_MICR] & BIT_MICR_INTEN ? "Enabled" : "Disabled");
+
+ fprintf(stdout,
+ "0xc8: MISR (MII Interrupt Status): 0x%04x\n",
+ data[REG_MISR]);
+ fprintf(stdout,
+ " Rx Error Counter Half-Full Interrupt %s\n"
+ " False Carrier Counter Half-Full Interrupt %s\n"
+ " Auto-Negotiation Complete Interrupt %s\n"
+ " Remote Fault Interrupt %s\n"
+ " Jabber Interrupt %s\n"
+ " Link Change Interrupt %s\n",
+ data[REG_MISR] & BIT_MISR_MSK_RHF ? "Masked" : "Enabled",
+ data[REG_MISR] & BIT_MISR_MSK_FHF ? "Masked" : "Enabled",
+ data[REG_MISR] & BIT_MISR_MSK_ANC ? "Masked" : "Enabled",
+ data[REG_MISR] & BIT_MISR_MSK_RF ? "Masked" : "Enabled",
+ data[REG_MISR] & BIT_MISR_MSK_JAB ? "Masked" : "Enabled",
+ data[REG_MISR] & BIT_MISR_MSK_LNK ? "Masked" : "Enabled");
+ if (data[REG_MISR] & BIT_MISR_MINT) fprintf(stdout,
+ " MII Interrupt Pending\n");
+
+ /* Page select register (from section of spec on 'suggested values') */
+ fprintf(stdout,
+ "0xcc: PGSEL (Phy Register Page Select): 0x%04x\n",
+ data[REG_PGSEL]);
+
+ /* counters */
+ fprintf(stdout,
+ "0xd0: FCSCR (False Carrier Counter): 0x%04x\n",
+ data[REG_FCSCR]);
+ fprintf(stdout,
+ " Value = %d\n", data[REG_FCSCR] & 0xff);
+ fprintf(stdout,
+ "0xd4: RECR (Rx Error Counter): 0x%04x\n",
+ data[REG_RECR]);
+ fprintf(stdout,
+ " Value = %d\n", data[REG_RECR] & 0xff);
+
+ /* 100 Mbit configuration register */
+ fprintf(stdout,
+ "0xd8: PCSR (100Mb/s PCS Config/Status): 0x%04x\n",
+ data[REG_PCSR]);
+ fprintf(stdout,
+ " NRZI Bypass %s\n"
+ " %s Signal Detect Algorithm\n"
+ " %s Signal Detect Operation\n"
+ " True Quiet Mode %s\n"
+ " Rx Clock is %s\n"
+ " 4B/5B Operation %s\n",
+ data[REG_PCSR] & BIT_PCSR_NRZI ? "Enabled" : "Disabled",
+ data[REG_PCSR] & BIT_PCSR_SDOPT ? "Enhanced" : "Reduced",
+ data[REG_PCSR] & BIT_PCSR_SDFORCE ? "Forced" : "Normal",
+ data[REG_PCSR] & BIT_PCSR_TQM ? "Enabled" : "Disabled",
+ data[REG_PCSR] & BIT_PCSR_CLK ?
+ "Free-Running" : "Phase-Adjusted",
+ data[REG_PCSR] & BIT_PCSR_4B5B ? "Bypassed" : "Normal");
+ if (data[REG_PCSR] & BIT_PCSR_FORCE_100) fprintf(stdout,
+ " Forced 100 Mb/s Good Link\n");
+
+ /* Phy control register */
+ fprintf(stdout,
+ "0xe4: PHYCR (Phy Control): 0x%04x\n",
+ data[REG_PHYCR]);
+ fprintf(stdout,
+ " Phy Address = 0x%x (%d)\n"
+ " %sPause Compatible with Link Partner\n"
+ " LED Stretching %s\n"
+ " Phy Self Test %s\n"
+ " Self Test Sequence = PSR%d\n",
+ data[REG_PHYCR] & BIT_PHYCR_PHYADDR,
+ data[REG_PHYCR] & BIT_PHYCR_PHYADDR,
+ data[REG_PHYCR] & BIT_PHYCR_PAUSE_STS ? "" : "Not ",
+ data[REG_PHYCR] & BIT_PHYCR_STRETCH ? "Bypassed" : "Enabled",
+ data[REG_PHYCR] & BIT_PHYCR_BIST ? "In Progress" :
+ data[REG_PHYCR] & BIT_PHYCR_BIST_STAT ?
+ "Passed" : "Failed or Not Run",
+ data[REG_PHYCR] & BIT_PHYCR_PSR15 ? 15 : 9);
+
+
+ /* 10 Mbit control and status register */
+ fprintf(stdout,
+ "0xe8: TBTSCR (10Base-T Status/Control): 0x%04x\n",
+ data[REG_TBTSCR]);
+ fprintf(stdout,
+ " Jabber %s\n"
+ " Heartbeat %s\n"
+ " Polarity Auto-Sense/Correct %s\n"
+ " %s Polarity %s\n"
+ " Normal Link Pulse %s\n"
+ " 10 Mb/s Loopback %s\n",
+ data[REG_TBTSCR] & BIT_TBTSCR_JAB ? "Disabled" : "Enabled",
+ data[REG_TBTSCR] & BIT_TBTSCR_BEAT ? "Disabled" : "Enabled",
+ data[REG_TBTSCR] & BIT_TBTSCR_AUTOPOL ? "Disabled" : "Enabled",
+ data[REG_TBTSCR] & BIT_TBTSCR_AUTOPOL ?
+ data[REG_TBTSCR]&BIT_TBTSCR_FPOL ? "Reverse":"Normal" :
+ data[REG_TBTSCR]&BIT_TBTSCR_POL ? "Reverse":"Normal",
+ data[REG_TBTSCR] & BIT_TBTSCR_AUTOPOL ? "Forced" : "Detected",
+ data[REG_TBTSCR] & BIT_TBTSCR_PULSE ? "Disabled" : "Enabled",
+ data[REG_TBTSCR] & BIT_TBTSCR_LOOP ? "Enabled" : "Disabled");
+ if (data[REG_TBTSCR] & BIT_TBTSCR_FORCE_10) fprintf(stdout,
+ " Forced 10 Mb/s Good Link\n");
+
+ /* the spec says to set these */
+ fprintf(stdout, "\n");
+ fprintf(stdout, "'Magic' Phy Registers\n");
+ fprintf(stdout, "---------------------\n");
+ fprintf(stdout,
+ "0xe4: PMDCSR: 0x%04x\n",
+ data[REG_PMDCSR]);
+ fprintf(stdout,
+ "0xf4: DSPCFG: 0x%04x\n",
+ data[REG_DSPCFG]);
+ fprintf(stdout,
+ "0xf8: SDCFG: 0x%04x\n",
+ data[REG_SDCFG]);
+ fprintf(stdout,
+ "0xfc: TSTDAT: 0x%04x\n",
+ data[REG_TSTDAT]);
+
+ return 0;
+}
+
+int
+natsemi_dump_eeprom(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_eeprom *ee)
+{
+ u16 *eebuf = (u16 *)ee->data;
+ unsigned int i;
+
+ if (ee->magic != NATSEMI_MAGIC) {
+ fprintf(stderr, "Magic number 0x%08x does not match 0x%08x\n",
+ ee->magic, NATSEMI_MAGIC);
+ return -1;
+ }
+
+ fprintf(stdout, "Address\tData\n");
+ fprintf(stdout, "-------\t------\n");
+ for (i = 0; i < ee->len/2; i++) {
+ fprintf(stdout, "0x%02x \t0x%04x\n", i + ee->offset, eebuf[i]);
+ }
+
+ return 0;
+}
+
diff --git a/netlink/bitset.c b/netlink/bitset.c
new file mode 100644
index 0000000..10ce8e9
--- /dev/null
+++ b/netlink/bitset.c
@@ -0,0 +1,259 @@
+/*
+ * bitset.h - netlink bitset helpers
+ *
+ * Functions for easier handling of ethtool netlink bitset attributes.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+
+uint32_t bitset_get_count(const struct nlattr *bitset, int *retptr)
+{
+ const struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, bitset) {
+ if (mnl_attr_get_type(attr) != ETHTOOL_A_BITSET_SIZE)
+ continue;
+ *retptr = 0;
+ return mnl_attr_get_u32(attr);
+ }
+
+ *retptr = -EFAULT;
+ return 0;
+}
+
+bool bitset_is_compact(const struct nlattr *bitset)
+{
+ const struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, bitset) {
+ switch(mnl_attr_get_type(attr)) {
+ case ETHTOOL_A_BITSET_BITS:
+ return 0;
+ case ETHTOOL_A_BITSET_VALUE:
+ case ETHTOOL_A_BITSET_MASK:
+ return 1;
+ }
+ }
+
+ return false;
+}
+
+bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
+ int *retptr)
+{
+ const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(bitset_tb);
+ const struct nlattr *bits;
+ const struct nlattr *bit;
+ bool nomask;
+ int ret;
+
+ *retptr = 0;
+ ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
+ if (ret < 0)
+ goto err;
+
+ nomask = bitset_tb[ETHTOOL_A_BITSET_NOMASK];
+ if (mask && nomask) {
+ /* Trying to determine if a bit is set in the mask of a "no
+ * mask" bitset doesn't make sense.
+ */
+ ret = -EFAULT;
+ goto err;
+ }
+
+ bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] :
+ bitset_tb[ETHTOOL_A_BITSET_VALUE];
+ if (bits) {
+ const uint32_t *bitmap =
+ (const uint32_t *)mnl_attr_get_payload(bits);
+
+ if (idx >= 8 * mnl_attr_get_payload_len(bits))
+ return false;
+ return bitmap[idx / 32] & (1U << (idx % 32));
+ }
+
+ bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
+ if (!bits)
+ goto err;
+ mnl_attr_for_each_nested(bit, bits) {
+ const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned int my_idx;
+
+ if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT)
+ continue;
+ ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+ ret = -EFAULT;
+ if (!tb[ETHTOOL_A_BITSET_BIT_INDEX])
+ goto err;
+
+ my_idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
+ if (my_idx == idx)
+ return mask || nomask || tb[ETHTOOL_A_BITSET_BIT_VALUE];
+ }
+
+ return false;
+err:
+ fprintf(stderr, "malformed netlink message (bitset)\n");
+ *retptr = ret;
+ return false;
+}
+
+bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr)
+{
+ const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(bitset_tb);
+ const struct nlattr *bits;
+ const struct nlattr *bit;
+ int ret;
+
+ *retptr = 0;
+ ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
+ if (ret < 0)
+ goto err;
+
+ bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] :
+ bitset_tb[ETHTOOL_A_BITSET_VALUE];
+ if (bits) {
+ const uint32_t *bitmap =
+ (const uint32_t *)mnl_attr_get_payload(bits);
+ unsigned int n = mnl_attr_get_payload_len(bits);
+ unsigned int i;
+
+ ret = -EFAULT;
+ if (n % 4)
+ goto err;
+ for (i = 0; i < n / 4; i++)
+ if (bitmap[i])
+ return false;
+ return true;
+ }
+
+ bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
+ if (!bits)
+ goto err;
+ mnl_attr_for_each_nested(bit, bits) {
+ const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+
+ if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT)
+ continue;
+ if (mask || bitset_tb[ETHTOOL_A_BITSET_NOMASK])
+ return false;
+
+ ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+ if (tb[ETHTOOL_A_BITSET_BIT_VALUE])
+ return false;
+ }
+
+ return true;
+err:
+ fprintf(stderr, "malformed netlink message (bitset)\n");
+ *retptr = ret;
+ return true;
+}
+
+static uint32_t *get_compact_bitset_attr(const struct nlattr *bitset,
+ uint16_t type)
+{
+ const struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned int count;
+ int ret;
+
+ ret = mnl_attr_parse_nested(bitset, attr_cb, &tb_info);
+ if (ret < 0)
+ return NULL;
+ if (!tb[ETHTOOL_A_BITSET_SIZE] || !tb[ETHTOOL_A_BITSET_VALUE] ||
+ !tb[type])
+ return NULL;
+ count = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_SIZE]);
+ if (8 * mnl_attr_get_payload_len(tb[type]) < count)
+ return NULL;
+
+ return mnl_attr_get_payload(tb[type]);
+}
+
+uint32_t *get_compact_bitset_value(const struct nlattr *bitset)
+{
+ return get_compact_bitset_attr(bitset, ETHTOOL_A_BITSET_VALUE);
+}
+
+uint32_t *get_compact_bitset_mask(const struct nlattr *bitset)
+{
+ return get_compact_bitset_attr(bitset, ETHTOOL_A_BITSET_MASK);
+}
+
+int walk_bitset(const struct nlattr *bitset, const struct stringset *labels,
+ bitset_walk_callback cb, void *data)
+{
+ const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(bitset_tb);
+ const struct nlattr *bits;
+ const struct nlattr *bit;
+ bool is_list;
+ int ret;
+
+ ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
+ if (ret < 0)
+ return ret;
+ is_list = bitset_tb[ETHTOOL_A_BITSET_NOMASK];
+
+ bits = bitset_tb[ETHTOOL_A_BITSET_VALUE];
+ if (bits) {
+ const struct nlattr *mask = bitset_tb[ETHTOOL_A_BITSET_MASK];
+ unsigned int count, nwords, idx;
+ uint32_t *val_bm;
+ uint32_t *mask_bm;
+
+ if (!bitset_tb[ETHTOOL_A_BITSET_SIZE])
+ return -EFAULT;
+ count = mnl_attr_get_u32(bitset_tb[ETHTOOL_A_BITSET_SIZE]);
+ nwords = (count + 31) / 32;
+ if ((mnl_attr_get_payload_len(bits) / 4 < nwords) ||
+ (mask && mnl_attr_get_payload_len(mask) / 4 < nwords))
+ return -EFAULT;
+
+ val_bm = mnl_attr_get_payload(bits);
+ mask_bm = mask ? mnl_attr_get_payload(mask) : NULL;
+ for (idx = 0; idx < count; idx++)
+ if (!mask_bm || (mask_bm[idx / 32] & (1 << (idx % 32))))
+ cb(idx, get_string(labels, idx),
+ val_bm[idx / 32] & (1 << (idx % 32)), data);
+ return 0;
+ }
+
+ bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
+ if (!bits)
+ return -EFAULT;
+ mnl_attr_for_each_nested(bit, bits) {
+ const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const char *name;
+ unsigned int idx;
+
+ if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT)
+ continue;
+
+ ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
+ if (ret < 0 || !tb[ETHTOOL_A_BITSET_BIT_INDEX] ||
+ !tb[ETHTOOL_A_BITSET_BIT_NAME])
+ return -EFAULT;
+
+ idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
+ name = mnl_attr_get_str(tb[ETHTOOL_A_BITSET_BIT_NAME]);
+ cb(idx, name, is_list || tb[ETHTOOL_A_BITSET_BIT_VALUE], data);
+ }
+
+ return 0;
+}
diff --git a/netlink/bitset.h b/netlink/bitset.h
new file mode 100644
index 0000000..4c9cdac
--- /dev/null
+++ b/netlink/bitset.h
@@ -0,0 +1,28 @@
+/*
+ * bitset.h - netlink bitset helpers
+ *
+ * Declarations of helpers for handling ethtool netlink bitsets.
+ */
+
+#ifndef ETHTOOL_NETLINK_BITSET_H__
+#define ETHTOOL_NETLINK_BITSET_H__
+
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#include <linux/ethtool_netlink.h>
+#include "strset.h"
+
+typedef void (*bitset_walk_callback)(unsigned int, const char *, bool, void *);
+
+uint32_t bitset_get_count(const struct nlattr *bitset, int *retptr);
+bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
+ int *retptr);
+bool bitset_is_compact(const struct nlattr *bitset);
+bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr);
+uint32_t *get_compact_bitset_value(const struct nlattr *bitset);
+uint32_t *get_compact_bitset_mask(const struct nlattr *bitset);
+int walk_bitset(const struct nlattr *bitset, const struct stringset *labels,
+ bitset_walk_callback cb, void *data);
+
+#endif /* ETHTOOL_NETLINK_BITSET_H__ */
diff --git a/netlink/cable_test.c b/netlink/cable_test.c
new file mode 100644
index 0000000..9305a47
--- /dev/null
+++ b/netlink/cable_test.c
@@ -0,0 +1,595 @@
+/*
+ * cable_test.c - netlink implementation of cable test command
+ *
+ * Implementation of ethtool --cable-test <dev>
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+struct cable_test_context {
+ bool breakout;
+};
+
+static int nl_get_cable_test_result(const struct nlattr *nest, uint8_t *pair,
+ uint16_t *code)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_RESULT_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_RESULT_PAIR] ||
+ !tb[ETHTOOL_A_CABLE_RESULT_CODE])
+ return -EFAULT;
+
+ *pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_PAIR]);
+ *code = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_CODE]);
+
+ return 0;
+}
+
+static int nl_get_cable_test_fault_length(const struct nlattr *nest,
+ uint8_t *pair, unsigned int *cm)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_FAULT_LENGTH_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR] ||
+ !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM])
+ return -EFAULT;
+
+ *pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR]);
+ *cm = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM]);
+
+ return 0;
+}
+
+static char *nl_code2txt(uint16_t code)
+{
+ switch (code) {
+ case ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC:
+ default:
+ return "Unknown";
+ case ETHTOOL_A_CABLE_RESULT_CODE_OK:
+ return "OK";
+ case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
+ return "Open Circuit";
+ case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
+ return "Short within Pair";
+ case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
+ return "Short to another pair";
+ }
+}
+
+static char *nl_pair2txt(uint8_t pair)
+{
+ switch (pair) {
+ case ETHTOOL_A_CABLE_PAIR_A:
+ return "Pair A";
+ case ETHTOOL_A_CABLE_PAIR_B:
+ return "Pair B";
+ case ETHTOOL_A_CABLE_PAIR_C:
+ return "Pair C";
+ case ETHTOOL_A_CABLE_PAIR_D:
+ return "Pair D";
+ default:
+ return "Unexpected pair";
+ }
+}
+
+static int nl_cable_test_ntf_attr(struct nlattr *evattr)
+{
+ unsigned int cm;
+ uint16_t code;
+ uint8_t pair;
+ int ret;
+
+ switch (mnl_attr_get_type(evattr)) {
+ case ETHTOOL_A_CABLE_NEST_RESULT:
+ ret = nl_get_cable_test_result(evattr, &pair, &code);
+ if (ret < 0)
+ return ret;
+
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair));
+ print_string(PRINT_ANY, "code", "code %s\n", nl_code2txt(code));
+ close_json_object();
+ break;
+
+ case ETHTOOL_A_CABLE_NEST_FAULT_LENGTH:
+ ret = nl_get_cable_test_fault_length(evattr, &pair, &cm);
+ if (ret < 0)
+ return ret;
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "pair", "%s, ", nl_pair2txt(pair));
+ print_float(PRINT_ANY, "length", "fault length: %0.2fm\n",
+ (float)cm / 100);
+ close_json_object();
+ break;
+ }
+ return 0;
+}
+
+static void cable_test_ntf_nest(const struct nlattr *nest)
+{
+ struct nlattr *pos;
+ int ret;
+
+ mnl_attr_for_each_nested(pos, nest) {
+ ret = nl_cable_test_ntf_attr(pos);
+ if (ret < 0)
+ return;
+ }
+}
+
+/* Returns MNL_CB_STOP when the test is complete. Used when executing
+ * a test, but not suitable for monitor.
+ */
+static int cable_test_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_NTF_MAX + 1] = {};
+ u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC;
+ struct cable_test_context *ctctx;
+ struct nl_context *nlctx = data;
+ DECLARE_ATTR_TB_INFO(tb);
+ bool silent;
+ int err_ret;
+ int ret;
+
+ ctctx = nlctx->cmd_private;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS])
+ status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]);
+
+ switch (status) {
+ case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED:
+ print_string(PRINT_FP, "status",
+ "Cable test started for device %s.\n",
+ nlctx->devname);
+ break;
+ case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED:
+ print_string(PRINT_FP, "status",
+ "Cable test completed for device %s.\n",
+ nlctx->devname);
+ break;
+ default:
+ break;
+ }
+
+ if (tb[ETHTOOL_A_CABLE_TEST_NTF_NEST])
+ cable_test_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_NTF_NEST]);
+
+ if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) {
+ if (ctctx)
+ ctctx->breakout = true;
+ return MNL_CB_STOP;
+ }
+
+ return MNL_CB_OK;
+}
+
+/* Wrapper around cable_test_ntf_stop_cb() which does not return STOP,
+ * used for monitor
+ */
+int cable_test_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ int status = cable_test_ntf_stop_cb(nlhdr, data);
+
+ if (status == MNL_CB_STOP)
+ status = MNL_CB_OK;
+
+ return status;
+}
+
+static int nl_cable_test_results_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+
+ if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_NTF)
+ return MNL_CB_OK;
+
+ return cable_test_ntf_stop_cb(nlhdr, data);
+}
+
+/* Receive the broadcasted messages until we get the cable test
+ * results
+ */
+static int nl_cable_test_process_results(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ struct cable_test_context ctctx;
+ int err;
+
+ nlctx->is_monitor = true;
+ nlsk->port = 0;
+ nlsk->seq = 0;
+ nlctx->filter_devname = ctx->devname;
+
+ ctctx.breakout = false;
+ nlctx->cmd_private = &ctctx;
+
+ while (!ctctx.breakout) {
+ err = nlsock_process_reply(nlsk, nl_cable_test_results_cb,
+ nlctx);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
+int nl_cable_test(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ uint32_t grpid = nlctx->ethnl_mongrp;
+ int ret;
+
+ /* Join the multicast group so we can receive the results in a
+ * race free way.
+ */
+ if (!grpid) {
+ fprintf(stderr, "multicast group 'monitor' not found\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
+ &grpid, sizeof(grpid));
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_CABLE_TEST_ACT,
+ ETHTOOL_A_CABLE_TEST_HEADER, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ fprintf(stderr, "Cannot start cable test\n");
+ else {
+ new_json_obj(ctx->json);
+
+ ret = nl_cable_test_process_results(ctx);
+
+ delete_json_obj();
+ }
+
+ return ret;
+}
+
+static int nl_get_cable_test_tdr_amplitude(const struct nlattr *nest,
+ uint8_t *pair, int16_t *mV)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_AMPLITUDE_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ uint16_t mV_unsigned;
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR] ||
+ !tb[ETHTOOL_A_CABLE_AMPLITUDE_mV])
+ return -EFAULT;
+
+ *pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR]);
+ mV_unsigned = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_AMPLITUDE_mV]);
+ *mV = (int16_t)(mV_unsigned);
+
+ return 0;
+}
+
+static int nl_get_cable_test_tdr_pulse(const struct nlattr *nest, uint16_t *mV)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_PULSE_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_PULSE_mV])
+ return -EFAULT;
+
+ *mV = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_PULSE_mV]);
+
+ return 0;
+}
+
+static int nl_get_cable_test_tdr_step(const struct nlattr *nest,
+ uint32_t *first, uint32_t *last,
+ uint32_t *step)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_STEP_MAX+1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 ||
+ !tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE] ||
+ !tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE] ||
+ !tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE])
+ return -EFAULT;
+
+ *first = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE]);
+ *last = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE]);
+ *step = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE]);
+
+ return 0;
+}
+
+static int nl_cable_test_tdr_ntf_attr(struct nlattr *evattr)
+{
+ uint32_t first, last, step;
+ uint8_t pair;
+ int ret;
+
+ switch (mnl_attr_get_type(evattr)) {
+ case ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE: {
+ int16_t mV;
+
+ ret = nl_get_cable_test_tdr_amplitude(
+ evattr, &pair, &mV);
+ if (ret < 0)
+ return ret;
+
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair));
+ print_int(PRINT_ANY, "amplitude", "Amplitude %4d\n", mV);
+ close_json_object();
+ break;
+ }
+ case ETHTOOL_A_CABLE_TDR_NEST_PULSE: {
+ uint16_t mV;
+
+ ret = nl_get_cable_test_tdr_pulse(evattr, &mV);
+ if (ret < 0)
+ return ret;
+
+ open_json_object(NULL);
+ print_uint(PRINT_ANY, "pulse", "TDR Pulse %dmV\n", mV);
+ close_json_object();
+ break;
+ }
+ case ETHTOOL_A_CABLE_TDR_NEST_STEP:
+ ret = nl_get_cable_test_tdr_step(evattr, &first, &last, &step);
+ if (ret < 0)
+ return ret;
+
+ open_json_object(NULL);
+ print_float(PRINT_ANY, "first", "Step configuration: %.2f-",
+ (float)first / 100);
+ print_float(PRINT_ANY, "last", "%.2f meters ",
+ (float)last / 100);
+ print_float(PRINT_ANY, "step", "in %.2fm steps\n",
+ (float)step / 100);
+ close_json_object();
+ break;
+ }
+ return 0;
+}
+
+static void cable_test_tdr_ntf_nest(const struct nlattr *nest)
+{
+ struct nlattr *pos;
+ int ret;
+
+ mnl_attr_for_each_nested(pos, nest) {
+ ret = nl_cable_test_tdr_ntf_attr(pos);
+ if (ret < 0)
+ return;
+ }
+}
+
+/* Returns MNL_CB_STOP when the test is complete. Used when executing
+ * a test, but not suitable for monitor.
+ */
+int cable_test_tdr_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_MAX + 1] = {};
+ u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC;
+ struct cable_test_context *ctctx;
+ struct nl_context *nlctx = data;
+
+ DECLARE_ATTR_TB_INFO(tb);
+ bool silent;
+ int err_ret;
+ int ret;
+
+ ctctx = nlctx->cmd_private;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS])
+ status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]);
+
+ switch (status) {
+ case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED:
+ print_string(PRINT_FP, "status",
+ "Cable test TDR started for device %s.\n",
+ nlctx->devname);
+ break;
+ case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED:
+ print_string(PRINT_FP, "status",
+ "Cable test TDR completed for device %s.\n",
+ nlctx->devname);
+ break;
+ default:
+ break;
+ }
+
+ if (tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST])
+ cable_test_tdr_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST]);
+
+ if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) {
+ if (ctctx)
+ ctctx->breakout = true;
+ return MNL_CB_STOP;
+ }
+
+ return MNL_CB_OK;
+}
+
+/* Wrapper around cable_test_tdr_ntf_stop_cb() which does not return
+ * STOP, used for monitor
+ */
+int cable_test_tdr_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ int status = cable_test_tdr_ntf_stop_cb(nlhdr, data);
+
+ if (status == MNL_CB_STOP)
+ status = MNL_CB_OK;
+
+ return status;
+}
+
+static int nl_cable_test_tdr_results_cb(const struct nlmsghdr *nlhdr,
+ void *data)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+
+ if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_TDR_NTF)
+ return MNL_CB_OK;
+
+ cable_test_tdr_ntf_cb(nlhdr, data);
+
+ return MNL_CB_STOP;
+}
+
+/* Receive the broadcasted messages until we get the cable test
+ * results
+ */
+static int nl_cable_test_tdr_process_results(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ struct cable_test_context ctctx;
+ int err;
+
+ nlctx->is_monitor = true;
+ nlsk->port = 0;
+ nlsk->seq = 0;
+ nlctx->filter_devname = ctx->devname;
+
+ ctctx.breakout = false;
+ nlctx->cmd_private = &ctctx;
+
+ while (!ctctx.breakout) {
+ err = nlsock_process_reply(nlsk, nl_cable_test_tdr_results_cb,
+ nlctx);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
+static const struct param_parser tdr_params[] = {
+ {
+ .arg = "first",
+ .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST,
+ .group = ETHTOOL_A_CABLE_TEST_TDR_CFG,
+ .handler = nl_parse_direct_m2cm,
+ },
+ {
+ .arg = "last",
+ .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST,
+ .group = ETHTOOL_A_CABLE_TEST_TDR_CFG,
+ .handler = nl_parse_direct_m2cm,
+ },
+ {
+ .arg = "step",
+ .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP,
+ .group = ETHTOOL_A_CABLE_TEST_TDR_CFG,
+ .handler = nl_parse_direct_m2cm,
+ },
+ {
+ .arg = "pair",
+ .type = ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR,
+ .group = ETHTOOL_A_CABLE_TEST_TDR_CFG,
+ .handler = nl_parse_direct_u8,
+ },
+ {}
+};
+
+int nl_cable_test_tdr(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ uint32_t grpid = nlctx->ethnl_mongrp;
+ struct nl_msg_buff *msgbuff;
+ int ret;
+
+ nlctx->cmd = "--cable-test-tdr";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ msgbuff = &nlsk->msgbuff;
+
+ /* Join the multicast group so we can receive the results in a
+ * race free way.
+ */
+ if (!grpid) {
+ fprintf(stderr, "multicast group 'monitor' not found\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
+ &grpid, sizeof(grpid));
+ if (ret < 0)
+ return ret;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_CABLE_TEST_TDR_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, tdr_params, NULL, PARSER_GROUP_NEST, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ fprintf(stderr, "Cannot start cable test TDR\n");
+ else {
+ new_json_obj(ctx->json);
+
+ ret = nl_cable_test_tdr_process_results(ctx);
+
+ delete_json_obj();
+ }
+
+ return ret;
+}
diff --git a/netlink/channels.c b/netlink/channels.c
new file mode 100644
index 0000000..5cae227
--- /dev/null
+++ b/netlink/channels.c
@@ -0,0 +1,143 @@
+/*
+ * channels.c - netlink implementation of channel commands
+ *
+ * Implementation of "ethtool -l <dev>" and "ethtool -L <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+/* CHANNELS_GET */
+
+int channels_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_CHANNELS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_CHANNELS_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ putchar('\n');
+ printf("Channel parameters for %s:\n", nlctx->devname);
+ printf("Pre-set maximums:\n");
+ show_u32("rx-max", "RX:\t\t", tb[ETHTOOL_A_CHANNELS_RX_MAX]);
+ show_u32("tx-max", "TX:\t\t", tb[ETHTOOL_A_CHANNELS_TX_MAX]);
+ show_u32("other-max", "Other:\t\t", tb[ETHTOOL_A_CHANNELS_OTHER_MAX]);
+ show_u32("combined-max", "Combined:\t",
+ tb[ETHTOOL_A_CHANNELS_COMBINED_MAX]);
+ printf("Current hardware settings:\n");
+ show_u32("rx", "RX:\t\t", tb[ETHTOOL_A_CHANNELS_RX_COUNT]);
+ show_u32("tx", "TX:\t\t", tb[ETHTOOL_A_CHANNELS_TX_COUNT]);
+ show_u32("other", "Other:\t\t", tb[ETHTOOL_A_CHANNELS_OTHER_COUNT]);
+ show_u32("combined", "Combined:\t",
+ tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT]);
+
+ return MNL_CB_OK;
+}
+
+int nl_gchannels(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_CHANNELS_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_CHANNELS_GET,
+ ETHTOOL_A_CHANNELS_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, channels_reply_cb);
+}
+
+/* CHANNELS_SET */
+
+static const struct param_parser schannels_params[] = {
+ {
+ .arg = "rx",
+ .type = ETHTOOL_A_CHANNELS_RX_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx",
+ .type = ETHTOOL_A_CHANNELS_TX_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "other",
+ .type = ETHTOOL_A_CHANNELS_OTHER_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "combined",
+ .type = ETHTOOL_A_CHANNELS_COMBINED_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_schannels(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_CHANNELS_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-L";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_CHANNELS_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_CHANNELS_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, schannels_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 1;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 1;
+}
diff --git a/netlink/coalesce.c b/netlink/coalesce.c
new file mode 100644
index 0000000..bc34d3d
--- /dev/null
+++ b/netlink/coalesce.c
@@ -0,0 +1,335 @@
+/*
+ * coalesce.c - netlink implementation of coalescing commands
+ *
+ * Implementation of "ethtool -c <dev>" and "ethtool -C <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+/* COALESCE_GET */
+
+int coalesce_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_COALESCE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_COALESCE_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ open_json_object(NULL);
+
+ if (silent)
+ show_cr();
+ print_string(PRINT_ANY, "ifname", "Coalesce parameters for %s:\n",
+ nlctx->devname);
+ show_bool("rx", "Adaptive RX: %s ",
+ tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX]);
+ show_bool("tx", "TX: %s\n", tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX]);
+ show_u32("stats-block-usecs", "stats-block-usecs:\t",
+ tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS]);
+ show_u32("sample-interval", "sample-interval:\t",
+ tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL]);
+ show_u32("pkt-rate-low", "pkt-rate-low:\t\t",
+ tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW]);
+ show_u32("pkt-rate-high", "pkt-rate-high:\t\t",
+ tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH]);
+ show_cr();
+ show_u32("rx-usecs", "rx-usecs:\t", tb[ETHTOOL_A_COALESCE_RX_USECS]);
+ show_u32("rx-frames", "rx-frames:\t",
+ tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES]);
+ show_u32("rx-usecs-irq", "rx-usecs-irq:\t",
+ tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ]);
+ show_u32("rx-frames-irq", "rx-frames-irq:\t",
+ tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ]);
+ show_cr();
+ show_u32("tx-usecs", "tx-usecs:\t", tb[ETHTOOL_A_COALESCE_TX_USECS]);
+ show_u32("tx-frames", "tx-frames:\t",
+ tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES]);
+ show_u32("tx-usecs-irq", "tx-usecs-irq:\t",
+ tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ]);
+ show_u32("tx-frames-irq", "tx-frames-irq:\t",
+ tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ]);
+ show_cr();
+ show_u32("rx-usecs-low", "rx-usecs-low:\t",
+ tb[ETHTOOL_A_COALESCE_RX_USECS_LOW]);
+ show_u32("rx-frame-low", "rx-frame-low:\t",
+ tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW]);
+ show_u32("tx-usecs-low", "tx-usecs-low:\t",
+ tb[ETHTOOL_A_COALESCE_TX_USECS_LOW]);
+ show_u32("tx-frame-low", "tx-frame-low:\t",
+ tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW]);
+ show_cr();
+ show_u32("rx-usecs-high", "rx-usecs-high:\t",
+ tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH]);
+ show_u32("rx-frame-high", "rx-frame-high:\t",
+ tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH]);
+ show_u32("tx-usecs-high", "tx-usecs-high:\t",
+ tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH]);
+ show_u32("tx-frame-high", "tx-frame-high:\t",
+ tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH]);
+ show_cr();
+ show_bool("rx", "CQE mode RX: %s ",
+ tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX]);
+ show_bool("tx", "TX: %s\n", tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX]);
+ show_cr();
+ show_u32("tx-aggr-max-bytes", "tx-aggr-max-bytes:\t",
+ tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES]);
+ show_u32("tx-aggr-max-frames", "tx-aggr-max-frames:\t",
+ tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES]);
+ show_u32("tx-aggr-time-usecs", "tx-aggr-time-usecs\t",
+ tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS]);
+ show_cr();
+
+ close_json_object();
+
+ return MNL_CB_OK;
+}
+
+int nl_gcoalesce(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_COALESCE_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_COALESCE_GET,
+ ETHTOOL_A_COALESCE_HEADER, 0);
+ if (ret < 0)
+ return ret;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, coalesce_reply_cb);
+ delete_json_obj();
+ return ret;
+}
+
+/* COALESCE_SET */
+
+static const struct param_parser scoalesce_params[] = {
+ {
+ .arg = "adaptive-rx",
+ .type = ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "adaptive-tx",
+ .type = ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "sample-interval",
+ .type = ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "stats-block-usecs",
+ .type = ETHTOOL_A_COALESCE_STATS_BLOCK_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "pkt-rate-low",
+ .type = ETHTOOL_A_COALESCE_PKT_RATE_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "pkt-rate-high",
+ .type = ETHTOOL_A_COALESCE_PKT_RATE_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs",
+ .type = ETHTOOL_A_COALESCE_RX_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames",
+ .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs-irq",
+ .type = ETHTOOL_A_COALESCE_RX_USECS_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames-irq",
+ .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs",
+ .type = ETHTOOL_A_COALESCE_TX_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames",
+ .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs-irq",
+ .type = ETHTOOL_A_COALESCE_TX_USECS_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames-irq",
+ .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs-low",
+ .type = ETHTOOL_A_COALESCE_RX_USECS_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames-low",
+ .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs-low",
+ .type = ETHTOOL_A_COALESCE_TX_USECS_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames-low",
+ .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs-high",
+ .type = ETHTOOL_A_COALESCE_RX_USECS_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames-high",
+ .type = ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs-high",
+ .type = ETHTOOL_A_COALESCE_TX_USECS_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames-high",
+ .type = ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "cqe-mode-rx",
+ .type = ETHTOOL_A_COALESCE_USE_CQE_MODE_RX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "cqe-mode-tx",
+ .type = ETHTOOL_A_COALESCE_USE_CQE_MODE_TX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-aggr-max-bytes",
+ .type = ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-aggr-max-frames",
+ .type = ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-aggr-time-usecs",
+ .type = ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_scoalesce(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_COALESCE_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-C";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_COALESCE_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_COALESCE_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, scoalesce_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 1;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 1;
+}
diff --git a/netlink/desc-ethtool.c b/netlink/desc-ethtool.c
new file mode 100644
index 0000000..661de26
--- /dev/null
+++ b/netlink/desc-ethtool.c
@@ -0,0 +1,595 @@
+/*
+ * desc-ethtool.c - ethtool netlink format descriptions
+ *
+ * Descriptions of ethtool netlink messages and attributes for pretty print.
+ */
+
+#include "../internal.h"
+#include <linux/ethtool_netlink.h>
+
+#include "prettymsg.h"
+
+static const struct pretty_nla_desc __header_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_HEADER_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_HEADER_DEV_INDEX),
+ NLATTR_DESC_STRING(ETHTOOL_A_HEADER_DEV_NAME),
+ NLATTR_DESC_X32(ETHTOOL_A_HEADER_FLAGS),
+};
+
+static const struct pretty_nla_desc __bitset_bit_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_BITSET_BIT_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_BITSET_BIT_INDEX),
+ NLATTR_DESC_STRING(ETHTOOL_A_BITSET_BIT_NAME),
+ NLATTR_DESC_FLAG(ETHTOOL_A_BITSET_BIT_VALUE),
+};
+
+static const struct pretty_nla_desc __bitset_bits_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_BITSET_BITS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_BITSET_BITS_BIT, bitset_bit),
+};
+
+static const struct pretty_nla_desc __bitset_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_BITSET_UNSPEC),
+ NLATTR_DESC_FLAG(ETHTOOL_A_BITSET_NOMASK),
+ NLATTR_DESC_U32(ETHTOOL_A_BITSET_SIZE),
+ NLATTR_DESC_NESTED(ETHTOOL_A_BITSET_BITS, bitset_bits),
+ NLATTR_DESC_BINARY(ETHTOOL_A_BITSET_VALUE),
+ NLATTR_DESC_BINARY(ETHTOOL_A_BITSET_MASK),
+};
+
+static const struct pretty_nla_desc __string_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_STRING_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_STRING_INDEX),
+ NLATTR_DESC_STRING(ETHTOOL_A_STRING_VALUE),
+};
+
+static const struct pretty_nla_desc __strings_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_STRINGS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STRINGS_STRING, string),
+};
+
+static const struct pretty_nla_desc __stringset_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_STRINGSET_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_STRINGSET_ID),
+ NLATTR_DESC_U32(ETHTOOL_A_STRINGSET_COUNT),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STRINGSET_STRINGS, strings),
+};
+
+static const struct pretty_nla_desc __stringsets_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_STRINGSETS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STRINGSETS_STRINGSET, stringset),
+};
+
+static const struct pretty_nla_desc __strset_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_STRSET_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STRSET_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STRSET_STRINGSETS, stringsets),
+ NLATTR_DESC_FLAG(ETHTOOL_A_STRSET_COUNTS_ONLY),
+};
+
+static const struct pretty_nla_desc __linkinfo_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_LINKINFO_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_LINKINFO_HEADER, header),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_PORT),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_PHYADDR),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_TP_MDIX),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_TP_MDIX_CTRL),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKINFO_TRANSCEIVER),
+};
+
+static const char *__linkmodes_rate_matching_names[] = {
+ [RATE_MATCH_NONE] = "RATE_MATCH_NONE",
+ [RATE_MATCH_PAUSE] = "RATE_MATCH_PAUSE",
+ [RATE_MATCH_CRS] = "RATE_MATCH_CRS",
+ [RATE_MATCH_OPEN_LOOP] = "RATE_MATCH_OPEN_LOOP",
+};
+
+static const struct pretty_nla_desc __linkmodes_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_LINKMODES_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_LINKMODES_HEADER, header),
+ NLATTR_DESC_BOOL(ETHTOOL_A_LINKMODES_AUTONEG),
+ NLATTR_DESC_NESTED(ETHTOOL_A_LINKMODES_OURS, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_LINKMODES_PEER, bitset),
+ NLATTR_DESC_U32(ETHTOOL_A_LINKMODES_SPEED),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKMODES_DUPLEX),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE),
+ NLATTR_DESC_U32(ETHTOOL_A_LINKMODES_LANES),
+ NLATTR_DESC_U8_ENUM(ETHTOOL_A_LINKMODES_RATE_MATCHING,
+ linkmodes_rate_matching),
+};
+
+static const struct pretty_nla_desc __linkstate_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_LINKSTATE_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_LINKSTATE_HEADER, header),
+ NLATTR_DESC_BOOL(ETHTOOL_A_LINKSTATE_LINK),
+ NLATTR_DESC_U32(ETHTOOL_A_LINKSTATE_SQI),
+ NLATTR_DESC_U32(ETHTOOL_A_LINKSTATE_SQI_MAX),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKSTATE_EXT_STATE),
+ NLATTR_DESC_U8(ETHTOOL_A_LINKSTATE_EXT_SUBSTATE),
+};
+
+static const struct pretty_nla_desc __debug_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_DEBUG_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_DEBUG_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_DEBUG_MSGMASK, bitset),
+};
+
+static const struct pretty_nla_desc __wol_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_WOL_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_WOL_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_WOL_MODES, bitset),
+ NLATTR_DESC_BINARY(ETHTOOL_A_WOL_SOPASS),
+};
+
+static const struct pretty_nla_desc __features_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_FEATURES_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_HW, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_WANTED, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_ACTIVE, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEATURES_NOCHANGE, bitset),
+};
+
+static const struct pretty_nla_desc __privflags_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_PRIVFLAGS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PRIVFLAGS_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PRIVFLAGS_FLAGS, bitset),
+};
+
+static const char *__rings_tcp_data_split_names[] = {
+ [ETHTOOL_TCP_DATA_SPLIT_UNKNOWN] = "ETHTOOL_TCP_DATA_SPLIT_UNKNOWN",
+ [ETHTOOL_TCP_DATA_SPLIT_DISABLED] = "ETHTOOL_TCP_DATA_SPLIT_DISABLED",
+ [ETHTOOL_TCP_DATA_SPLIT_ENABLED] = "ETHTOOL_TCP_DATA_SPLIT_ENABLED",
+};
+
+static const struct pretty_nla_desc __rings_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_RINGS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_RINGS_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_MINI_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_JUMBO_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_MINI),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_JUMBO),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_RX_BUF_LEN),
+ NLATTR_DESC_U8_ENUM(ETHTOOL_A_RINGS_TCP_DATA_SPLIT, rings_tcp_data_split),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_CQE_SIZE),
+ NLATTR_DESC_BOOL(ETHTOOL_A_RINGS_TX_PUSH),
+ NLATTR_DESC_BOOL(ETHTOOL_A_RINGS_RX_PUSH),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN),
+ NLATTR_DESC_U32(ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX),
+};
+
+static const struct pretty_nla_desc __channels_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CHANNELS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CHANNELS_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_RX_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_TX_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_OTHER_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_COMBINED_MAX),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_RX_COUNT),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_TX_COUNT),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_OTHER_COUNT),
+ NLATTR_DESC_U32(ETHTOOL_A_CHANNELS_COMBINED_COUNT),
+};
+
+static const struct pretty_nla_desc __coalesce_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_COALESCE_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_COALESCE_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS_IRQ),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS_IRQ),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_STATS_BLOCK_USECS),
+ NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX),
+ NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_PKT_RATE_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_PKT_RATE_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_USECS_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_USECS_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL),
+ NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_CQE_MODE_TX),
+ NLATTR_DESC_BOOL(ETHTOOL_A_COALESCE_USE_CQE_MODE_RX),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES),
+ NLATTR_DESC_U32(ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS),
+};
+
+static const struct pretty_nla_desc __pause_stats_desc[] = {
+ NLATTR_DESC_BINARY(ETHTOOL_A_PAUSE_STAT_PAD),
+ NLATTR_DESC_U64(ETHTOOL_A_PAUSE_STAT_TX_FRAMES),
+ NLATTR_DESC_U64(ETHTOOL_A_PAUSE_STAT_RX_FRAMES),
+};
+
+static const struct pretty_nla_desc __pause_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_PAUSE_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PAUSE_HEADER, header),
+ NLATTR_DESC_BOOL(ETHTOOL_A_PAUSE_AUTONEG),
+ NLATTR_DESC_BOOL(ETHTOOL_A_PAUSE_RX),
+ NLATTR_DESC_BOOL(ETHTOOL_A_PAUSE_TX),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PAUSE_STATS, pause_stats),
+};
+
+static const struct pretty_nla_desc __eee_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_EEE_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_EEE_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_EEE_MODES_OURS, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_EEE_MODES_PEER, bitset),
+ NLATTR_DESC_BOOL(ETHTOOL_A_EEE_ACTIVE),
+ NLATTR_DESC_BOOL(ETHTOOL_A_EEE_ENABLED),
+ NLATTR_DESC_BOOL(ETHTOOL_A_EEE_TX_LPI_ENABLED),
+ NLATTR_DESC_U32(ETHTOOL_A_EEE_TX_LPI_TIMER),
+};
+
+static const struct pretty_nla_desc __tsinfo_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TSINFO_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_TIMESTAMPING, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_TX_TYPES, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TSINFO_RX_FILTERS, bitset),
+ NLATTR_DESC_U32(ETHTOOL_A_TSINFO_PHC_INDEX),
+};
+
+static const struct pretty_nla_desc __cable_test_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_HEADER, header),
+};
+
+static const struct pretty_nla_desc __cable_test_result_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_RESULT_UNSPEC),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_RESULT_PAIR),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_RESULT_CODE),
+};
+
+static const struct pretty_nla_desc __cable_test_flength_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_FAULT_LENGTH_UNSPEC),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_FAULT_LENGTH_CM),
+};
+
+static const struct pretty_nla_desc __cable_nest_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_NEST_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_NEST_RESULT, cable_test_result),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_NEST_FAULT_LENGTH,
+ cable_test_flength),
+};
+
+static const struct pretty_nla_desc __cable_test_ntf_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_NTF_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_NTF_HEADER, header),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_TEST_NTF_STATUS),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_NTF_NEST, cable_nest),
+};
+
+static const struct pretty_nla_desc __cable_test_tdr_cfg_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_TDR_CFG_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR),
+};
+
+static const struct pretty_nla_desc __cable_test_tdr_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_TDR_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_CFG, cable_test_tdr_cfg),
+};
+
+static const struct pretty_nla_desc __cable_step_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_STEP_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_STEP_LAST_DISTANCE),
+ NLATTR_DESC_U32(ETHTOOL_A_CABLE_STEP_STEP_DISTANCE),
+};
+
+static const struct pretty_nla_desc __cable_amplitude_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_AMPLITUDE_UNSPEC),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_AMPLITUDE_PAIR),
+ NLATTR_DESC_S16(ETHTOOL_A_CABLE_AMPLITUDE_mV),
+};
+
+static const struct pretty_nla_desc __cable_pulse_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_PULSE_UNSPEC),
+ NLATTR_DESC_S16(ETHTOOL_A_CABLE_PULSE_mV),
+};
+
+static const struct pretty_nla_desc __cable_test_tdr_nest_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TDR_NEST_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TDR_NEST_STEP, cable_step),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE, cable_amplitude),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TDR_NEST_PULSE, cable_pulse),
+};
+
+static const struct pretty_nla_desc __cable_test_tdr_ntf_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_CABLE_TEST_TDR_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_NTF_HEADER, header),
+ NLATTR_DESC_U8(ETHTOOL_A_CABLE_TEST_TDR_NTF_STATUS),
+ NLATTR_DESC_NESTED(ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST,
+ cable_test_tdr_nest),
+};
+
+const struct pretty_nla_desc __tunnel_udp_entry_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_UDP_ENTRY_UNSPEC),
+ NLATTR_DESC_U16(ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT),
+ NLATTR_DESC_U32(ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE),
+};
+
+const struct pretty_nla_desc __tunnel_udp_table_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_UDP_TABLE_UNSPEC),
+ NLATTR_DESC_U32(ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY, tunnel_udp_entry),
+};
+
+const struct pretty_nla_desc __tunnel_udp_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_UDP_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_UDP_TABLE, tunnel_udp_table),
+};
+
+const struct pretty_nla_desc __tunnel_info_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_TUNNEL_INFO_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_INFO_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_TUNNEL_INFO_UDP_PORTS, tunnel_udp),
+};
+
+const struct pretty_nla_desc __fec_stats_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_FEC_STAT_UNSPEC),
+ NLATTR_DESC_BINARY(ETHTOOL_A_FEC_STAT_PAD),
+ NLATTR_DESC_U64(ETHTOOL_A_FEC_STAT_CORRECTED),
+ NLATTR_DESC_U64(ETHTOOL_A_FEC_STAT_UNCORR),
+ NLATTR_DESC_U64(ETHTOOL_A_FEC_STAT_CORR_BITS),
+};
+
+static const struct pretty_nla_desc __fec_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_FEC_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEC_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEC_MODES, bitset),
+ NLATTR_DESC_BOOL(ETHTOOL_A_FEC_AUTO),
+ NLATTR_DESC_U32(ETHTOOL_A_FEC_ACTIVE),
+ NLATTR_DESC_NESTED(ETHTOOL_A_FEC_STATS, fec_stats),
+};
+
+const struct pretty_nla_desc __module_eeprom_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_MODULE_EEPROM_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_MODULE_EEPROM_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_MODULE_EEPROM_OFFSET),
+ NLATTR_DESC_U32(ETHTOOL_A_MODULE_EEPROM_LENGTH),
+ NLATTR_DESC_U8(ETHTOOL_A_MODULE_EEPROM_PAGE),
+ NLATTR_DESC_U8(ETHTOOL_A_MODULE_EEPROM_BANK),
+ NLATTR_DESC_U8(ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS),
+ NLATTR_DESC_BINARY(ETHTOOL_A_MODULE_EEPROM_DATA)
+};
+
+static const struct pretty_nla_desc __stats_grp_stat_desc[] = {
+ NLATTR_DESC_U64(0), NLATTR_DESC_U64(1), NLATTR_DESC_U64(2),
+ NLATTR_DESC_U64(3), NLATTR_DESC_U64(4), NLATTR_DESC_U64(5),
+ NLATTR_DESC_U64(6), NLATTR_DESC_U64(7), NLATTR_DESC_U64(8),
+ NLATTR_DESC_U64(9), NLATTR_DESC_U64(10), NLATTR_DESC_U64(11),
+ NLATTR_DESC_U64(12), NLATTR_DESC_U64(13), NLATTR_DESC_U64(14),
+ NLATTR_DESC_U64(15), NLATTR_DESC_U64(16), NLATTR_DESC_U64(17),
+ NLATTR_DESC_U64(18), NLATTR_DESC_U64(19), NLATTR_DESC_U64(20),
+ NLATTR_DESC_U64(21), NLATTR_DESC_U64(22), NLATTR_DESC_U64(23),
+ NLATTR_DESC_U64(24), NLATTR_DESC_U64(25), NLATTR_DESC_U64(26),
+ NLATTR_DESC_U64(27), NLATTR_DESC_U64(28), NLATTR_DESC_U64(29),
+};
+
+static const struct pretty_nla_desc __stats_grp_hist_desc[] = {
+ NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_HIST_BKT_LOW),
+ NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_HIST_BKT_HI),
+ NLATTR_DESC_U64(ETHTOOL_A_STATS_GRP_HIST_VAL),
+};
+
+static const struct pretty_nla_desc __stats_grp_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_STATS_GRP_UNSPEC),
+ NLATTR_DESC_INVALID(ETHTOOL_A_STATS_GRP_PAD),
+ NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_ID),
+ NLATTR_DESC_U32(ETHTOOL_A_STATS_GRP_SS_ID),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GRP_STAT, stats_grp_stat),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GRP_HIST_RX, stats_grp_hist),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GRP_HIST_TX, stats_grp_hist),
+};
+
+static const struct pretty_nla_desc __stats_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_STATS_UNSPEC),
+ NLATTR_DESC_INVALID(ETHTOOL_A_STATS_PAD),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STATS_HEADER, header),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GROUPS, bitset),
+ NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GRP, stats_grp),
+};
+
+static const struct pretty_nla_desc __phc_vclocks_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_PHC_VCLOCKS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PHC_VCLOCKS_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_PHC_VCLOCKS_NUM),
+ NLATTR_DESC_BINARY(ETHTOOL_A_PHC_VCLOCKS_INDEX),
+};
+
+static const struct pretty_nla_desc __module_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_MODULE_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_MODULE_HEADER, header),
+ NLATTR_DESC_U8(ETHTOOL_A_MODULE_POWER_MODE_POLICY),
+ NLATTR_DESC_U8(ETHTOOL_A_MODULE_POWER_MODE),
+};
+
+static const char *__pse_admin_state_names[] = {
+ [ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN] = "ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN",
+ [ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED] = "ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED",
+ [ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED] = "ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED",
+};
+
+static const char *__pse_pw_d_status_names[] = {
+ [ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN] = "ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN",
+ [ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED] = "ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED",
+ [ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING] = "ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING",
+ [ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING] = "ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING",
+ [ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP] = "ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP",
+ [ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE] = "ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE",
+ [ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR] = "ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR",
+};
+
+static const struct pretty_nla_desc __pse_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_PSE_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PSE_HEADER, header),
+ NLATTR_DESC_U32_ENUM(ETHTOOL_A_PODL_PSE_ADMIN_STATE, pse_admin_state),
+ NLATTR_DESC_U32_ENUM(ETHTOOL_A_PODL_PSE_ADMIN_CONTROL, pse_admin_state),
+ NLATTR_DESC_U32_ENUM(ETHTOOL_A_PODL_PSE_PW_D_STATUS, pse_pw_d_status),
+};
+
+static const struct pretty_nla_desc __rss_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_RSS_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_RSS_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_RSS_CONTEXT),
+ NLATTR_DESC_U32(ETHTOOL_A_RSS_HFUNC),
+ NLATTR_DESC_BINARY(ETHTOOL_A_RSS_INDIR),
+ NLATTR_DESC_BINARY(ETHTOOL_A_RSS_HKEY),
+};
+
+static const struct pretty_nla_desc __plca_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_PLCA_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PLCA_HEADER, header),
+ NLATTR_DESC_U16(ETHTOOL_A_PLCA_VERSION),
+ NLATTR_DESC_U8(ETHTOOL_A_PLCA_ENABLED),
+ NLATTR_DESC_U8(ETHTOOL_A_PLCA_STATUS),
+ NLATTR_DESC_U32(ETHTOOL_A_PLCA_NODE_CNT),
+ NLATTR_DESC_U32(ETHTOOL_A_PLCA_NODE_ID),
+ NLATTR_DESC_U32(ETHTOOL_A_PLCA_TO_TMR),
+ NLATTR_DESC_U32(ETHTOOL_A_PLCA_BURST_CNT),
+ NLATTR_DESC_U32(ETHTOOL_A_PLCA_BURST_TMR),
+};
+
+static const struct pretty_nla_desc __mm_stat_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_MM_STAT_UNSPEC),
+ NLATTR_DESC_BINARY(ETHTOOL_A_MM_STAT_PAD),
+ NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS),
+ NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_SMD_ERRORS),
+ NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_REASSEMBLY_OK),
+ NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_RX_FRAG_COUNT),
+ NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_TX_FRAG_COUNT),
+ NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_HOLD_COUNT),
+};
+
+static const struct pretty_nla_desc __mm_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_MM_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_MM_HEADER, header),
+ NLATTR_DESC_U8(ETHTOOL_A_MM_PMAC_ENABLED),
+ NLATTR_DESC_U8(ETHTOOL_A_MM_TX_ENABLED),
+ NLATTR_DESC_U8(ETHTOOL_A_MM_TX_ACTIVE),
+ NLATTR_DESC_U32(ETHTOOL_A_MM_TX_MIN_FRAG_SIZE),
+ NLATTR_DESC_U32(ETHTOOL_A_MM_RX_MIN_FRAG_SIZE),
+ NLATTR_DESC_U8(ETHTOOL_A_MM_VERIFY_ENABLED),
+ NLATTR_DESC_U8(ETHTOOL_A_MM_VERIFY_STATUS),
+ NLATTR_DESC_U32(ETHTOOL_A_MM_VERIFY_TIME),
+ NLATTR_DESC_U32(ETHTOOL_A_MM_MAX_VERIFY_TIME),
+ NLATTR_DESC_NESTED(ETHTOOL_A_MM_STATS, mm_stat),
+};
+
+const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
+ NLMSG_DESC_INVALID(ETHTOOL_MSG_USER_NONE),
+ NLMSG_DESC(ETHTOOL_MSG_STRSET_GET, strset),
+ NLMSG_DESC(ETHTOOL_MSG_LINKINFO_GET, linkinfo),
+ NLMSG_DESC(ETHTOOL_MSG_LINKINFO_SET, linkinfo),
+ NLMSG_DESC(ETHTOOL_MSG_LINKMODES_GET, linkmodes),
+ NLMSG_DESC(ETHTOOL_MSG_LINKMODES_SET, linkmodes),
+ NLMSG_DESC(ETHTOOL_MSG_LINKSTATE_GET, linkstate),
+ NLMSG_DESC(ETHTOOL_MSG_DEBUG_GET, debug),
+ NLMSG_DESC(ETHTOOL_MSG_DEBUG_SET, debug),
+ NLMSG_DESC(ETHTOOL_MSG_WOL_GET, wol),
+ NLMSG_DESC(ETHTOOL_MSG_WOL_SET, wol),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_GET, features),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_SET, features),
+ NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_GET, privflags),
+ NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_SET, privflags),
+ NLMSG_DESC(ETHTOOL_MSG_RINGS_GET, rings),
+ NLMSG_DESC(ETHTOOL_MSG_RINGS_SET, rings),
+ NLMSG_DESC(ETHTOOL_MSG_CHANNELS_GET, channels),
+ NLMSG_DESC(ETHTOOL_MSG_CHANNELS_SET, channels),
+ NLMSG_DESC(ETHTOOL_MSG_COALESCE_GET, coalesce),
+ NLMSG_DESC(ETHTOOL_MSG_COALESCE_SET, coalesce),
+ NLMSG_DESC(ETHTOOL_MSG_PAUSE_GET, pause),
+ NLMSG_DESC(ETHTOOL_MSG_PAUSE_SET, pause),
+ NLMSG_DESC(ETHTOOL_MSG_EEE_GET, eee),
+ NLMSG_DESC(ETHTOOL_MSG_EEE_SET, eee),
+ NLMSG_DESC(ETHTOOL_MSG_TSINFO_GET, tsinfo),
+ NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_ACT, cable_test),
+ NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_TDR_ACT, cable_test_tdr),
+ NLMSG_DESC(ETHTOOL_MSG_TUNNEL_INFO_GET, tunnel_info),
+ NLMSG_DESC(ETHTOOL_MSG_FEC_GET, fec),
+ NLMSG_DESC(ETHTOOL_MSG_FEC_SET, fec),
+ NLMSG_DESC(ETHTOOL_MSG_MODULE_EEPROM_GET, module_eeprom),
+ NLMSG_DESC(ETHTOOL_MSG_STATS_GET, stats),
+ NLMSG_DESC(ETHTOOL_MSG_PHC_VCLOCKS_GET, phc_vclocks),
+ NLMSG_DESC(ETHTOOL_MSG_MODULE_GET, module),
+ NLMSG_DESC(ETHTOOL_MSG_MODULE_SET, module),
+ NLMSG_DESC(ETHTOOL_MSG_PSE_GET, pse),
+ NLMSG_DESC(ETHTOOL_MSG_PSE_SET, pse),
+ NLMSG_DESC(ETHTOOL_MSG_RSS_GET, rss),
+ NLMSG_DESC(ETHTOOL_MSG_PLCA_GET_CFG, plca),
+ NLMSG_DESC(ETHTOOL_MSG_PLCA_SET_CFG, plca),
+ NLMSG_DESC(ETHTOOL_MSG_PLCA_GET_STATUS, plca),
+ NLMSG_DESC(ETHTOOL_MSG_MM_GET, mm),
+ NLMSG_DESC(ETHTOOL_MSG_MM_SET, mm),
+};
+
+const unsigned int ethnl_umsg_n_desc = ARRAY_SIZE(ethnl_umsg_desc);
+
+const struct pretty_nlmsg_desc ethnl_kmsg_desc[] = {
+ NLMSG_DESC_INVALID(ETHTOOL_MSG_KERNEL_NONE),
+ NLMSG_DESC(ETHTOOL_MSG_STRSET_GET_REPLY, strset),
+ NLMSG_DESC(ETHTOOL_MSG_LINKINFO_GET_REPLY, linkinfo),
+ NLMSG_DESC(ETHTOOL_MSG_LINKINFO_NTF, linkinfo),
+ NLMSG_DESC(ETHTOOL_MSG_LINKMODES_GET_REPLY, linkmodes),
+ NLMSG_DESC(ETHTOOL_MSG_LINKMODES_NTF, linkmodes),
+ NLMSG_DESC(ETHTOOL_MSG_LINKSTATE_GET_REPLY, linkstate),
+ NLMSG_DESC(ETHTOOL_MSG_DEBUG_GET_REPLY, debug),
+ NLMSG_DESC(ETHTOOL_MSG_DEBUG_NTF, debug),
+ NLMSG_DESC(ETHTOOL_MSG_WOL_GET_REPLY, wol),
+ NLMSG_DESC(ETHTOOL_MSG_WOL_NTF, wol),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_GET_REPLY, features),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_SET_REPLY, features),
+ NLMSG_DESC(ETHTOOL_MSG_FEATURES_NTF, features),
+ NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, privflags),
+ NLMSG_DESC(ETHTOOL_MSG_PRIVFLAGS_NTF, privflags),
+ NLMSG_DESC(ETHTOOL_MSG_RINGS_GET_REPLY, rings),
+ NLMSG_DESC(ETHTOOL_MSG_RINGS_NTF, rings),
+ NLMSG_DESC(ETHTOOL_MSG_CHANNELS_GET_REPLY, channels),
+ NLMSG_DESC(ETHTOOL_MSG_CHANNELS_NTF, channels),
+ NLMSG_DESC(ETHTOOL_MSG_COALESCE_GET_REPLY, coalesce),
+ NLMSG_DESC(ETHTOOL_MSG_COALESCE_NTF, coalesce),
+ NLMSG_DESC(ETHTOOL_MSG_PAUSE_GET_REPLY, pause),
+ NLMSG_DESC(ETHTOOL_MSG_PAUSE_NTF, pause),
+ NLMSG_DESC(ETHTOOL_MSG_EEE_GET_REPLY, eee),
+ NLMSG_DESC(ETHTOOL_MSG_EEE_NTF, eee),
+ NLMSG_DESC(ETHTOOL_MSG_TSINFO_GET_REPLY, tsinfo),
+ NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_NTF, cable_test_ntf),
+ NLMSG_DESC(ETHTOOL_MSG_CABLE_TEST_TDR_NTF, cable_test_tdr_ntf),
+ NLMSG_DESC(ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY, tunnel_info),
+ NLMSG_DESC(ETHTOOL_MSG_FEC_GET_REPLY, fec),
+ NLMSG_DESC(ETHTOOL_MSG_FEC_NTF, fec),
+ NLMSG_DESC(ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY, module_eeprom),
+ NLMSG_DESC(ETHTOOL_MSG_STATS_GET_REPLY, stats),
+ NLMSG_DESC(ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY, phc_vclocks),
+ NLMSG_DESC(ETHTOOL_MSG_MODULE_GET_REPLY, module),
+ NLMSG_DESC(ETHTOOL_MSG_MODULE_NTF, module),
+ NLMSG_DESC(ETHTOOL_MSG_PSE_GET_REPLY, pse),
+ NLMSG_DESC(ETHTOOL_MSG_RSS_GET_REPLY, rss),
+ NLMSG_DESC(ETHTOOL_MSG_PLCA_GET_CFG_REPLY, plca),
+ NLMSG_DESC(ETHTOOL_MSG_PLCA_GET_STATUS_REPLY, plca),
+ NLMSG_DESC(ETHTOOL_MSG_PLCA_NTF, plca),
+ NLMSG_DESC(ETHTOOL_MSG_MM_GET_REPLY, mm),
+ NLMSG_DESC(ETHTOOL_MSG_MM_NTF, mm),
+};
+
+const unsigned int ethnl_kmsg_n_desc = ARRAY_SIZE(ethnl_kmsg_desc);
diff --git a/netlink/desc-genlctrl.c b/netlink/desc-genlctrl.c
new file mode 100644
index 0000000..43b41ab
--- /dev/null
+++ b/netlink/desc-genlctrl.c
@@ -0,0 +1,113 @@
+/*
+ * desc-genlctrl.c - genetlink control format descriptions
+ *
+ * Descriptions of genetlink control messages and attributes for pretty print.
+ */
+
+#include <linux/genetlink.h>
+
+#include "../internal.h"
+#include "prettymsg.h"
+
+static const struct pretty_nla_desc __attrop_desc[] = {
+ NLATTR_DESC_INVALID(CTRL_ATTR_OP_UNSPEC),
+ NLATTR_DESC_U32(CTRL_ATTR_OP_ID),
+ NLATTR_DESC_X32(CTRL_ATTR_OP_FLAGS),
+};
+
+static const struct pretty_nla_desc __attrops_desc[] = {
+ NLATTR_DESC_NESTED(0, attrop),
+};
+
+static const struct pretty_nla_desc __mcgrp_desc[] = {
+ NLATTR_DESC_INVALID(CTRL_ATTR_MCAST_GRP_UNSPEC),
+ NLATTR_DESC_STRING(CTRL_ATTR_MCAST_GRP_NAME),
+ NLATTR_DESC_U32(CTRL_ATTR_MCAST_GRP_ID),
+};
+
+static const struct pretty_nla_desc __mcgrps_desc[] = {
+ NLATTR_DESC_NESTED(0, mcgrp),
+};
+
+static const char *__policy_attr_type_names[] = {
+ [NL_ATTR_TYPE_INVALID] = "NL_ATTR_TYPE_INVALID",
+ [NL_ATTR_TYPE_FLAG] = "NL_ATTR_TYPE_FLAG",
+ [NL_ATTR_TYPE_U8] = "NL_ATTR_TYPE_U8",
+ [NL_ATTR_TYPE_U16] = "NL_ATTR_TYPE_U16",
+ [NL_ATTR_TYPE_U32] = "NL_ATTR_TYPE_U32",
+ [NL_ATTR_TYPE_U64] = "NL_ATTR_TYPE_U64",
+ [NL_ATTR_TYPE_S8] = "NL_ATTR_TYPE_S8",
+ [NL_ATTR_TYPE_S16] = "NL_ATTR_TYPE_S16",
+ [NL_ATTR_TYPE_S32] = "NL_ATTR_TYPE_S32",
+ [NL_ATTR_TYPE_S64] = "NL_ATTR_TYPE_S64",
+ [NL_ATTR_TYPE_BINARY] = "NL_ATTR_TYPE_BINARY",
+ [NL_ATTR_TYPE_STRING] = "NL_ATTR_TYPE_STRING",
+ [NL_ATTR_TYPE_NUL_STRING] = "NL_ATTR_TYPE_NUL_STRING",
+ [NL_ATTR_TYPE_NESTED] = "NL_ATTR_TYPE_NESTED",
+ [NL_ATTR_TYPE_NESTED_ARRAY] = "NL_ATTR_TYPE_NESTED_ARRAY",
+ [NL_ATTR_TYPE_BITFIELD32] = "NL_ATTR_TYPE_BITFIELD32",
+};
+
+static const struct pretty_nla_desc __policy_attr_desc[] = {
+ NLATTR_DESC_INVALID(NL_POLICY_TYPE_ATTR_UNSPEC),
+ NLATTR_DESC_U32_ENUM(NL_POLICY_TYPE_ATTR_TYPE, policy_attr_type),
+ NLATTR_DESC_S64(NL_POLICY_TYPE_ATTR_MIN_VALUE_S),
+ NLATTR_DESC_S64(NL_POLICY_TYPE_ATTR_MAX_VALUE_S),
+ NLATTR_DESC_U64(NL_POLICY_TYPE_ATTR_MIN_VALUE_U),
+ NLATTR_DESC_U64(NL_POLICY_TYPE_ATTR_MAX_VALUE_U),
+ NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_MIN_LENGTH),
+ NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_MAX_LENGTH),
+ NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_POLICY_IDX),
+ NLATTR_DESC_U32(NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE),
+ NLATTR_DESC_X32(NL_POLICY_TYPE_ATTR_BITFIELD32_MASK),
+ NLATTR_DESC_X64(NL_POLICY_TYPE_ATTR_PAD),
+ NLATTR_DESC_BINARY(NL_POLICY_TYPE_ATTR_MASK),
+};
+
+static const struct pretty_nla_desc __policy_attrs_desc[] = {
+ NLATTR_DESC_NESTED(0, policy_attr),
+};
+
+static const struct pretty_nla_desc __policies_desc[] = {
+ NLATTR_DESC_ARRAY(0, policy_attrs),
+};
+
+static const struct pretty_nla_desc __op_policy_desc[] = {
+ NLATTR_DESC_INVALID(CTRL_ATTR_POLICY_UNSPEC),
+ NLATTR_DESC_U32(CTRL_ATTR_POLICY_DO),
+ NLATTR_DESC_U32(CTRL_ATTR_POLICY_DUMP),
+};
+
+static const struct pretty_nla_desc __op_policies_desc[] = {
+ NLATTR_DESC_NESTED(0, op_policy),
+};
+
+static const struct pretty_nla_desc __attr_desc[] = {
+ NLATTR_DESC_INVALID(CTRL_ATTR_UNSPEC),
+ NLATTR_DESC_U16(CTRL_ATTR_FAMILY_ID),
+ NLATTR_DESC_STRING(CTRL_ATTR_FAMILY_NAME),
+ NLATTR_DESC_U32(CTRL_ATTR_VERSION),
+ NLATTR_DESC_U32(CTRL_ATTR_HDRSIZE),
+ NLATTR_DESC_U32(CTRL_ATTR_MAXATTR),
+ NLATTR_DESC_ARRAY(CTRL_ATTR_OPS, attrops),
+ NLATTR_DESC_ARRAY(CTRL_ATTR_MCAST_GROUPS, mcgrps),
+ NLATTR_DESC_ARRAY(CTRL_ATTR_POLICY, policies),
+ NLATTR_DESC_ARRAY(CTRL_ATTR_OP_POLICY, op_policies),
+ NLATTR_DESC_U32(CTRL_ATTR_OP),
+};
+
+const struct pretty_nlmsg_desc genlctrl_msg_desc[] = {
+ NLMSG_DESC_INVALID(CTRL_CMD_UNSPEC),
+ NLMSG_DESC(CTRL_CMD_NEWFAMILY, attr),
+ NLMSG_DESC(CTRL_CMD_DELFAMILY, attr),
+ NLMSG_DESC(CTRL_CMD_GETFAMILY, attr),
+ NLMSG_DESC(CTRL_CMD_NEWOPS, attr),
+ NLMSG_DESC(CTRL_CMD_DELOPS, attr),
+ NLMSG_DESC(CTRL_CMD_GETOPS, attr),
+ NLMSG_DESC(CTRL_CMD_NEWMCAST_GRP, attr),
+ NLMSG_DESC(CTRL_CMD_DELMCAST_GRP, attr),
+ NLMSG_DESC(CTRL_CMD_GETMCAST_GRP, attr),
+ NLMSG_DESC(CTRL_CMD_GETPOLICY, attr),
+};
+
+const unsigned int genlctrl_msg_n_desc = ARRAY_SIZE(genlctrl_msg_desc);
diff --git a/netlink/desc-rtnl.c b/netlink/desc-rtnl.c
new file mode 100644
index 0000000..e15e6f0
--- /dev/null
+++ b/netlink/desc-rtnl.c
@@ -0,0 +1,96 @@
+/*
+ * desc-rtnl.c - rtnetlink message descriptions
+ *
+ * Descriptions of rtnetlink messages and attributes for pretty print.
+ */
+
+#include <linux/rtnetlink.h>
+
+#include "../internal.h"
+#include "prettymsg.h"
+
+static const struct pretty_nla_desc __link_desc[] = {
+ NLATTR_DESC_INVALID(IFLA_UNSPEC),
+ NLATTR_DESC_BINARY(IFLA_ADDRESS),
+ NLATTR_DESC_BINARY(IFLA_BROADCAST),
+ NLATTR_DESC_STRING(IFLA_IFNAME),
+ NLATTR_DESC_U32(IFLA_MTU),
+ NLATTR_DESC_U32(IFLA_LINK),
+ NLATTR_DESC_STRING(IFLA_QDISC),
+ NLATTR_DESC_BINARY(IFLA_STATS),
+ NLATTR_DESC_INVALID(IFLA_COST),
+ NLATTR_DESC_INVALID(IFLA_PRIORITY),
+ NLATTR_DESC_U32(IFLA_MASTER),
+ NLATTR_DESC_BINARY(IFLA_WIRELESS),
+ NLATTR_DESC_NESTED_NODESC(IFLA_PROTINFO),
+ NLATTR_DESC_U32(IFLA_TXQLEN),
+ NLATTR_DESC_BINARY(IFLA_MAP),
+ NLATTR_DESC_U32(IFLA_WEIGHT),
+ NLATTR_DESC_U8(IFLA_OPERSTATE),
+ NLATTR_DESC_U8(IFLA_LINKMODE),
+ NLATTR_DESC_NESTED_NODESC(IFLA_LINKINFO),
+ NLATTR_DESC_U32(IFLA_NET_NS_PID),
+ NLATTR_DESC_STRING(IFLA_IFALIAS),
+ NLATTR_DESC_U32(IFLA_NUM_VF),
+ NLATTR_DESC_NESTED_NODESC(IFLA_VFINFO_LIST),
+ NLATTR_DESC_BINARY(IFLA_STATS64),
+ NLATTR_DESC_NESTED_NODESC(IFLA_VF_PORTS),
+ NLATTR_DESC_NESTED_NODESC(IFLA_PORT_SELF),
+ NLATTR_DESC_NESTED_NODESC(IFLA_AF_SPEC),
+ NLATTR_DESC_U32(IFLA_GROUP),
+ NLATTR_DESC_U32(IFLA_NET_NS_FD),
+ NLATTR_DESC_U32(IFLA_EXT_MASK),
+ NLATTR_DESC_U32(IFLA_PROMISCUITY),
+ NLATTR_DESC_U32(IFLA_NUM_TX_QUEUES),
+ NLATTR_DESC_U32(IFLA_NUM_RX_QUEUES),
+ NLATTR_DESC_U8(IFLA_CARRIER),
+ NLATTR_DESC_BINARY(IFLA_PHYS_PORT_ID),
+ NLATTR_DESC_U32(IFLA_CARRIER_CHANGES),
+ NLATTR_DESC_BINARY(IFLA_PHYS_SWITCH_ID),
+ NLATTR_DESC_S32(IFLA_LINK_NETNSID),
+ NLATTR_DESC_STRING(IFLA_PHYS_PORT_NAME),
+ NLATTR_DESC_U8(IFLA_PROTO_DOWN),
+ NLATTR_DESC_U32(IFLA_GSO_MAX_SEGS),
+ NLATTR_DESC_U32(IFLA_GSO_MAX_SIZE),
+ NLATTR_DESC_BINARY(IFLA_PAD),
+ NLATTR_DESC_U32(IFLA_XDP),
+ NLATTR_DESC_U32(IFLA_EVENT),
+ NLATTR_DESC_S32(IFLA_NEW_NETNSID),
+ NLATTR_DESC_S32(IFLA_IF_NETNSID),
+ NLATTR_DESC_U32(IFLA_CARRIER_UP_COUNT),
+ NLATTR_DESC_U32(IFLA_CARRIER_DOWN_COUNT),
+ NLATTR_DESC_S32(IFLA_NEW_IFINDEX),
+ NLATTR_DESC_U32(IFLA_MIN_MTU),
+ NLATTR_DESC_U32(IFLA_MAX_MTU),
+ NLATTR_DESC_NESTED_NODESC(IFLA_PROP_LIST),
+ NLATTR_DESC_STRING(IFLA_ALT_IFNAME),
+ NLATTR_DESC_BINARY(IFLA_PERM_ADDRESS),
+};
+
+const struct pretty_nlmsg_desc rtnl_msg_desc[] = {
+ NLMSG_DESC(RTM_NEWLINK, link),
+ NLMSG_DESC(RTM_DELLINK, link),
+ NLMSG_DESC(RTM_GETLINK, link),
+ NLMSG_DESC(RTM_SETLINK, link),
+};
+
+const unsigned int rtnl_msg_n_desc = ARRAY_SIZE(rtnl_msg_desc);
+
+#define RTNL_MSGHDR_LEN(_name, _struct) \
+ [((RTM_ ## _name) - RTM_BASE) / 4] = sizeof(struct _struct)
+#define RTNL_MSGHDR_NOLEN(_name) \
+ [((RTM_ ## _name) - RTM_BASE) / 4] = USHRT_MAX
+
+const unsigned short rtnl_msghdr_lengths[] = {
+ RTNL_MSGHDR_LEN(NEWLINK, ifinfomsg),
+ RTNL_MSGHDR_LEN(NEWADDR, ifaddrmsg),
+ RTNL_MSGHDR_LEN(NEWROUTE, rtmsg),
+ RTNL_MSGHDR_LEN(NEWNEIGH, ndmsg),
+ RTNL_MSGHDR_LEN(NEWRULE, rtmsg),
+ RTNL_MSGHDR_LEN(NEWQDISC, tcmsg),
+ RTNL_MSGHDR_LEN(NEWTCLASS, tcmsg),
+ RTNL_MSGHDR_LEN(NEWTFILTER, tcmsg),
+ RTNL_MSGHDR_LEN(NEWACTION, tcamsg),
+};
+
+const unsigned int rtnl_msghdr_n_len = ARRAY_SIZE(rtnl_msghdr_lengths);
diff --git a/netlink/eee.c b/netlink/eee.c
new file mode 100644
index 0000000..04d8f0b
--- /dev/null
+++ b/netlink/eee.c
@@ -0,0 +1,189 @@
+/*
+ * eee.c - netlink implementation of eee commands
+ *
+ * Implementation of "ethtool --show-eee <dev>" and
+ * "ethtool --set-eee <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* EEE_GET */
+
+int eee_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_EEE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ bool enabled, active, tx_lpi_enabled;
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_EEE_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (!tb[ETHTOOL_A_EEE_MODES_OURS] ||
+ !tb[ETHTOOL_A_EEE_ACTIVE] || !tb[ETHTOOL_A_EEE_ENABLED] ||
+ !tb[ETHTOOL_A_EEE_TX_LPI_ENABLED] ||
+ !tb[ETHTOOL_A_EEE_TX_LPI_TIMER]) {
+ fprintf(stderr, "Malformed response from kernel\n");
+ return err_ret;
+ }
+ active = mnl_attr_get_u8(tb[ETHTOOL_A_EEE_ACTIVE]);
+ enabled = mnl_attr_get_u8(tb[ETHTOOL_A_EEE_ENABLED]);
+ tx_lpi_enabled = mnl_attr_get_u8(tb[ETHTOOL_A_EEE_TX_LPI_ENABLED]);
+
+ if (silent)
+ putchar('\n');
+ printf("EEE settings for %s:\n", nlctx->devname);
+ printf("\tEEE status: ");
+ if (bitset_is_empty(tb[ETHTOOL_A_EEE_MODES_OURS], true, &ret)) {
+ printf("not supported\n");
+ return MNL_CB_OK;
+ }
+ if (!enabled)
+ printf("disabled\n");
+ else
+ printf("enabled - %s\n", active ? "active" : "inactive");
+ printf("\tTx LPI: ");
+ if (tx_lpi_enabled)
+ printf("%u (us)\n",
+ mnl_attr_get_u32(tb[ETHTOOL_A_EEE_TX_LPI_TIMER]));
+ else
+ printf("disabled\n");
+
+ ret = dump_link_modes(nlctx, tb[ETHTOOL_A_EEE_MODES_OURS], true,
+ LM_CLASS_REAL,
+ "Supported EEE link modes: ", NULL, "\n",
+ "Not reported");
+ if (ret < 0)
+ return err_ret;
+ ret = dump_link_modes(nlctx, tb[ETHTOOL_A_EEE_MODES_OURS], false,
+ LM_CLASS_REAL,
+ "Advertised EEE link modes: ", NULL, "\n",
+ "Not reported");
+ if (ret < 0)
+ return err_ret;
+ ret = dump_link_modes(nlctx, tb[ETHTOOL_A_EEE_MODES_PEER], false,
+ LM_CLASS_REAL,
+ "Link partner advertised EEE link modes: ", NULL,
+ "\n", "Not reported");
+ if (ret < 0)
+ return err_ret;
+
+ return MNL_CB_OK;
+}
+
+int nl_geee(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_EEE_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_EEE_GET,
+ ETHTOOL_A_EEE_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, eee_reply_cb);
+}
+
+/* EEE_SET */
+
+static const struct bitset_parser_data advertise_parser_data = {
+ .no_mask = false,
+ .force_hex = true,
+};
+
+static const struct param_parser seee_params[] = {
+ {
+ .arg = "advertise",
+ .type = ETHTOOL_A_EEE_MODES_OURS,
+ .handler = nl_parse_bitset,
+ .handler_data = &advertise_parser_data,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-lpi",
+ .type = ETHTOOL_A_EEE_TX_LPI_ENABLED,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-timer",
+ .type = ETHTOOL_A_EEE_TX_LPI_TIMER,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "eee",
+ .type = ETHTOOL_A_EEE_ENABLED,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_seee(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_EEE_SET, false))
+ return -EOPNOTSUPP;
+ if (!ctx->argc) {
+ fprintf(stderr, "ethtool (--set-eee): parameters missing\n");
+ return 1;
+ }
+
+ nlctx->cmd = "--set-eee";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_EEE_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_EEE_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, seee_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 76;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 76;
+}
diff --git a/netlink/extapi.h b/netlink/extapi.h
new file mode 100644
index 0000000..e2d6b71
--- /dev/null
+++ b/netlink/extapi.h
@@ -0,0 +1,136 @@
+/*
+ * extapi.h - external interface of netlink code
+ *
+ * Declarations needed by non-netlink code (mostly ethtool.c).
+ */
+
+#ifndef ETHTOOL_EXTAPI_H__
+#define ETHTOOL_EXTAPI_H__
+
+struct cmd_context;
+struct nl_context;
+
+typedef int (*nl_func_t)(struct cmd_context *);
+typedef bool (*nl_chk_t)(struct cmd_context *);
+
+#ifdef ETHTOOL_ENABLE_NETLINK
+
+void netlink_run_handler(struct cmd_context *ctx, nl_chk_t nlchk,
+ nl_func_t nlfunc, bool no_fallback);
+
+int nl_gset(struct cmd_context *ctx);
+int nl_sset(struct cmd_context *ctx);
+int nl_permaddr(struct cmd_context *ctx);
+int nl_gfeatures(struct cmd_context *ctx);
+int nl_sfeatures(struct cmd_context *ctx);
+int nl_gprivflags(struct cmd_context *ctx);
+int nl_sprivflags(struct cmd_context *ctx);
+int nl_gring(struct cmd_context *ctx);
+int nl_sring(struct cmd_context *ctx);
+int nl_gchannels(struct cmd_context *ctx);
+int nl_schannels(struct cmd_context *ctx);
+int nl_gcoalesce(struct cmd_context *ctx);
+int nl_scoalesce(struct cmd_context *ctx);
+int nl_gpause(struct cmd_context *ctx);
+int nl_spause(struct cmd_context *ctx);
+int nl_geee(struct cmd_context *ctx);
+int nl_seee(struct cmd_context *ctx);
+int nl_tsinfo(struct cmd_context *ctx);
+int nl_cable_test(struct cmd_context *ctx);
+int nl_cable_test_tdr(struct cmd_context *ctx);
+int nl_gtunnels(struct cmd_context *ctx);
+int nl_gfec(struct cmd_context *ctx);
+int nl_sfec(struct cmd_context *ctx);
+bool nl_gstats_chk(struct cmd_context *ctx);
+int nl_gstats(struct cmd_context *ctx);
+int nl_gmodule(struct cmd_context *ctx);
+int nl_smodule(struct cmd_context *ctx);
+int nl_monitor(struct cmd_context *ctx);
+int nl_getmodule(struct cmd_context *ctx);
+int nl_grss(struct cmd_context *ctx);
+int nl_plca_get_cfg(struct cmd_context *ctx);
+int nl_plca_set_cfg(struct cmd_context *ctx);
+int nl_plca_get_status(struct cmd_context *ctx);
+int nl_get_mm(struct cmd_context *ctx);
+int nl_set_mm(struct cmd_context *ctx);
+int nl_gpse(struct cmd_context *ctx);
+int nl_spse(struct cmd_context *ctx);
+
+void nl_monitor_usage(void);
+
+int nl_get_eeprom_page(struct cmd_context *ctx,
+ struct ethtool_module_eeprom *request);
+
+#else /* ETHTOOL_ENABLE_NETLINK */
+
+static inline void netlink_run_handler(struct cmd_context *ctx __maybe_unused,
+ nl_chk_t nlchk __maybe_unused,
+ nl_func_t nlfunc __maybe_unused,
+ bool no_fallback)
+{
+ if (no_fallback) {
+ fprintf(stderr,
+ "Command requires kernel netlink support which is not "
+ "enabled in this ethtool binary\n");
+ exit(1);
+ }
+}
+
+static inline int nl_monitor(struct cmd_context *ctx __maybe_unused)
+{
+ fprintf(stderr, "Netlink not supported by ethtool, option --monitor unsupported.\n");
+ return -EOPNOTSUPP;
+}
+
+static inline void nl_monitor_usage(void)
+{
+}
+
+static inline int
+nl_get_eeprom_page(struct cmd_context *ctx __maybe_unused,
+ struct ethtool_module_eeprom *request __maybe_unused)
+{
+ fprintf(stderr, "Netlink not supported by ethtool.\n");
+ return -EOPNOTSUPP;
+}
+
+#define nl_gset NULL
+#define nl_sset NULL
+#define nl_permaddr NULL
+#define nl_gfeatures NULL
+#define nl_sfeatures NULL
+#define nl_gprivflags NULL
+#define nl_sprivflags NULL
+#define nl_gring NULL
+#define nl_sring NULL
+#define nl_gchannels NULL
+#define nl_schannels NULL
+#define nl_gcoalesce NULL
+#define nl_scoalesce NULL
+#define nl_gpause NULL
+#define nl_spause NULL
+#define nl_geee NULL
+#define nl_seee NULL
+#define nl_tsinfo NULL
+#define nl_cable_test NULL
+#define nl_cable_test_tdr NULL
+#define nl_gtunnels NULL
+#define nl_gfec NULL
+#define nl_sfec NULL
+#define nl_gstats_chk NULL
+#define nl_gstats NULL
+#define nl_getmodule NULL
+#define nl_gmodule NULL
+#define nl_smodule NULL
+#define nl_grss NULL
+#define nl_plca_get_cfg NULL
+#define nl_plca_set_cfg NULL
+#define nl_plca_get_status NULL
+#define nl_get_mm NULL
+#define nl_set_mm NULL
+#define nl_gpse NULL
+#define nl_spse NULL
+
+#endif /* ETHTOOL_ENABLE_NETLINK */
+
+#endif /* ETHTOOL_EXTAPI_H__ */
diff --git a/netlink/features.c b/netlink/features.c
new file mode 100644
index 0000000..5711ff4
--- /dev/null
+++ b/netlink/features.c
@@ -0,0 +1,569 @@
+/*
+ * features.c - netlink implementation of netdev features commands
+ *
+ * Implementation of "ethtool -k <dev>".
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "strset.h"
+#include "bitset.h"
+
+/* FEATURES_GET */
+
+struct feature_results {
+ uint32_t *hw;
+ uint32_t *wanted;
+ uint32_t *active;
+ uint32_t *nochange;
+ unsigned int count;
+ unsigned int words;
+};
+
+static int prepare_feature_results(const struct nlattr *const *tb,
+ struct feature_results *dest)
+{
+ unsigned int count;
+ int ret;
+
+ memset(dest, '\0', sizeof(*dest));
+ if (!tb[ETHTOOL_A_FEATURES_HW] || !tb[ETHTOOL_A_FEATURES_WANTED] ||
+ !tb[ETHTOOL_A_FEATURES_ACTIVE] || !tb[ETHTOOL_A_FEATURES_NOCHANGE])
+ return -EFAULT;
+ count = bitset_get_count(tb[ETHTOOL_A_FEATURES_HW], &ret);
+ if (ret < 0)
+ return -EFAULT;
+ if ((bitset_get_count(tb[ETHTOOL_A_FEATURES_WANTED], &ret) != count) ||
+ (bitset_get_count(tb[ETHTOOL_A_FEATURES_ACTIVE], &ret) != count) ||
+ (bitset_get_count(tb[ETHTOOL_A_FEATURES_NOCHANGE], &ret) != count))
+ return -EFAULT;
+ dest->hw = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_HW]);
+ dest->wanted = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_WANTED]);
+ dest->active = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_ACTIVE]);
+ dest->nochange =
+ get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_NOCHANGE]);
+ if (!dest->hw || !dest->wanted || !dest->active || !dest->nochange)
+ return -EFAULT;
+ dest->count = count;
+ dest->words = (count + 31) / 32;
+
+ return 0;
+}
+
+static bool feature_on(const uint32_t *bitmap, unsigned int idx)
+{
+ return bitmap[idx / 32] & (1 << (idx % 32));
+}
+
+static void dump_feature(const struct feature_results *results,
+ const uint32_t *ref, const uint32_t *ref_mask,
+ unsigned int idx, const char *name, const char *prefix)
+{
+ const char *suffix = "";
+
+ if (!name || !*name)
+ return;
+ if (ref) {
+ if (ref_mask && !feature_on(ref_mask, idx))
+ return;
+ if ((!ref_mask || feature_on(ref_mask, idx)) &&
+ (feature_on(results->active, idx) == feature_on(ref, idx)))
+ return;
+ }
+
+ if (!feature_on(results->hw, idx) || feature_on(results->nochange, idx))
+ suffix = " [fixed]";
+ else if (feature_on(results->active, idx) !=
+ feature_on(results->wanted, idx))
+ suffix = feature_on(results->wanted, idx) ?
+ " [requested on]" : " [requested off]";
+ if (is_json_context()) {
+ open_json_object(name);
+ print_bool(PRINT_JSON, "active", NULL, feature_on(results->active, idx));
+ print_bool(PRINT_JSON, "fixed", NULL,
+ (!feature_on(results->hw, idx) || feature_on(results->nochange, idx)));
+ print_bool(PRINT_JSON, "requested", NULL, feature_on(results->wanted, idx));
+ close_json_object();
+ } else {
+ printf("%s%s: %s%s\n", prefix, name,
+ feature_on(results->active, idx) ? "on" : "off", suffix);
+ }
+}
+
+/* this assumes pattern contains no more than one asterisk */
+static bool flag_pattern_match(const char *name, const char *pattern)
+{
+ const char *p_ast = strchr(pattern, '*');
+
+ if (p_ast) {
+ size_t name_len = strlen(name);
+ size_t pattern_len = strlen(pattern);
+
+ if (name_len + 1 < pattern_len)
+ return false;
+ if (strncmp(name, pattern, p_ast - pattern))
+ return false;
+ pattern_len -= (p_ast - pattern) + 1;
+ name += name_len - pattern_len;
+ pattern = p_ast + 1;
+ }
+ return !strcmp(name, pattern);
+}
+
+int dump_features(const struct nlattr *const *tb,
+ const struct stringset *feature_names)
+{
+ unsigned int *feature_flags = NULL;
+ struct feature_results results;
+ unsigned int i, j;
+ int ret;
+
+ ret = prepare_feature_results(tb, &results);
+ if (ret < 0)
+ return -EFAULT;
+ feature_flags = calloc(results.count, sizeof(feature_flags[0]));
+ if (!feature_flags)
+ return -ENOMEM;
+
+ /* map netdev features to legacy flags */
+ for (i = 0; i < results.count; i++) {
+ const char *name = get_string(feature_names, i);
+ feature_flags[i] = UINT_MAX;
+
+ if (!name || !*name)
+ continue;
+ for (j = 0; j < OFF_FLAG_DEF_SIZE; j++) {
+ const char *flag_name = off_flag_def[j].kernel_name;
+
+ if (flag_pattern_match(name, flag_name)) {
+ feature_flags[i] = j;
+ break;
+ }
+ }
+ }
+ /* show legacy flags and their matching features first */
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ unsigned int n_match = 0;
+ bool flag_value = false;
+
+ /* no kernel with netlink interface supports UFO */
+ if (off_flag_def[i].value == ETH_FLAG_UFO)
+ continue;
+
+ for (j = 0; j < results.count; j++) {
+ if (feature_flags[j] == i) {
+ n_match++;
+ flag_value = flag_value ||
+ feature_on(results.active, j);
+ }
+ }
+ if (n_match != 1) {
+ if (is_json_context()) {
+ open_json_object(off_flag_def[i].long_name);
+ print_bool(PRINT_JSON, "active", NULL, flag_value);
+ print_null(PRINT_JSON, "fixed", NULL, NULL);
+ print_null(PRINT_JSON, "requested", NULL, NULL);
+ close_json_object();
+ } else {
+ printf("%s: %s\n", off_flag_def[i].long_name,
+ flag_value ? "on" : "off");
+ }
+ }
+ if (n_match == 0)
+ continue;
+ for (j = 0; j < results.count; j++) {
+ const char *name = get_string(feature_names, j);
+
+ if (feature_flags[j] != i)
+ continue;
+ if (n_match == 1)
+ dump_feature(&results, NULL, NULL, j,
+ off_flag_def[i].long_name, "");
+ else
+ dump_feature(&results, NULL, NULL, j, name,
+ "\t");
+ }
+ }
+ /* and, finally, remaining netdev_features not matching legacy flags */
+ for (i = 0; i < results.count; i++) {
+ const char *name = get_string(feature_names, i);
+
+ if (!name || !*name || feature_flags[i] != UINT_MAX)
+ continue;
+ dump_feature(&results, NULL, NULL, i, name, "");
+ }
+
+ free(feature_flags);
+ return 0;
+}
+
+int features_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const struct stringset *feature_names;
+ struct nl_context *nlctx = data;
+ bool silent;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ if (!nlctx->is_monitor) {
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+ }
+ feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl2_socket);
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return silent ? MNL_CB_OK : MNL_CB_ERROR;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_FEATURES_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (silent)
+ putchar('\n');
+ open_json_object(NULL);
+ print_string(PRINT_ANY, "ifname", "Features for %s:\n", nlctx->devname);
+ ret = dump_features(tb, feature_names);
+ close_json_object();
+ return (silent || !ret) ? MNL_CB_OK : MNL_CB_ERROR;
+}
+
+int nl_gfeatures(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEATURES_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_FEATURES_GET,
+ ETHTOOL_A_FEATURES_HEADER,
+ ETHTOOL_FLAG_COMPACT_BITSETS);
+ if (ret < 0)
+ return ret;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, features_reply_cb);
+ delete_json_obj();
+
+ return ret;
+}
+
+/* FEATURES_SET */
+
+struct sfeatures_context {
+ bool nothing_changed;
+ uint32_t req_mask[];
+};
+
+static int find_feature(const char *name,
+ const struct stringset *feature_names)
+{
+ const unsigned int count = get_count(feature_names);
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ if (!strcmp(name, get_string(feature_names, i)))
+ return i;
+
+ return -1;
+}
+
+static int fill_feature(struct nl_msg_buff *msgbuff, const char *name, bool val)
+{
+ struct nlattr *bit_attr;
+
+ bit_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS_BIT);
+ if (!bit_attr)
+ return -EMSGSIZE;
+ if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME, name))
+ return -EMSGSIZE;
+ if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE, val))
+ return -EMSGSIZE;
+ mnl_attr_nest_end(msgbuff->nlhdr, bit_attr);
+
+ return 0;
+}
+
+static void set_sf_req_mask(struct nl_context *nlctx, unsigned int idx)
+{
+ struct sfeatures_context *sfctx = nlctx->cmd_private;
+
+ sfctx->req_mask[idx / 32] |= (1 << (idx % 32));
+}
+
+static int fill_legacy_flag(struct nl_context *nlctx, const char *flag_name,
+ const struct stringset *feature_names, bool val)
+{
+ struct nl_msg_buff *msgbuff = &nlctx->ethnl_socket->msgbuff;
+ const unsigned int count = get_count(feature_names);
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ const char *pattern;
+
+ if (strcmp(flag_name, off_flag_def[i].short_name) &&
+ strcmp(flag_name, off_flag_def[i].long_name))
+ continue;
+ pattern = off_flag_def[i].kernel_name;
+
+ for (j = 0; j < count; j++) {
+ const char *name = get_string(feature_names, j);
+
+ if (flag_pattern_match(name, pattern)) {
+ ret = fill_feature(msgbuff, name, val);
+ if (ret < 0)
+ return ret;
+ set_sf_req_mask(nlctx, j);
+ }
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+int fill_sfeatures_bitmap(struct nl_context *nlctx,
+ const struct stringset *feature_names)
+{
+ struct nl_msg_buff *msgbuff = &nlctx->ethnl_socket->msgbuff;
+ struct nlattr *bitset_attr;
+ struct nlattr *bits_attr;
+ int ret;
+
+ ret = -EMSGSIZE;
+ bitset_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_FEATURES_WANTED);
+ if (!bitset_attr)
+ return ret;
+ bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS);
+ if (!bits_attr)
+ goto err;
+
+ while (nlctx->argc > 0) {
+ bool val;
+
+ if (!strcmp(*nlctx->argp, "--")) {
+ nlctx->argp++;
+ nlctx->argc--;
+ break;
+ }
+ ret = -EINVAL;
+ if (nlctx->argc < 2 ||
+ (strcmp(nlctx->argp[1], "on") &&
+ strcmp(nlctx->argp[1], "off"))) {
+ fprintf(stderr,
+ "ethtool (%s): flag '%s' for parameter '%s' is"
+ " not followed by 'on' or 'off'\n",
+ nlctx->cmd, nlctx->argp[1], nlctx->param);
+ goto err;
+ }
+
+ val = !strcmp(nlctx->argp[1], "on");
+ ret = fill_legacy_flag(nlctx, nlctx->argp[0], feature_names,
+ val);
+ if (ret > 0) {
+ ret = fill_feature(msgbuff, nlctx->argp[0], val);
+ if (ret == 0) {
+ int idx = find_feature(nlctx->argp[0],
+ feature_names);
+
+ if (idx >= 0)
+ set_sf_req_mask(nlctx, idx);
+ }
+ }
+ if (ret < 0)
+ goto err;
+
+ nlctx->argp += 2;
+ nlctx->argc -= 2;
+ }
+
+ ethnla_nest_end(msgbuff, bits_attr);
+ ethnla_nest_end(msgbuff, bitset_attr);
+ return 0;
+err:
+ ethnla_nest_cancel(msgbuff, bitset_attr);
+ return ret;
+}
+
+static void show_feature_changes(struct nl_context *nlctx,
+ const struct nlattr *const *tb)
+{
+ struct sfeatures_context *sfctx = nlctx->cmd_private;
+ const struct stringset *feature_names;
+ const uint32_t *wanted_mask;
+ const uint32_t *active_mask;
+ const uint32_t *wanted_val;
+ const uint32_t *active_val;
+ unsigned int count, words;
+ unsigned int i;
+ bool diff;
+ int ret;
+
+ feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl_socket);
+ count = get_count(feature_names);
+ words = DIV_ROUND_UP(count, 32);
+
+ if (!tb[ETHTOOL_A_FEATURES_WANTED] || !tb[ETHTOOL_A_FEATURES_ACTIVE])
+ goto err;
+ if (bitset_get_count(tb[ETHTOOL_A_FEATURES_WANTED], &ret) != count ||
+ ret < 0)
+ goto err;
+ if (bitset_get_count(tb[ETHTOOL_A_FEATURES_ACTIVE], &ret) != count ||
+ ret < 0)
+ goto err;
+ wanted_val = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_WANTED]);
+ wanted_mask = get_compact_bitset_mask(tb[ETHTOOL_A_FEATURES_WANTED]);
+ active_val = get_compact_bitset_value(tb[ETHTOOL_A_FEATURES_ACTIVE]);
+ active_mask = get_compact_bitset_mask(tb[ETHTOOL_A_FEATURES_ACTIVE]);
+ if (!wanted_val || !wanted_mask || !active_val || !active_mask)
+ goto err;
+
+ sfctx->nothing_changed = true;
+ diff = false;
+ for (i = 0; i < words; i++) {
+ if (wanted_mask[i] != sfctx->req_mask[i])
+ sfctx->nothing_changed = false;
+ if (wanted_mask[i] || (active_mask[i] & ~sfctx->req_mask[i]))
+ diff = true;
+ }
+ if (!diff)
+ return;
+
+ /* result is not exactly as requested, show differences */
+ printf("Actual changes:\n");
+ for (i = 0; i < count; i++) {
+ const char *name = get_string(feature_names, i);
+
+ if (!name)
+ continue;
+ if (!feature_on(wanted_mask, i) && !feature_on(active_mask, i))
+ continue;
+ printf("%s: ", name);
+ if (feature_on(wanted_mask, i))
+ /* we requested a value but result is different */
+ printf("%s [requested %s]",
+ feature_on(wanted_val, i) ? "off" : "on",
+ feature_on(wanted_val, i) ? "on" : "off");
+ else if (!feature_on(sfctx->req_mask, i))
+ /* not requested but changed anyway */
+ printf("%s [not requested]",
+ feature_on(active_val, i) ? "on" : "off");
+ else
+ printf("%s", feature_on(active_val, i) ? "on" : "off");
+ fputc('\n', stdout);
+ }
+
+ return;
+err:
+ fprintf(stderr, "malformed diff info from kernel\n");
+}
+
+int sfeatures_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+ const struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ const char *devname;
+ int ret;
+
+ if (ghdr->cmd != ETHTOOL_MSG_FEATURES_SET_REPLY) {
+ fprintf(stderr, "warning: unexpected reply message type %u\n",
+ ghdr->cmd);
+ return MNL_CB_OK;
+ }
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ devname = get_dev_name(tb[ETHTOOL_A_FEATURES_HEADER]);
+ if (strcmp(devname, nlctx->devname)) {
+ fprintf(stderr, "warning: unexpected message for device %s\n",
+ devname);
+ return MNL_CB_OK;
+ }
+
+ show_feature_changes(nlctx, tb);
+ return MNL_CB_OK;
+}
+
+int nl_sfeatures(struct cmd_context *ctx)
+{
+ const struct stringset *feature_names;
+ struct nl_context *nlctx = ctx->nlctx;
+ struct sfeatures_context *sfctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ unsigned int words;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEATURES_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-K";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->cmd_private = &sfctx;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ feature_names = global_stringset(ETH_SS_FEATURES, nlctx->ethnl_socket);
+ words = (get_count(feature_names) + 31) / 32;
+ sfctx = malloc(sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
+ if (!sfctx)
+ return -ENOMEM;
+ memset(sfctx, '\0',
+ sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
+ nlctx->cmd_private = sfctx;
+
+ nlctx->devname = ctx->devname;
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_FEATURES_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0) {
+ free(sfctx);
+ return 2;
+ }
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_FEATURES_HEADER, ctx->devname,
+ ETHTOOL_FLAG_COMPACT_BITSETS)) {
+ free(sfctx);
+ return -EMSGSIZE;
+ }
+ ret = fill_sfeatures_bitmap(nlctx, feature_names);
+ if (ret < 0) {
+ free(sfctx);
+ return ret;
+ }
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0) {
+ free(sfctx);
+ return 92;
+ }
+ ret = nlsock_process_reply(nlsk, sfeatures_reply_cb, nlctx);
+ if (sfctx->nothing_changed) {
+ fprintf(stderr, "Could not change any device features\n");
+ free(sfctx);
+ return nlctx->exit_code ?: 1;
+ }
+ if (ret == 0) {
+ free(sfctx);
+ return 0;
+ }
+ free(sfctx);
+ return nlctx->exit_code ?: 92;
+}
diff --git a/netlink/fec.c b/netlink/fec.c
new file mode 100644
index 0000000..6027dc0
--- /dev/null
+++ b/netlink/fec.c
@@ -0,0 +1,360 @@
+/*
+ * fec.c - netlink implementation of FEC commands
+ *
+ * Implementation of "ethtool --show-fec <dev>" and
+ * "ethtool --set-fec <dev> ..."
+ */
+
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* FEC_GET */
+
+static void
+fec_mode_walk(unsigned int idx, const char *name, bool val, void *data)
+{
+ bool *empty = data;
+
+ if (!val)
+ return;
+ if (empty)
+ *empty = false;
+
+ /* Rename None to Off - in legacy ioctl None means "not supported"
+ * rather than supported but disabled.
+ */
+ if (idx == ETHTOOL_LINK_MODE_FEC_NONE_BIT)
+ name = "Off";
+ /* Rename to match the ioctl letter case */
+ else if (idx == ETHTOOL_LINK_MODE_FEC_BASER_BIT)
+ name = "BaseR";
+
+ print_string(PRINT_ANY, NULL, " %s", name);
+}
+
+static int fec_show_stats(const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHTOOL_A_FEC_STAT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ static const struct {
+ unsigned int attr;
+ char *name;
+ } stats[] = {
+ { ETHTOOL_A_FEC_STAT_CORRECTED, "corrected_blocks" },
+ { ETHTOOL_A_FEC_STAT_UNCORR, "uncorrectable_blocks" },
+ { ETHTOOL_A_FEC_STAT_CORR_BITS, "corrected_bits" },
+ };
+ bool header = false;
+ unsigned int i;
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ open_json_object("statistics");
+ for (i = 0; i < ARRAY_SIZE(stats); i++) {
+ uint64_t *vals;
+ int lanes, l;
+
+ if (!tb[stats[i].attr] ||
+ !mnl_attr_get_payload_len(tb[stats[i].attr]))
+ continue;
+
+ if (!header && !is_json_context()) {
+ printf("Statistics:\n");
+ header = true;
+ }
+
+ if (mnl_attr_get_payload_len(tb[stats[i].attr]) % 8) {
+ fprintf(stderr, "malformed netlink message (statistic)\n");
+ goto err_close_stats;
+ }
+
+ vals = mnl_attr_get_payload(tb[stats[i].attr]);
+ lanes = mnl_attr_get_payload_len(tb[stats[i].attr]) / 8 - 1;
+
+ if (!is_json_context()) {
+ fprintf(stdout, " %s: %" PRIu64 "\n",
+ stats[i].name, *vals++);
+ } else {
+ open_json_object(stats[i].name);
+ print_u64(PRINT_JSON, "total", NULL, *vals++);
+ }
+
+ if (lanes)
+ open_json_array("lanes", "");
+ for (l = 0; l < lanes; l++) {
+ if (!is_json_context())
+ fprintf(stdout, " Lane %d: %" PRIu64 "\n",
+ l, *vals++);
+ else
+ print_u64(PRINT_JSON, NULL, NULL, *vals++);
+ }
+ if (lanes)
+ close_json_array("");
+
+ close_json_object();
+ }
+ close_json_object();
+
+ return 0;
+
+err_close_stats:
+ close_json_object();
+ return -1;
+}
+
+int fec_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_FEC_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ const struct stringset *lm_strings;
+ const char *name;
+ bool fa, empty;
+ bool silent;
+ int err_ret;
+ u32 active;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_FEC_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return err_ret;
+ lm_strings = global_stringset(ETH_SS_LINK_MODES, nlctx->ethnl2_socket);
+
+ active = 0;
+ if (tb[ETHTOOL_A_FEC_ACTIVE])
+ active = mnl_attr_get_u32(tb[ETHTOOL_A_FEC_ACTIVE]);
+
+ if (silent)
+ print_nl();
+
+ open_json_object(NULL);
+
+ print_string(PRINT_ANY, "ifname", "FEC parameters for %s:\n",
+ nlctx->devname);
+
+ open_json_array("config", "Supported/Configured FEC encodings:");
+ fa = tb[ETHTOOL_A_FEC_AUTO] && mnl_attr_get_u8(tb[ETHTOOL_A_FEC_AUTO]);
+ if (fa)
+ print_string(PRINT_ANY, NULL, " %s", "Auto");
+ empty = !fa;
+
+ ret = walk_bitset(tb[ETHTOOL_A_FEC_MODES], lm_strings, fec_mode_walk,
+ &empty);
+ if (ret < 0)
+ goto err_close_dev;
+ if (empty)
+ print_string(PRINT_ANY, NULL, " %s", "None");
+ close_json_array("\n");
+
+ open_json_array("active", "Active FEC encoding:");
+ if (active) {
+ name = get_string(lm_strings, active);
+ if (name)
+ /* Take care of renames */
+ fec_mode_walk(active, name, true, NULL);
+ else
+ print_uint(PRINT_ANY, NULL, " BIT%u", active);
+ } else {
+ print_string(PRINT_ANY, NULL, " %s", "None");
+ }
+ close_json_array("\n");
+
+ if (tb[ETHTOOL_A_FEC_STATS]) {
+ ret = fec_show_stats(tb[ETHTOOL_A_FEC_STATS]);
+ if (ret < 0)
+ goto err_close_dev;
+ }
+
+ close_json_object();
+
+ return MNL_CB_OK;
+
+err_close_dev:
+ close_json_object();
+ return err_ret;
+}
+
+int nl_gfec(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ u32 flags;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEC_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ flags = get_stats_flag(nlctx, ETHTOOL_MSG_FEC_GET,
+ ETHTOOL_A_FEC_HEADER);
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_FEC_GET,
+ ETHTOOL_A_FEC_HEADER, flags);
+ if (ret < 0)
+ return ret;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, fec_reply_cb);
+ delete_json_obj();
+ return ret;
+}
+
+/* FEC_SET */
+
+static void strupc(char *dst, const char *src)
+{
+ while (*src)
+ *dst++ = toupper(*src++);
+ *dst = '\0';
+}
+
+static int fec_parse_bitset(struct nl_context *nlctx, uint16_t type,
+ const void *data __maybe_unused,
+ struct nl_msg_buff *msgbuff, void *dest)
+{
+ struct nlattr *bitset_attr;
+ struct nlattr *bits_attr;
+ struct nlattr *bit_attr;
+ char upper[ETH_GSTRING_LEN];
+ bool fec_auto = false;
+ int ret;
+
+ if (!type || dest) {
+ fprintf(stderr, "ethtool (%s): internal error parsing '%s'\n",
+ nlctx->cmd, nlctx->param);
+ return -EFAULT;
+ }
+
+ bitset_attr = ethnla_nest_start(msgbuff, type);
+ if (!bitset_attr)
+ return -EMSGSIZE;
+ ret = -EMSGSIZE;
+ if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true))
+ goto err;
+ bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS);
+ if (!bits_attr)
+ goto err;
+
+ while (nlctx->argc > 0) {
+ const char *name = *nlctx->argp;
+
+ if (!strcmp(name, "--")) {
+ nlctx->argp++;
+ nlctx->argc--;
+ break;
+ }
+
+ if (!strcasecmp(name, "auto")) {
+ fec_auto = true;
+ goto next;
+ }
+ if (!strcasecmp(name, "off")) {
+ name = "None";
+ } else {
+ strupc(upper, name);
+ name = upper;
+ }
+
+ ret = -EMSGSIZE;
+ bit_attr = ethnla_nest_start(msgbuff,
+ ETHTOOL_A_BITSET_BITS_BIT);
+ if (!bit_attr)
+ goto err;
+ if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME, name))
+ goto err;
+ ethnla_nest_end(msgbuff, bit_attr);
+
+next:
+ nlctx->argp++;
+ nlctx->argc--;
+ }
+
+ ethnla_nest_end(msgbuff, bits_attr);
+ ethnla_nest_end(msgbuff, bitset_attr);
+
+ if (ethnla_put_u8(msgbuff, ETHTOOL_A_FEC_AUTO, fec_auto))
+ goto err;
+
+ return 0;
+err:
+ ethnla_nest_cancel(msgbuff, bitset_attr);
+ return ret;
+}
+
+static const struct param_parser sfec_params[] = {
+ {
+ .arg = "encoding",
+ .type = ETHTOOL_A_FEC_MODES,
+ .handler = fec_parse_bitset,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_sfec(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEC_SET, false))
+ return -EOPNOTSUPP;
+ if (!ctx->argc) {
+ fprintf(stderr, "ethtool (--set-fec): parameters missing\n");
+ return 1;
+ }
+
+ nlctx->cmd = "--set-fec";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_FEC_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_FEC_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, sfec_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 83;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 83;
+}
diff --git a/netlink/mm.c b/netlink/mm.c
new file mode 100644
index 0000000..d026bc3
--- /dev/null
+++ b/netlink/mm.c
@@ -0,0 +1,270 @@
+/*
+ * mm.c - netlink implementation of MAC merge layer settings
+ *
+ * Implementation of "ethtool --show-mm <dev>" and "ethtool --set-mm <dev> ..."
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* MM_GET */
+
+static const char *
+mm_verify_state_to_string(enum ethtool_mm_verify_status state)
+{
+ switch (state) {
+ case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
+ return "INITIAL";
+ case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
+ return "VERIFYING";
+ case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
+ return "SUCCEEDED";
+ case ETHTOOL_MM_VERIFY_STATUS_FAILED:
+ return "FAILED";
+ case ETHTOOL_MM_VERIFY_STATUS_DISABLED:
+ return "DISABLED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int show_mm_stats(const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHTOOL_A_MM_STAT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ static const struct {
+ unsigned int attr;
+ char *name;
+ } stats[] = {
+ { ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS, "MACMergeFrameAssErrorCount" },
+ { ETHTOOL_A_MM_STAT_SMD_ERRORS, "MACMergeFrameSmdErrorCount" },
+ { ETHTOOL_A_MM_STAT_REASSEMBLY_OK, "MACMergeFrameAssOkCount" },
+ { ETHTOOL_A_MM_STAT_RX_FRAG_COUNT, "MACMergeFragCountRx" },
+ { ETHTOOL_A_MM_STAT_TX_FRAG_COUNT, "MACMergeFragCountTx" },
+ { ETHTOOL_A_MM_STAT_HOLD_COUNT, "MACMergeHoldCount" },
+ };
+ bool header = false;
+ unsigned int i;
+ size_t n;
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ open_json_object("statistics");
+ for (i = 0; i < ARRAY_SIZE(stats); i++) {
+ char fmt[64];
+
+ if (!tb[stats[i].attr])
+ continue;
+
+ if (!header && !is_json_context()) {
+ printf("Statistics:\n");
+ header = true;
+ }
+
+ if (mnl_attr_validate(tb[stats[i].attr], MNL_TYPE_U64)) {
+ fprintf(stderr, "malformed netlink message (statistic)\n");
+ goto err_close_stats;
+ }
+
+ n = snprintf(fmt, sizeof(fmt), " %s: %%" PRIu64 "\n",
+ stats[i].name);
+ if (n >= sizeof(fmt)) {
+ fprintf(stderr, "internal error - malformed label\n");
+ continue;
+ }
+
+ print_u64(PRINT_ANY, stats[i].name, fmt,
+ mnl_attr_get_u64(tb[stats[i].attr]));
+ }
+ close_json_object();
+
+ return 0;
+
+err_close_stats:
+ close_json_object();
+ return -1;
+}
+
+int mm_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_MM_MAX + 1] = {};
+ struct nl_context *nlctx = data;
+ DECLARE_ATTR_TB_INFO(tb);
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_MM_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ print_nl();
+
+ open_json_object(NULL);
+
+ print_string(PRINT_ANY, "ifname", "MAC Merge layer state for %s:\n",
+ nlctx->devname);
+
+ show_bool("pmac-enabled", "pMAC enabled: %s\n",
+ tb[ETHTOOL_A_MM_PMAC_ENABLED]);
+ show_bool("tx-enabled", "TX enabled: %s\n",
+ tb[ETHTOOL_A_MM_TX_ENABLED]);
+ show_bool("tx-active", "TX active: %s\n", tb[ETHTOOL_A_MM_TX_ACTIVE]);
+ show_u32("tx-min-frag-size", "TX minimum fragment size: ",
+ tb[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE]);
+ show_u32("rx-min-frag-size", "RX minimum fragment size: ",
+ tb[ETHTOOL_A_MM_RX_MIN_FRAG_SIZE]);
+ show_bool("verify-enabled", "Verify enabled: %s\n",
+ tb[ETHTOOL_A_MM_VERIFY_ENABLED]);
+ show_u32("verify-time", "Verify time: ",
+ tb[ETHTOOL_A_MM_VERIFY_TIME]);
+ show_u32("max-verify-time", "Max verify time: ",
+ tb[ETHTOOL_A_MM_MAX_VERIFY_TIME]);
+
+ if (tb[ETHTOOL_A_MM_VERIFY_STATUS]) {
+ u8 val = mnl_attr_get_u8(tb[ETHTOOL_A_MM_VERIFY_STATUS]);
+
+ print_string(PRINT_ANY, "verify-status", "Verification status: %s\n",
+ mm_verify_state_to_string(val));
+ }
+
+ if (tb[ETHTOOL_A_MM_STATS]) {
+ ret = show_mm_stats(tb[ETHTOOL_A_MM_STATS]);
+ if (ret) {
+ fprintf(stderr, "Failed to print stats: %d\n", ret);
+ goto err;
+ }
+ }
+
+ if (!silent)
+ print_nl();
+
+ close_json_object();
+
+ return MNL_CB_OK;
+
+err:
+ close_json_object();
+ return err_ret;
+}
+
+int nl_get_mm(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ u32 flags;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_MM_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ flags = get_stats_flag(nlctx, ETHTOOL_MSG_MM_GET, ETHTOOL_A_MM_HEADER);
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_MM_GET,
+ ETHTOOL_A_MM_HEADER, flags);
+ if (ret)
+ return ret;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, mm_reply_cb);
+ delete_json_obj();
+ return ret;
+}
+
+/* MM_SET */
+
+static const struct param_parser mm_set_params[] = {
+ {
+ .arg = "verify-enabled",
+ .type = ETHTOOL_A_MM_VERIFY_ENABLED,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "verify-time",
+ .type = ETHTOOL_A_MM_VERIFY_TIME,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-enabled",
+ .type = ETHTOOL_A_MM_TX_ENABLED,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "pmac-enabled",
+ .type = ETHTOOL_A_MM_PMAC_ENABLED,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-min-frag-size",
+ .type = ETHTOOL_A_MM_TX_MIN_FRAG_SIZE,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_set_mm(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_MM_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "--set-mm";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_MM_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret)
+ return ret;
+
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_MM_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, mm_set_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret)
+ return ret;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret)
+ return nlctx->exit_code;
+
+ return 0;
+}
diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
new file mode 100644
index 0000000..fe02c5a
--- /dev/null
+++ b/netlink/module-eeprom.c
@@ -0,0 +1,310 @@
+/*
+ * module-eeprom.c - netlink implementation of module eeprom get command
+ *
+ * ethtool -m <dev>
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#include "../sff-common.h"
+#include "../qsfp.h"
+#include "../cmis.h"
+#include "../internal.h"
+#include "../common.h"
+#include "../list.h"
+#include "netlink.h"
+#include "parser.h"
+
+#define ETH_I2C_ADDRESS_LOW 0x50
+#define ETH_I2C_MAX_ADDRESS 0x7F
+
+struct cmd_params {
+ u8 dump_hex;
+ u8 dump_raw;
+ u32 offset;
+ u32 length;
+ u32 page;
+ u32 bank;
+ u32 i2c_address;
+};
+
+static const struct param_parser getmodule_params[] = {
+ {
+ .arg = "hex",
+ .handler = nl_parse_u8bool,
+ .dest_offset = offsetof(struct cmd_params, dump_hex),
+ .min_argc = 1,
+ },
+ {
+ .arg = "raw",
+ .handler = nl_parse_u8bool,
+ .dest_offset = offsetof(struct cmd_params, dump_raw),
+ .min_argc = 1,
+ },
+ {
+ .arg = "offset",
+ .handler = nl_parse_direct_u32,
+ .dest_offset = offsetof(struct cmd_params, offset),
+ .min_argc = 1,
+ },
+ {
+ .arg = "length",
+ .handler = nl_parse_direct_u32,
+ .dest_offset = offsetof(struct cmd_params, length),
+ .min_argc = 1,
+ },
+ {
+ .arg = "page",
+ .handler = nl_parse_direct_u32,
+ .dest_offset = offsetof(struct cmd_params, page),
+ .min_argc = 1,
+ },
+ {
+ .arg = "bank",
+ .handler = nl_parse_direct_u32,
+ .dest_offset = offsetof(struct cmd_params, bank),
+ .min_argc = 1,
+ },
+ {
+ .arg = "i2c",
+ .handler = nl_parse_direct_u32,
+ .dest_offset = offsetof(struct cmd_params, i2c_address),
+ .min_argc = 1,
+ },
+ {}
+};
+
+static struct list_head eeprom_page_list = LIST_HEAD_INIT(eeprom_page_list);
+
+struct eeprom_page_entry {
+ struct list_head list; /* Member of eeprom_page_list */
+ void *data;
+};
+
+static int eeprom_page_list_add(void *data)
+{
+ struct eeprom_page_entry *entry;
+
+ entry = malloc(sizeof(*entry));
+ if (!entry)
+ return -ENOMEM;
+
+ entry->data = data;
+ list_add(&entry->list, &eeprom_page_list);
+
+ return 0;
+}
+
+static void eeprom_page_list_flush(void)
+{
+ struct eeprom_page_entry *entry;
+ struct list_head *head, *next;
+
+ list_for_each_safe(head, next, &eeprom_page_list) {
+ entry = (struct eeprom_page_entry *) head;
+ free(entry->data);
+ list_del(head);
+ free(entry);
+ }
+}
+
+static int get_eeprom_page_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {};
+ struct ethtool_module_eeprom *request = data;
+ DECLARE_ATTR_TB_INFO(tb);
+ u8 *eeprom_data;
+ int ret;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ if (!tb[ETHTOOL_A_MODULE_EEPROM_DATA])
+ return MNL_CB_ERROR;
+
+ eeprom_data = mnl_attr_get_payload(tb[ETHTOOL_A_MODULE_EEPROM_DATA]);
+ request->data = malloc(request->length);
+ if (!request->data)
+ return MNL_CB_ERROR;
+ memcpy(request->data, eeprom_data, request->length);
+
+ ret = eeprom_page_list_add(request->data);
+ if (ret < 0)
+ goto err_list_add;
+
+ return MNL_CB_OK;
+
+err_list_add:
+ free(request->data);
+ return MNL_CB_ERROR;
+}
+
+int nl_get_eeprom_page(struct cmd_context *ctx,
+ struct ethtool_module_eeprom *request)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsock;
+ struct nl_msg_buff *msg;
+ int ret;
+
+ if (!request || request->i2c_address > ETH_I2C_MAX_ADDRESS)
+ return -EINVAL;
+
+ nlsock = nlctx->ethnl_socket;
+ msg = &nlsock->msgbuff;
+
+ ret = nlsock_prep_get_request(nlsock, ETHTOOL_MSG_MODULE_EEPROM_GET,
+ ETHTOOL_A_MODULE_EEPROM_HEADER, 0);
+ if (ret < 0)
+ return ret;
+
+ if (ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH,
+ request->length) ||
+ ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET,
+ request->offset) ||
+ ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE,
+ request->page) ||
+ ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_BANK,
+ request->bank) ||
+ ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS,
+ request->i2c_address))
+ return -EMSGSIZE;
+
+ ret = nlsock_sendmsg(nlsock, NULL);
+ if (ret < 0)
+ return ret;
+ return nlsock_process_reply(nlsock, get_eeprom_page_reply_cb,
+ (void *)request);
+}
+
+static int eeprom_dump_hex(struct cmd_context *ctx)
+{
+ struct ethtool_module_eeprom request = {
+ .length = 128,
+ .i2c_address = ETH_I2C_ADDRESS_LOW,
+ };
+ int ret;
+
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+
+ dump_hex(stdout, request.data, request.length, request.offset);
+
+ return 0;
+}
+
+static int eeprom_parse(struct cmd_context *ctx)
+{
+ struct ethtool_module_eeprom request = {
+ .length = 1,
+ .i2c_address = ETH_I2C_ADDRESS_LOW,
+ };
+ int ret;
+
+ /* Fetch the SFF-8024 Identifier Value. For all supported standards, it
+ * is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024,
+ * revision 4.9.
+ */
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+
+ switch (request.data[0]) {
+#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ case SFF8024_ID_GBIC:
+ case SFF8024_ID_SOLDERED_MODULE:
+ case SFF8024_ID_SFP:
+ return sff8079_show_all_nl(ctx);
+ case SFF8024_ID_QSFP:
+ case SFF8024_ID_QSFP28:
+ case SFF8024_ID_QSFP_PLUS:
+ return sff8636_show_all_nl(ctx);
+ case SFF8024_ID_QSFP_DD:
+ case SFF8024_ID_OSFP:
+ case SFF8024_ID_DSFP:
+ case SFF8024_ID_QSFP_PLUS_CMIS:
+ case SFF8024_ID_SFP_DD_CMIS:
+ case SFF8024_ID_SFP_PLUS_CMIS:
+ return cmis_show_all_nl(ctx);
+#endif
+ default:
+ /* If we cannot recognize the memory map, default to dumping
+ * the first 128 bytes in hex.
+ */
+ return eeprom_dump_hex(ctx);
+ }
+}
+
+int nl_getmodule(struct cmd_context *ctx)
+{
+ struct cmd_params getmodule_cmd_params = {};
+ struct ethtool_module_eeprom request = {0};
+ struct nl_context *nlctx = ctx->nlctx;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_EEPROM_GET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-m";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ ret = nl_parser(nlctx, getmodule_params, &getmodule_cmd_params, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return ret;
+
+ if (getmodule_cmd_params.dump_hex && getmodule_cmd_params.dump_raw) {
+ fprintf(stderr, "Hex and raw dump cannot be specified together\n");
+ return -EINVAL;
+ }
+
+ /* When complete hex/raw dump of the EEPROM is requested, fallback to
+ * ioctl. Netlink can only request specific pages.
+ */
+ if ((getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) &&
+ !getmodule_cmd_params.page && !getmodule_cmd_params.bank &&
+ !getmodule_cmd_params.i2c_address) {
+ nlctx->ioctl_fallback = true;
+ return -EOPNOTSUPP;
+ }
+
+#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ if (getmodule_cmd_params.page || getmodule_cmd_params.bank ||
+ getmodule_cmd_params.offset || getmodule_cmd_params.length)
+#endif
+ getmodule_cmd_params.dump_hex = true;
+
+ request.offset = getmodule_cmd_params.offset;
+ request.length = getmodule_cmd_params.length ?: 128;
+ request.page = getmodule_cmd_params.page;
+ request.bank = getmodule_cmd_params.bank;
+ request.i2c_address = getmodule_cmd_params.i2c_address ?: ETH_I2C_ADDRESS_LOW;
+
+ if (request.page && !request.offset)
+ request.offset = 128;
+
+ if (getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) {
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ goto cleanup;
+
+ if (getmodule_cmd_params.dump_raw)
+ fwrite(request.data, 1, request.length, stdout);
+ else
+ dump_hex(stdout, request.data, request.length,
+ request.offset);
+ } else {
+ ret = eeprom_parse(ctx);
+ if (ret < 0)
+ goto cleanup;
+ }
+
+cleanup:
+ eeprom_page_list_flush();
+ return ret;
+}
diff --git a/netlink/module.c b/netlink/module.c
new file mode 100644
index 0000000..54aa6d0
--- /dev/null
+++ b/netlink/module.c
@@ -0,0 +1,179 @@
+/*
+ * module.c - netlink implementation of module commands
+ *
+ * Implementation of "ethtool --show-module <dev>" and
+ * "ethtool --set-module <dev> ..."
+ */
+
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+/* MODULE_GET */
+
+static const char *module_power_mode_policy_name(u8 val)
+{
+ switch (val) {
+ case ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH:
+ return "high";
+ case ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO:
+ return "auto";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *module_power_mode_name(u8 val)
+{
+ switch (val) {
+ case ETHTOOL_MODULE_POWER_MODE_LOW:
+ return "low";
+ case ETHTOOL_MODULE_POWER_MODE_HIGH:
+ return "high";
+ default:
+ return "unknown";
+ }
+}
+
+int module_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_MODULE_MAX + 1] = {};
+ struct nl_context *nlctx = data;
+ DECLARE_ATTR_TB_INFO(tb);
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_MODULE_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ print_nl();
+
+ open_json_object(NULL);
+
+ print_string(PRINT_ANY, "ifname", "Module parameters for %s:\n",
+ nlctx->devname);
+
+ if (tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]) {
+ u8 val;
+
+ val = mnl_attr_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]);
+ print_string(PRINT_ANY, "power-mode-policy",
+ "power-mode-policy: %s\n",
+ module_power_mode_policy_name(val));
+ }
+
+ if (tb[ETHTOOL_A_MODULE_POWER_MODE]) {
+ u8 val;
+
+ val = mnl_attr_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE]);
+ print_string(PRINT_ANY, "power-mode",
+ "power-mode: %s\n", module_power_mode_name(val));
+ }
+
+ close_json_object();
+
+ return MNL_CB_OK;
+}
+
+int nl_gmodule(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ nlsk = nlctx->ethnl_socket;
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_MODULE_GET,
+ ETHTOOL_A_MODULE_HEADER, 0);
+ if (ret < 0)
+ return ret;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, module_reply_cb);
+ delete_json_obj();
+ return ret;
+}
+
+/* MODULE_SET */
+
+static const struct lookup_entry_u8 power_mode_policy_values[] = {
+ { .arg = "high", .val = ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH },
+ { .arg = "auto", .val = ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO },
+ {}
+};
+
+static const struct param_parser smodule_params[] = {
+ {
+ .arg = "power-mode-policy",
+ .type = ETHTOOL_A_MODULE_POWER_MODE_POLICY,
+ .handler = nl_parse_lookup_u8,
+ .handler_data = power_mode_policy_values,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_smodule(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_SET, false))
+ return -EOPNOTSUPP;
+ if (!ctx->argc) {
+ fprintf(stderr, "ethtool (--set-module): parameters missing\n");
+ return 1;
+ }
+
+ nlctx->cmd = "--set-module";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_MODULE_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_MODULE_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, smodule_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 83;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 83;
+}
diff --git a/netlink/monitor.c b/netlink/monitor.c
new file mode 100644
index 0000000..ace9b25
--- /dev/null
+++ b/netlink/monitor.c
@@ -0,0 +1,324 @@
+/*
+ * monitor.c - netlink notification monitor
+ *
+ * Implementation of "ethtool --monitor" for watching netlink notifications.
+ */
+
+#include <errno.h>
+
+#include "../internal.h"
+#include "netlink.h"
+#include "nlsock.h"
+#include "strset.h"
+
+static struct {
+ uint8_t cmd;
+ mnl_cb_t cb;
+} monitor_callbacks[] = {
+ {
+ .cmd = ETHTOOL_MSG_LINKMODES_NTF,
+ .cb = linkmodes_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_LINKINFO_NTF,
+ .cb = linkinfo_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_WOL_NTF,
+ .cb = wol_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_DEBUG_NTF,
+ .cb = debug_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_FEATURES_NTF,
+ .cb = features_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_PRIVFLAGS_NTF,
+ .cb = privflags_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_RINGS_NTF,
+ .cb = rings_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_CHANNELS_NTF,
+ .cb = channels_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_COALESCE_NTF,
+ .cb = coalesce_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_PAUSE_NTF,
+ .cb = pause_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_EEE_NTF,
+ .cb = eee_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_CABLE_TEST_NTF,
+ .cb = cable_test_ntf_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
+ .cb = cable_test_tdr_ntf_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_FEC_NTF,
+ .cb = fec_reply_cb,
+ },
+ {
+ .cmd = ETHTOOL_MSG_MODULE_NTF,
+ .cb = module_reply_cb,
+ },
+};
+
+static void clear_filter(struct nl_context *nlctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < CMDMASK_WORDS; i++)
+ nlctx->filter_cmds[i] = 0;
+}
+
+static bool test_filter_cmd(const struct nl_context *nlctx, unsigned int cmd)
+{
+ return nlctx->filter_cmds[cmd / 32] & (1U << (cmd % 32));
+}
+
+static void set_filter_cmd(struct nl_context *nlctx, unsigned int cmd)
+{
+ nlctx->filter_cmds[cmd / 32] |= (1U << (cmd % 32));
+}
+
+static void set_filter_all(struct nl_context *nlctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(monitor_callbacks); i++)
+ set_filter_cmd(nlctx, monitor_callbacks[i].cmd);
+}
+
+static int monitor_any_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+ struct nl_context *nlctx = data;
+ unsigned int i;
+
+ if (!test_filter_cmd(nlctx, ghdr->cmd))
+ return MNL_CB_OK;
+
+ for (i = 0; i < MNL_ARRAY_SIZE(monitor_callbacks); i++)
+ if (monitor_callbacks[i].cmd == ghdr->cmd)
+ return monitor_callbacks[i].cb(nlhdr, data);
+
+ return MNL_CB_OK;
+}
+
+struct monitor_option {
+ const char *pattern;
+ uint8_t cmd;
+ uint32_t info_mask;
+};
+
+static struct monitor_option monitor_opts[] = {
+ {
+ .pattern = "|--all",
+ .cmd = 0,
+ },
+ {
+ .pattern = "-s|--change",
+ .cmd = ETHTOOL_MSG_LINKINFO_NTF,
+ },
+ {
+ .pattern = "-s|--change",
+ .cmd = ETHTOOL_MSG_LINKMODES_NTF,
+ },
+ {
+ .pattern = "-s|--change",
+ .cmd = ETHTOOL_MSG_WOL_NTF,
+ },
+ {
+ .pattern = "-s|--change",
+ .cmd = ETHTOOL_MSG_DEBUG_NTF,
+ },
+ {
+ .pattern = "-k|--show-features|--show-offload|-K|--features|--offload",
+ .cmd = ETHTOOL_MSG_FEATURES_NTF,
+ },
+ {
+ .pattern = "--show-priv-flags|--set-priv-flags",
+ .cmd = ETHTOOL_MSG_PRIVFLAGS_NTF,
+ },
+ {
+ .pattern = "-g|--show-ring|-G|--set-ring",
+ .cmd = ETHTOOL_MSG_RINGS_NTF,
+ },
+ {
+ .pattern = "-l|--show-channels|-L|--set-channels",
+ .cmd = ETHTOOL_MSG_CHANNELS_NTF,
+ },
+ {
+ .pattern = "-c|--show-coalesce|-C|--coalesce",
+ .cmd = ETHTOOL_MSG_COALESCE_NTF,
+ },
+ {
+ .pattern = "-a|--show-pause|-A|--pause",
+ .cmd = ETHTOOL_MSG_PAUSE_NTF,
+ },
+ {
+ .pattern = "--show-eee|--set-eee",
+ .cmd = ETHTOOL_MSG_EEE_NTF,
+ },
+ {
+ .pattern = "--cable-test",
+ .cmd = ETHTOOL_MSG_CABLE_TEST_NTF,
+ },
+ {
+ .pattern = "--cable-test-tdr",
+ .cmd = ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
+ },
+ {
+ .pattern = "--show-module|--set-module",
+ .cmd = ETHTOOL_MSG_MODULE_NTF,
+ },
+};
+
+static bool pattern_match(const char *s, const char *pattern)
+{
+ const char *opt = pattern;
+ const char *next;
+ int slen = strlen(s);
+ int optlen;
+
+ do {
+ next = opt;
+ while (*next && *next != '|')
+ next++;
+ optlen = next - opt;
+ if (slen == optlen && !strncmp(s, opt, optlen))
+ return true;
+
+ opt = next;
+ if (*opt == '|')
+ opt++;
+ } while (*opt);
+
+ return false;
+}
+
+static int parse_monitor(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ char **argp = ctx->argp;
+ int argc = ctx->argc;
+ const char *opt = "";
+ bool opt_found;
+ unsigned int i;
+
+ if (*argp && argp[0][0] == '-') {
+ opt = *argp;
+ argp++;
+ argc--;
+ }
+ opt_found = false;
+ clear_filter(nlctx);
+ for (i = 0; i < MNL_ARRAY_SIZE(monitor_opts); i++) {
+ if (pattern_match(opt, monitor_opts[i].pattern)) {
+ unsigned int cmd = monitor_opts[i].cmd;
+
+ if (!cmd)
+ set_filter_all(nlctx);
+ else
+ set_filter_cmd(nlctx, cmd);
+ opt_found = true;
+ }
+ }
+ if (!opt_found) {
+ fprintf(stderr, "monitoring for option '%s' not supported\n",
+ *argp);
+ return -1;
+ }
+
+ if (*argp && strcmp(*argp, WILDCARD_DEVNAME))
+ ctx->devname = *argp;
+ return 0;
+}
+
+int nl_monitor(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx;
+ struct nl_socket *nlsk;
+ uint32_t grpid;
+ bool is_dev;
+ int ret;
+
+ ret = netlink_init(ctx);
+ if (ret < 0) {
+ fprintf(stderr, "Netlink interface initialization failed, option --monitor not supported.\n");
+ return ret;
+ }
+ nlctx = ctx->nlctx;
+ nlsk = nlctx->ethnl_socket;
+ grpid = nlctx->ethnl_mongrp;
+ if (!grpid) {
+ fprintf(stderr, "multicast group 'monitor' not found\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (parse_monitor(ctx) < 0)
+ return 1;
+ is_dev = ctx->devname && strcmp(ctx->devname, WILDCARD_DEVNAME);
+
+ ret = preload_global_strings(nlsk);
+ if (ret < 0)
+ return ret;
+ ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
+ &grpid, sizeof(grpid));
+ if (ret < 0)
+ return ret;
+ if (is_dev) {
+ ret = preload_perdev_strings(nlsk, ctx->devname);
+ if (ret < 0)
+ goto out_strings;
+ }
+
+ nlctx->filter_devname = ctx->devname;
+ nlctx->is_monitor = true;
+ nlsk->port = 0;
+ nlsk->seq = 0;
+
+ fputs("listening...\n", stdout);
+ fflush(stdout);
+ ret = nlsock_process_reply(nlsk, monitor_any_cb, nlctx);
+
+out_strings:
+ cleanup_all_strings();
+ return ret;
+}
+
+void nl_monitor_usage(void)
+{
+ unsigned int i;
+ const char *p;
+
+ fputs(" ethtool --monitor Show kernel notifications\n",
+ stdout);
+ fputs(" ( [ --all ]", stdout);
+ for (i = 1; i < MNL_ARRAY_SIZE(monitor_opts); i++) {
+ if (!strcmp(monitor_opts[i].pattern, monitor_opts[i - 1].pattern))
+ continue;
+ fputs("\n | ", stdout);
+ for (p = monitor_opts[i].pattern; *p; p++)
+ if (*p == '|')
+ fputs(" | ", stdout);
+ else
+ fputc(*p, stdout);
+ }
+ fputs(" )\n", stdout);
+ fputs(" [ DEVNAME | * ]\n", stdout);
+}
diff --git a/netlink/msgbuff.c b/netlink/msgbuff.c
new file mode 100644
index 0000000..216f5b9
--- /dev/null
+++ b/netlink/msgbuff.c
@@ -0,0 +1,256 @@
+/*
+ * msgbuff.c - netlink message buffer
+ *
+ * Data structures and code for flexible message buffer abstraction.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "../internal.h"
+#include "netlink.h"
+#include "msgbuff.h"
+
+#define MAX_MSG_SIZE (4 << 20) /* 4 MB */
+
+/**
+ * msgbuff_realloc() - reallocate buffer if needed
+ * @msgbuff: message buffer
+ * @new_size: requested minimum size (add MNL_SOCKET_BUFFER_SIZE if zero)
+ *
+ * Make sure allocated buffer has size at least @new_size. If @new_size is
+ * shorter than current size, do nothing. If @new_size is 0, grow buffer by
+ * MNL_SOCKET_BUFFER_SIZE. Fail if new size would exceed MAX_MSG_SIZE.
+ *
+ * Return: 0 on success or negative error code
+ */
+int msgbuff_realloc(struct nl_msg_buff *msgbuff, unsigned int new_size)
+{
+ unsigned int nlhdr_off, genlhdr_off, payload_off;
+ unsigned int old_size = msgbuff->size;
+ char *nbuff;
+
+ nlhdr_off = (char *)msgbuff->nlhdr - msgbuff->buff;
+ genlhdr_off = (char *)msgbuff->genlhdr - msgbuff->buff;
+ payload_off = (char *)msgbuff->payload - msgbuff->buff;
+
+ if (!new_size)
+ new_size = old_size + MNL_SOCKET_BUFFER_SIZE;
+ if (new_size <= old_size)
+ return 0;
+ if (new_size > MAX_MSG_SIZE)
+ return -EMSGSIZE;
+ nbuff = realloc(msgbuff->buff, new_size);
+ if (!nbuff) {
+ msgbuff->buff = NULL;
+ msgbuff->size = 0;
+ msgbuff->left = 0;
+ return -ENOMEM;
+ }
+ if (nbuff != msgbuff->buff) {
+ if (new_size > old_size)
+ memset(nbuff + old_size, '\0', new_size - old_size);
+ msgbuff->nlhdr = (struct nlmsghdr *)(nbuff + nlhdr_off);
+ msgbuff->genlhdr = (struct genlmsghdr *)(nbuff + genlhdr_off);
+ msgbuff->payload = nbuff + payload_off;
+ msgbuff->buff = nbuff;
+ }
+ msgbuff->size = new_size;
+ msgbuff->left += (new_size - old_size);
+
+ return 0;
+}
+
+/**
+ * msgbuff_append() - add contents of another message buffer
+ * @dest: target message buffer
+ * @src: source message buffer
+ *
+ * Append contents of @src at the end of @dest. Fail if target buffer cannot
+ * be reallocated to sufficient size.
+ *
+ * Return: 0 on success or negative error code.
+ */
+int msgbuff_append(struct nl_msg_buff *dest, struct nl_msg_buff *src)
+{
+ unsigned int src_len = mnl_nlmsg_get_payload_len(src->nlhdr);
+ unsigned int dest_len = MNL_ALIGN(msgbuff_len(dest));
+ int ret;
+
+ src_len -= GENL_HDRLEN;
+ ret = msgbuff_realloc(dest, dest_len + src_len);
+ if (ret < 0)
+ return ret;
+ memcpy(mnl_nlmsg_get_payload_tail(dest->nlhdr), src->payload, src_len);
+ msgbuff_reset(dest, dest_len + src_len);
+
+ return 0;
+}
+
+/**
+ * ethnla_put - write a netlink attribute to message buffer
+ * @msgbuff: message buffer
+ * @type: attribute type
+ * @len: attribute payload length
+ * @data: attribute payload
+ *
+ * Appends a netlink attribute with header to message buffer, reallocates
+ * if needed. This is mostly used via specific ethnla_put_* wrappers for
+ * basic data types.
+ *
+ * Return: false on success, true on error (reallocation failed)
+ */
+bool ethnla_put(struct nl_msg_buff *msgbuff, uint16_t type, size_t len,
+ const void *data)
+{
+ struct nlmsghdr *nlhdr = msgbuff->nlhdr;
+
+ while (!mnl_attr_put_check(nlhdr, msgbuff->left, type, len, data)) {
+ int ret = msgbuff_realloc(msgbuff, 0);
+
+ if (ret < 0)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * ethnla_nest_start - start a nested attribute
+ * @msgbuff: message buffer
+ * @type: nested attribute type (NLA_F_NESTED is added automatically)
+ *
+ * Return: pointer to the nest attribute or null of error
+ */
+struct nlattr *ethnla_nest_start(struct nl_msg_buff *msgbuff, uint16_t type)
+{
+ struct nlmsghdr *nlhdr = msgbuff->nlhdr;
+ struct nlattr *attr;
+
+ do {
+ attr = mnl_attr_nest_start_check(nlhdr, msgbuff->left, type);
+ if (attr)
+ return attr;
+ } while (msgbuff_realloc(msgbuff, 0) == 0);
+
+ return NULL;
+}
+
+/**
+ * ethnla_fill_header() - write standard ethtool request header to message
+ * @msgbuff: message buffer
+ * @type: attribute type for header nest
+ * @devname: device name (NULL to omit)
+ * @flags: request flags (omitted if 0)
+ *
+ * Return: pointer to the nest attribute or null of error
+ */
+bool ethnla_fill_header(struct nl_msg_buff *msgbuff, uint16_t type,
+ const char *devname, uint32_t flags)
+{
+ struct nlattr *nest;
+
+ nest = ethnla_nest_start(msgbuff, type);
+ if (!nest)
+ return true;
+
+ if ((devname &&
+ ethnla_put_strz(msgbuff, ETHTOOL_A_HEADER_DEV_NAME, devname)) ||
+ (flags &&
+ ethnla_put_u32(msgbuff, ETHTOOL_A_HEADER_FLAGS, flags)))
+ goto err;
+
+ ethnla_nest_end(msgbuff, nest);
+ return false;
+
+err:
+ ethnla_nest_cancel(msgbuff, nest);
+ return true;
+}
+
+/**
+ * __msg_init() - init a genetlink message, fill netlink and genetlink header
+ * @msgbuff: message buffer
+ * @family: genetlink family
+ * @cmd: genetlink command (genlmsghdr::cmd)
+ * @flags: netlink flags (nlmsghdr::nlmsg_flags)
+ * @version: genetlink family version (genlmsghdr::version)
+ *
+ * Initialize a new genetlink message, fill netlink and genetlink header and
+ * set pointers in struct nl_msg_buff.
+ *
+ * Return: 0 on success or negative error code.
+ */
+int __msg_init(struct nl_msg_buff *msgbuff, int family, int cmd,
+ unsigned int flags, int version)
+{
+ struct nlmsghdr *nlhdr;
+ struct genlmsghdr *genlhdr;
+ int ret;
+
+ ret = msgbuff_realloc(msgbuff, MNL_SOCKET_BUFFER_SIZE);
+ if (ret < 0)
+ return ret;
+ memset(msgbuff->buff, '\0', NLMSG_HDRLEN + GENL_HDRLEN);
+
+ nlhdr = mnl_nlmsg_put_header(msgbuff->buff);
+ nlhdr->nlmsg_type = family;
+ nlhdr->nlmsg_flags = flags;
+ msgbuff->nlhdr = nlhdr;
+
+ genlhdr = mnl_nlmsg_put_extra_header(nlhdr, sizeof(*genlhdr));
+ genlhdr->cmd = cmd;
+ genlhdr->version = version;
+ msgbuff->genlhdr = genlhdr;
+
+ msgbuff->payload = mnl_nlmsg_get_payload_offset(nlhdr, GENL_HDRLEN);
+
+ return 0;
+}
+
+/**
+ * msg_init() - init an ethtool netlink message
+ * @msgbuff: message buffer
+ * @cmd: genetlink command (genlmsghdr::cmd)
+ * @flags: netlink flags (nlmsghdr::nlmsg_flags)
+ *
+ * Initialize a new ethtool netlink message, fill netlink and genetlink header
+ * and set pointers in struct nl_msg_buff.
+ *
+ * Return: 0 on success or negative error code.
+ */
+int msg_init(struct nl_context *nlctx, struct nl_msg_buff *msgbuff, int cmd,
+ unsigned int flags)
+{
+ return __msg_init(msgbuff, nlctx->ethnl_fam, cmd, flags,
+ ETHTOOL_GENL_VERSION);
+}
+
+/**
+ * msgbuff_init() - initialize a message buffer
+ * @msgbuff: message buffer
+ *
+ * Initialize a message buffer structure before first use. Buffer length is
+ * set to zero and the buffer is not allocated until the first call to
+ * msgbuff_reallocate().
+ */
+void msgbuff_init(struct nl_msg_buff *msgbuff)
+{
+ memset(msgbuff, '\0', sizeof(*msgbuff));
+}
+
+/**
+ * msg_done() - destroy a message buffer
+ * @msgbuff: message buffer
+ *
+ * Free the buffer and reset size and remaining size.
+ */
+void msgbuff_done(struct nl_msg_buff *msgbuff)
+{
+ free(msgbuff->buff);
+ msgbuff->buff = NULL;
+ msgbuff->size = 0;
+ msgbuff->left = 0;
+}
diff --git a/netlink/msgbuff.h b/netlink/msgbuff.h
new file mode 100644
index 0000000..7d6731f
--- /dev/null
+++ b/netlink/msgbuff.h
@@ -0,0 +1,123 @@
+/*
+ * msgbuff.h - netlink message buffer
+ *
+ * Declarations of netlink message buffer and related functions.
+ */
+
+#ifndef ETHTOOL_NETLINK_MSGBUFF_H__
+#define ETHTOOL_NETLINK_MSGBUFF_H__
+
+#include <string.h>
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+
+struct nl_context;
+
+/**
+ * struct nl_msg_buff - message buffer abstraction
+ * @buff: pointer to buffer
+ * @size: total size of allocated buffer
+ * @left: remaining length current message end to end of buffer
+ * @nlhdr: pointer to netlink header of current message
+ * @genlhdr: pointer to genetlink header of current message
+ * @payload: pointer to message payload (after genetlink header)
+ */
+struct nl_msg_buff {
+ char *buff;
+ unsigned int size;
+ unsigned int left;
+ struct nlmsghdr *nlhdr;
+ struct genlmsghdr *genlhdr;
+ void *payload;
+};
+
+void msgbuff_init(struct nl_msg_buff *msgbuff);
+void msgbuff_done(struct nl_msg_buff *msgbuff);
+int msgbuff_realloc(struct nl_msg_buff *msgbuff, unsigned int new_size);
+int msgbuff_append(struct nl_msg_buff *dest, struct nl_msg_buff *src);
+
+int __msg_init(struct nl_msg_buff *msgbuff, int family, int cmd,
+ unsigned int flags, int version);
+int msg_init(struct nl_context *nlctx, struct nl_msg_buff *msgbuff, int cmd,
+ unsigned int flags);
+
+bool ethnla_put(struct nl_msg_buff *msgbuff, uint16_t type, size_t len,
+ const void *data);
+struct nlattr *ethnla_nest_start(struct nl_msg_buff *msgbuff, uint16_t type);
+bool ethnla_fill_header(struct nl_msg_buff *msgbuff, uint16_t type,
+ const char *devname, uint32_t flags);
+
+/* length of current message */
+static inline unsigned int msgbuff_len(const struct nl_msg_buff *msgbuff)
+{
+ return msgbuff->nlhdr->nlmsg_len;
+}
+
+/* reset message length to position returned by msgbuff_len() */
+static inline void msgbuff_reset(const struct nl_msg_buff *msgbuff,
+ unsigned int len)
+{
+ msgbuff->nlhdr->nlmsg_len = len;
+}
+
+/* put data wrappers */
+
+static inline void ethnla_nest_end(struct nl_msg_buff *msgbuff,
+ struct nlattr *nest)
+{
+ mnl_attr_nest_end(msgbuff->nlhdr, nest);
+}
+
+static inline void ethnla_nest_cancel(struct nl_msg_buff *msgbuff,
+ struct nlattr *nest)
+{
+ mnl_attr_nest_cancel(msgbuff->nlhdr, nest);
+}
+
+static inline bool ethnla_put_u32(struct nl_msg_buff *msgbuff, uint16_t type,
+ uint32_t data)
+{
+ return ethnla_put(msgbuff, type, sizeof(uint32_t), &data);
+}
+
+static inline bool ethnla_put_u16(struct nl_msg_buff *msgbuff, uint16_t type,
+ uint16_t data)
+{
+ return ethnla_put(msgbuff, type, sizeof(uint16_t), &data);
+}
+
+static inline bool ethnla_put_u8(struct nl_msg_buff *msgbuff, uint16_t type,
+ uint8_t data)
+{
+ return ethnla_put(msgbuff, type, sizeof(uint8_t), &data);
+}
+
+static inline bool ethnla_put_flag(struct nl_msg_buff *msgbuff, uint16_t type,
+ bool val)
+{
+ if (val)
+ return ethnla_put(msgbuff, type, 0, &val);
+ else
+ return false;
+}
+
+static inline bool ethnla_put_bitfield32(struct nl_msg_buff *msgbuff,
+ uint16_t type, uint32_t value,
+ uint32_t selector)
+{
+ struct nla_bitfield32 val = {
+ .value = value,
+ .selector = selector,
+ };
+
+ return ethnla_put(msgbuff, type, sizeof(val), &val);
+}
+
+static inline bool ethnla_put_strz(struct nl_msg_buff *msgbuff, uint16_t type,
+ const char *data)
+{
+ return ethnla_put(msgbuff, type, strlen(data) + 1, data);
+}
+
+#endif /* ETHTOOL_NETLINK_MSGBUFF_H__ */
diff --git a/netlink/netlink.c b/netlink/netlink.c
new file mode 100644
index 0000000..ef0d825
--- /dev/null
+++ b/netlink/netlink.c
@@ -0,0 +1,527 @@
+/*
+ * netlink.c - basic infrastructure for netlink code
+ *
+ * Heart of the netlink interface implementation.
+ */
+
+#include <errno.h>
+
+#include "../internal.h"
+#include "netlink.h"
+#include "extapi.h"
+#include "msgbuff.h"
+#include "nlsock.h"
+#include "strset.h"
+
+/* Used as reply callback for requests where no reply is expected (e.g. most
+ * "set" type commands)
+ */
+int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data __maybe_unused)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+
+ fprintf(stderr, "received unexpected message: len=%u type=%u cmd=%u\n",
+ nlhdr->nlmsg_len, nlhdr->nlmsg_type, ghdr->cmd);
+ return MNL_CB_OK;
+}
+
+/* standard attribute parser callback; it fills provided array with pointers
+ * to attributes like kernel nla_parse(). We must expect to run on top of
+ * a newer kernel which may send attributes that we do not know (yet). Rather
+ * than treating them as an error, just ignore them.
+ */
+int attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct attr_tb_info *tb_info = data;
+ uint16_t type = mnl_attr_get_type(attr);
+
+ if (type <= tb_info->max_type)
+ tb_info->tb[type] = attr;
+
+ return MNL_CB_OK;
+}
+
+/* misc helpers */
+
+const char *get_dev_name(const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ if (!nest)
+ return NULL;
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0 || !tb[ETHTOOL_A_HEADER_DEV_NAME])
+ return "(none)";
+ return mnl_attr_get_str(tb[ETHTOOL_A_HEADER_DEV_NAME]);
+}
+
+int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname)
+{
+ const struct nlattr *tb[ETHTOOL_A_HEADER_MAX + 1] = {};
+ const struct nlattr *index_attr;
+ const struct nlattr *name_attr;
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ if (ifindex)
+ *ifindex = 0;
+ if (ifname)
+ memset(ifname, '\0', ALTIFNAMSIZ);
+
+ if (!nest)
+ return -EFAULT;
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ index_attr = tb[ETHTOOL_A_HEADER_DEV_INDEX];
+ name_attr = tb[ETHTOOL_A_HEADER_DEV_NAME];
+ if (ret < 0 || (ifindex && !index_attr) || (ifname && !name_attr))
+ return -EFAULT;
+
+ if (ifindex)
+ *ifindex = mnl_attr_get_u32(index_attr);
+ if (ifname) {
+ strncpy(ifname, mnl_attr_get_str(name_attr), ALTIFNAMSIZ);
+ if (ifname[ALTIFNAMSIZ - 1]) {
+ ifname[ALTIFNAMSIZ - 1] = '\0';
+ fprintf(stderr, "kernel device name too long: '%s'\n",
+ mnl_attr_get_str(name_attr));
+ return -EFAULT;
+ }
+ }
+ return 0;
+}
+
+/**
+ * netlink_cmd_check() - check support for netlink command
+ * @ctx: ethtool command context
+ * @cmd: netlink command id
+ * @devname: device name from user
+ * @allow_wildcard: wildcard dumps supported
+ *
+ * Check if command @cmd is known to be unsupported based on ops information
+ * from genetlink family id request. Set nlctx->ioctl_fallback if ethtool
+ * should fall back to ioctl, i.e. when we do not know in advance that
+ * netlink request is supported. Set nlctx->wildcard_unsupported if "*" was
+ * used as device name but the request does not support wildcards (on either
+ * side).
+ *
+ * Return: true if we know the netlink request is not supported and should
+ * fail (and possibly fall back) without actually sending it to kernel.
+ */
+bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd,
+ bool allow_wildcard)
+{
+ bool is_dump = !strcmp(ctx->devname, WILDCARD_DEVNAME);
+ uint32_t cap = is_dump ? GENL_CMD_CAP_DUMP : GENL_CMD_CAP_DO;
+ struct nl_context *nlctx = ctx->nlctx;
+
+ if (is_dump && !allow_wildcard) {
+ nlctx->wildcard_unsupported = true;
+ return true;
+ }
+ if (!nlctx->ops_info) {
+ nlctx->ioctl_fallback = true;
+ return false;
+ }
+ if (cmd > ETHTOOL_MSG_USER_MAX || !nlctx->ops_info[cmd].op_flags) {
+ nlctx->ioctl_fallback = true;
+ return true;
+ }
+
+ if (is_dump && !(nlctx->ops_info[cmd].op_flags & GENL_CMD_CAP_DUMP))
+ nlctx->wildcard_unsupported = true;
+
+ return !(nlctx->ops_info[cmd].op_flags & cap);
+}
+
+struct ethtool_op_policy_query_ctx {
+ struct nl_context *nlctx;
+ unsigned int op;
+ unsigned int op_hdr_attr;
+
+ bool op_policy_found;
+ bool hdr_policy_found;
+ unsigned int op_policy_idx;
+ unsigned int hdr_policy_idx;
+ uint64_t flag_mask;
+};
+
+static int family_policy_find_op(struct ethtool_op_policy_query_ctx *policy_ctx,
+ const struct nlattr *op_policy)
+{
+ const struct nlattr *attr;
+ unsigned int type;
+ int ret;
+
+ type = policy_ctx->nlctx->is_dump ?
+ CTRL_ATTR_POLICY_DUMP : CTRL_ATTR_POLICY_DO;
+
+ mnl_attr_for_each_nested(attr, op_policy) {
+ const struct nlattr *tb[CTRL_ATTR_POLICY_DUMP_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+
+ if (mnl_attr_get_type(attr) != policy_ctx->op)
+ continue;
+
+ ret = mnl_attr_parse_nested(attr, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ if (!tb[type])
+ continue;
+
+ policy_ctx->op_policy_found = true;
+ policy_ctx->op_policy_idx = mnl_attr_get_u32(tb[type]);
+ break;
+ }
+
+ return 0;
+}
+
+static int family_policy_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tba[NL_POLICY_TYPE_ATTR_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tba);
+ const struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct ethtool_op_policy_query_ctx *policy_ctx = data;
+ const struct nlattr *policy_attr, *attr_attr, *attr;
+ unsigned int attr_idx, policy_idx;
+ int ret;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+
+ if (!policy_ctx->op_policy_found) {
+ if (!tb[CTRL_ATTR_OP_POLICY]) {
+ fprintf(stderr, "Error: op policy map not present\n");
+ return MNL_CB_ERROR;
+ }
+ ret = family_policy_find_op(policy_ctx, tb[CTRL_ATTR_OP_POLICY]);
+ return ret < 0 ? MNL_CB_ERROR : MNL_CB_OK;
+ }
+
+ if (!tb[CTRL_ATTR_POLICY])
+ return MNL_CB_OK;
+
+ policy_attr = mnl_attr_get_payload(tb[CTRL_ATTR_POLICY]);
+ policy_idx = mnl_attr_get_type(policy_attr);
+ attr_attr = mnl_attr_get_payload(policy_attr);
+ attr_idx = mnl_attr_get_type(attr_attr);
+
+ ret = mnl_attr_parse_nested(attr_attr, attr_cb, &tba_info);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+
+ if (policy_idx == policy_ctx->op_policy_idx &&
+ attr_idx == policy_ctx->op_hdr_attr) {
+ attr = tba[NL_POLICY_TYPE_ATTR_POLICY_IDX];
+ if (!attr) {
+ fprintf(stderr, "Error: no policy index in what was expected to be ethtool header attribute\n");
+ return MNL_CB_ERROR;
+ }
+ policy_ctx->hdr_policy_found = true;
+ policy_ctx->hdr_policy_idx = mnl_attr_get_u32(attr);
+ }
+
+ if (policy_ctx->hdr_policy_found &&
+ policy_ctx->hdr_policy_idx == policy_idx &&
+ attr_idx == ETHTOOL_A_HEADER_FLAGS) {
+ attr = tba[NL_POLICY_TYPE_ATTR_MASK];
+ if (!attr) {
+ fprintf(stderr, "Error: validation mask not reported for ethtool header flags\n");
+ return MNL_CB_ERROR;
+ }
+
+ policy_ctx->flag_mask = mnl_attr_get_u64(attr);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int read_flags_policy(struct nl_context *nlctx, struct nl_socket *nlsk,
+ unsigned int nlcmd, unsigned int hdrattr)
+{
+ struct ethtool_op_policy_query_ctx policy_ctx;
+ struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+ int ret;
+
+ if (nlctx->ops_info[nlcmd].hdr_policy_loaded)
+ return 0;
+
+ memset(&policy_ctx, 0, sizeof(policy_ctx));
+ policy_ctx.nlctx = nlctx;
+ policy_ctx.op = nlcmd;
+ policy_ctx.op_hdr_attr = hdrattr;
+
+ ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETPOLICY,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, 1);
+ if (ret < 0)
+ return ret;
+ ret = -EMSGSIZE;
+ if (ethnla_put_u16(msgbuff, CTRL_ATTR_FAMILY_ID, nlctx->ethnl_fam))
+ return ret;
+ if (ethnla_put_u32(msgbuff, CTRL_ATTR_OP, nlcmd))
+ return ret;
+
+ nlsock_sendmsg(nlsk, NULL);
+ nlsock_process_reply(nlsk, family_policy_cb, &policy_ctx);
+
+ nlctx->ops_info[nlcmd].hdr_policy_loaded = 1;
+ nlctx->ops_info[nlcmd].hdr_flags = policy_ctx.flag_mask;
+ return 0;
+}
+
+u32 get_stats_flag(struct nl_context *nlctx, unsigned int nlcmd,
+ unsigned int hdrattr)
+{
+ if (!nlctx->ctx->show_stats)
+ return 0;
+ if (nlcmd > ETHTOOL_MSG_USER_MAX ||
+ !(nlctx->ops_info[nlcmd].op_flags & GENL_CMD_CAP_HASPOL))
+ return 0;
+
+ if (read_flags_policy(nlctx, nlctx->ethnl_socket, nlcmd, hdrattr) < 0)
+ return 0;
+
+ return nlctx->ops_info[nlcmd].hdr_flags & ETHTOOL_FLAG_STATS;
+}
+
+/* initialization */
+
+static int genl_read_ops(struct nl_context *nlctx,
+ const struct nlattr *ops_attr)
+{
+ struct nl_op_info *ops_info;
+ struct nlattr *op_attr;
+ int ret;
+
+ ops_info = calloc(__ETHTOOL_MSG_USER_CNT, sizeof(ops_info[0]));
+ if (!ops_info)
+ return -ENOMEM;
+
+ mnl_attr_for_each_nested(op_attr, ops_attr) {
+ const struct nlattr *tb[CTRL_ATTR_OP_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ uint32_t op_id;
+
+ ret = mnl_attr_parse_nested(op_attr, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+
+ if (!tb[CTRL_ATTR_OP_ID] || !tb[CTRL_ATTR_OP_FLAGS])
+ continue;
+ op_id = mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID]);
+ if (op_id >= __ETHTOOL_MSG_USER_CNT)
+ continue;
+
+ ops_info[op_id].op_flags =
+ mnl_attr_get_u32(tb[CTRL_ATTR_OP_FLAGS]);
+ }
+
+ nlctx->ops_info = ops_info;
+ return 0;
+err:
+ free(ops_info);
+ return ret;
+}
+
+static void find_mc_group(struct nl_context *nlctx, struct nlattr *nest)
+{
+ const struct nlattr *grp_tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(grp_tb);
+ struct nlattr *grp_attr;
+ int ret;
+
+ mnl_attr_for_each_nested(grp_attr, nest) {
+ ret = mnl_attr_parse_nested(grp_attr, attr_cb, &grp_tb_info);
+ if (ret < 0)
+ return;
+ if (!grp_tb[CTRL_ATTR_MCAST_GRP_NAME] ||
+ !grp_tb[CTRL_ATTR_MCAST_GRP_ID])
+ continue;
+ if (strcmp(mnl_attr_get_str(grp_tb[CTRL_ATTR_MCAST_GRP_NAME]),
+ ETHTOOL_MCGRP_MONITOR_NAME))
+ continue;
+ nlctx->ethnl_mongrp =
+ mnl_attr_get_u32(grp_tb[CTRL_ATTR_MCAST_GRP_ID]);
+ return;
+ }
+}
+
+static int __maybe_unused family_info_cb(const struct nlmsghdr *nlhdr,
+ void *data)
+{
+ struct nl_context *nlctx = data;
+ struct nlattr *attr;
+ int ret;
+
+ mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
+ switch (mnl_attr_get_type(attr)) {
+ case CTRL_ATTR_FAMILY_ID:
+ nlctx->ethnl_fam = mnl_attr_get_u16(attr);
+ break;
+ case CTRL_ATTR_OPS:
+ ret = genl_read_ops(nlctx, attr);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+ break;
+ case CTRL_ATTR_MCAST_GROUPS:
+ find_mc_group(nlctx, attr);
+ break;
+ }
+ }
+
+ return MNL_CB_OK;
+}
+
+#ifdef TEST_ETHTOOL
+static int get_genl_family(struct nl_context *nlctx __maybe_unused,
+ struct nl_socket *nlsk __maybe_unused)
+{
+ return 0;
+}
+#else
+static int get_genl_family(struct nl_context *nlctx, struct nl_socket *nlsk)
+{
+ struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+ int ret;
+
+ nlctx->suppress_nlerr = 2;
+ ret = __msg_init(msgbuff, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
+ NLM_F_REQUEST | NLM_F_ACK, 1);
+ if (ret < 0)
+ goto out;
+ ret = -EMSGSIZE;
+ if (ethnla_put_strz(msgbuff, CTRL_ATTR_FAMILY_NAME, ETHTOOL_GENL_NAME))
+ goto out;
+
+ nlsock_sendmsg(nlsk, NULL);
+ nlsock_process_reply(nlsk, family_info_cb, nlctx);
+ ret = nlctx->ethnl_fam ? 0 : -EADDRNOTAVAIL;
+
+out:
+ nlctx->suppress_nlerr = 0;
+ return ret;
+}
+#endif
+
+int netlink_init(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx;
+ int ret;
+
+ nlctx = calloc(1, sizeof(*nlctx));
+ if (!nlctx)
+ return -ENOMEM;
+ nlctx->ctx = ctx;
+ ret = nlsock_init(nlctx, &nlctx->ethnl_socket, NETLINK_GENERIC);
+ if (ret < 0)
+ goto out_free;
+ ret = get_genl_family(nlctx, nlctx->ethnl_socket);
+ if (ret < 0)
+ goto out_nlsk;
+
+ ctx->nlctx = nlctx;
+ return 0;
+
+out_nlsk:
+ nlsock_done(nlctx->ethnl_socket);
+out_free:
+ free(nlctx->ops_info);
+ free(nlctx);
+ return ret;
+}
+
+static void netlink_done(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+
+ if (!nlctx)
+ return;
+
+ nlsock_done(nlctx->ethnl_socket);
+ nlsock_done(nlctx->ethnl2_socket);
+ nlsock_done(nlctx->rtnl_socket);
+ free(nlctx->ops_info);
+ free(nlctx);
+ ctx->nlctx = NULL;
+ cleanup_all_strings();
+}
+
+/**
+ * netlink_run_handler() - run netlink handler for subcommand
+ * @ctx: command context
+ * @nlchk: netlink capability check
+ * @nlfunc: subcommand netlink handler to call
+ * @no_fallback: there is no ioctl fallback handler
+ *
+ * This function returns only if ioctl() handler should be run as fallback.
+ * Otherwise it exits with appropriate return code.
+ */
+void netlink_run_handler(struct cmd_context *ctx, nl_chk_t nlchk,
+ nl_func_t nlfunc, bool no_fallback)
+{
+ bool wildcard = ctx->devname && !strcmp(ctx->devname, WILDCARD_DEVNAME);
+ bool wildcard_unsupported, ioctl_fallback;
+ struct nl_context *nlctx;
+ const char *reason;
+ int ret;
+
+ if (nlchk && !nlchk(ctx)) {
+ reason = "ioctl-only request";
+ goto no_support;
+ }
+ if (ctx->devname && strlen(ctx->devname) >= ALTIFNAMSIZ) {
+ fprintf(stderr, "device name '%s' longer than %u characters\n",
+ ctx->devname, ALTIFNAMSIZ - 1);
+ exit(1);
+ }
+
+ if (!nlfunc) {
+ reason = "ethtool netlink support for subcommand missing";
+ goto no_support;
+ }
+ if (netlink_init(ctx)) {
+ reason = "netlink interface initialization failed";
+ goto no_support;
+ }
+ nlctx = ctx->nlctx;
+
+ ret = nlfunc(ctx);
+ wildcard_unsupported = nlctx->wildcard_unsupported;
+ ioctl_fallback = nlctx->ioctl_fallback;
+ netlink_done(ctx);
+
+ if (no_fallback || ret != -EOPNOTSUPP || !ioctl_fallback) {
+ if (wildcard_unsupported)
+ fprintf(stderr, "%s\n",
+ "subcommand does not support wildcard dump");
+ exit(ret >= 0 ? ret : 1);
+ }
+ if (wildcard_unsupported)
+ reason = "subcommand does not support wildcard dump";
+ else
+ reason = "kernel netlink support for subcommand missing";
+
+no_support:
+ if (no_fallback) {
+ fprintf(stderr, "%s, subcommand not supported by ioctl\n",
+ reason);
+ exit(1);
+ }
+ if (wildcard) {
+ fprintf(stderr, "%s, wildcard dump not supported\n", reason);
+ exit(1);
+ }
+ if (ctx->devname && strlen(ctx->devname) >= IFNAMSIZ) {
+ fprintf(stderr,
+ "%s, device name longer than %u not supported\n",
+ reason, IFNAMSIZ - 1);
+ exit(1);
+ }
+
+ /* fallback to ioctl() */
+}
diff --git a/netlink/netlink.h b/netlink/netlink.h
new file mode 100644
index 0000000..1274a3b
--- /dev/null
+++ b/netlink/netlink.h
@@ -0,0 +1,178 @@
+/*
+ * netlink.h - common interface for all netlink code
+ *
+ * Declarations of data structures, global data and helpers for netlink code
+ */
+
+#ifndef ETHTOOL_NETLINK_INT_H__
+#define ETHTOOL_NETLINK_INT_H__
+
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#include <linux/ethtool_netlink.h>
+#include "nlsock.h"
+
+#define WILDCARD_DEVNAME "*"
+#define CMDMASK_WORDS DIV_ROUND_UP(__ETHTOOL_MSG_KERNEL_CNT, 32)
+
+enum link_mode_class {
+ LM_CLASS_UNKNOWN,
+ LM_CLASS_REAL,
+ LM_CLASS_AUTONEG,
+ LM_CLASS_PORT,
+ LM_CLASS_PAUSE,
+ LM_CLASS_FEC,
+};
+
+struct nl_op_info {
+ uint32_t op_flags;
+ uint32_t hdr_flags;
+ uint8_t hdr_policy_loaded:1;
+};
+
+struct nl_context {
+ struct cmd_context *ctx;
+ void *cmd_private;
+ const char *devname;
+ bool is_dump;
+ int exit_code;
+ unsigned int suppress_nlerr;
+ uint16_t ethnl_fam;
+ uint32_t ethnl_mongrp;
+ struct nl_op_info *ops_info;
+ struct nl_socket *ethnl_socket;
+ struct nl_socket *ethnl2_socket;
+ struct nl_socket *rtnl_socket;
+ bool is_monitor;
+ uint32_t filter_cmds[CMDMASK_WORDS];
+ const char *filter_devname;
+ bool no_banner;
+ const char *cmd;
+ const char *param;
+ char **argp;
+ unsigned int argc;
+ bool ioctl_fallback;
+ bool wildcard_unsupported;
+};
+
+struct attr_tb_info {
+ const struct nlattr **tb;
+ unsigned int max_type;
+};
+
+#define DECLARE_ATTR_TB_INFO(tbl) \
+ struct attr_tb_info tbl ## _info = { (tbl), (MNL_ARRAY_SIZE(tbl) - 1) }
+
+int nomsg_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int attr_cb(const struct nlattr *attr, void *data);
+
+int netlink_init(struct cmd_context *ctx);
+bool netlink_cmd_check(struct cmd_context *ctx, unsigned int cmd,
+ bool allow_wildcard);
+const char *get_dev_name(const struct nlattr *nest);
+int get_dev_info(const struct nlattr *nest, int *ifindex, char *ifname);
+u32 get_stats_flag(struct nl_context *nlctx, unsigned int nlcmd,
+ unsigned int hdrattr);
+
+int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int wol_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int debug_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int features_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int privflags_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int rings_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int channels_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int coalesce_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int pause_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int eee_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int cable_test_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int cable_test_ntf_cb(const struct nlmsghdr *nlhdr, void *data);
+int cable_test_tdr_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int cable_test_tdr_ntf_cb(const struct nlmsghdr *nlhdr, void *data);
+int fec_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int module_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+
+/* dump helpers */
+
+int dump_link_modes(struct nl_context *nlctx, const struct nlattr *bitset,
+ bool mask, unsigned int class, const char *before,
+ const char *between, const char *after,
+ const char *if_none);
+
+static inline void show_u32(const char *key,
+ const char *fmt,
+ const struct nlattr *attr)
+{
+ if (is_json_context()) {
+ if (attr)
+ print_uint(PRINT_JSON, key, NULL,
+ mnl_attr_get_u32(attr));
+ } else {
+ if (attr)
+ printf("%s%u\n", fmt, mnl_attr_get_u32(attr));
+ else
+ printf("%sn/a\n", fmt);
+ }
+}
+
+static inline const char *u8_to_bool(const uint8_t *val)
+{
+ if (val)
+ return *val ? "on" : "off";
+ else
+ return "n/a";
+}
+
+static inline void show_bool_val(const char *key, const char *fmt, uint8_t *val)
+{
+ if (is_json_context()) {
+ if (val)
+ print_bool(PRINT_JSON, key, NULL, *val);
+ } else {
+ print_string(PRINT_FP, NULL, fmt, u8_to_bool(val));
+ }
+}
+
+static inline void show_bool(const char *key, const char *fmt,
+ const struct nlattr *attr)
+{
+ show_bool_val(key, fmt, attr ? mnl_attr_get_payload(attr) : NULL);
+}
+
+static inline void show_cr(void)
+{
+ if (!is_json_context())
+ putchar('\n');
+}
+
+/* misc */
+
+static inline void copy_devname(char *dst, const char *src)
+{
+ strncpy(dst, src, ALTIFNAMSIZ);
+ dst[ALTIFNAMSIZ - 1] = '\0';
+}
+
+static inline bool dev_ok(const struct nl_context *nlctx)
+{
+ return !nlctx->filter_devname ||
+ (nlctx->devname &&
+ !strcmp(nlctx->devname, nlctx->filter_devname));
+}
+
+static inline int netlink_init_ethnl2_socket(struct nl_context *nlctx)
+{
+ if (nlctx->ethnl2_socket)
+ return 0;
+ return nlsock_init(nlctx, &nlctx->ethnl2_socket, NETLINK_GENERIC);
+}
+
+static inline int netlink_init_rtnl_socket(struct nl_context *nlctx)
+{
+ if (nlctx->rtnl_socket)
+ return 0;
+ return nlsock_init(nlctx, &nlctx->rtnl_socket, NETLINK_ROUTE);
+}
+
+#endif /* ETHTOOL_NETLINK_INT_H__ */
diff --git a/netlink/nlsock.c b/netlink/nlsock.c
new file mode 100644
index 0000000..0ec2738
--- /dev/null
+++ b/netlink/nlsock.c
@@ -0,0 +1,405 @@
+/*
+ * nlsock.c - netlink socket
+ *
+ * Data structure and code for netlink socket abstraction.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include "../internal.h"
+#include "nlsock.h"
+#include "netlink.h"
+#include "prettymsg.h"
+
+#define NLSOCK_RECV_BUFFSIZE 65536
+
+static void ctrl_msg_summary(const struct nlmsghdr *nlhdr)
+{
+ const struct nlmsgerr *nlerr;
+
+ switch (nlhdr->nlmsg_type) {
+ case NLMSG_NOOP:
+ printf(" noop\n");
+ break;
+ case NLMSG_ERROR:
+ printf(" error");
+ if (nlhdr->nlmsg_len < NLMSG_HDRLEN + sizeof(*nlerr)) {
+ printf(" malformed\n");
+ break;
+ }
+ nlerr = mnl_nlmsg_get_payload(nlhdr);
+ printf(" errno=%d\n", nlerr->error);
+ break;
+ case NLMSG_DONE:
+ printf(" done\n");
+ break;
+ case NLMSG_OVERRUN:
+ printf(" overrun\n");
+ break;
+ default:
+ printf(" type %u\n", nlhdr->nlmsg_type);
+ break;
+ }
+}
+
+static void genl_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam,
+ bool outgoing, bool pretty)
+{
+ if (nlhdr->nlmsg_type == ethnl_fam) {
+ const struct pretty_nlmsg_desc *msg_desc;
+ const struct genlmsghdr *ghdr;
+ unsigned int n_desc;
+
+ printf(" ethool");
+ if (nlhdr->nlmsg_len < NLMSG_HDRLEN + GENL_HDRLEN) {
+ printf(" malformed\n");
+ return;
+ }
+ ghdr = mnl_nlmsg_get_payload(nlhdr);
+
+ msg_desc = outgoing ? ethnl_umsg_desc : ethnl_kmsg_desc;
+ n_desc = outgoing ? ethnl_umsg_n_desc : ethnl_kmsg_n_desc;
+ if (ghdr->cmd < n_desc && msg_desc[ghdr->cmd].name)
+ printf(" %s", msg_desc[ghdr->cmd].name);
+ else
+ printf(" cmd %u", ghdr->cmd);
+ fputc('\n', stdout);
+
+ if (pretty)
+ pretty_print_genlmsg(nlhdr, msg_desc, n_desc, 0);
+ return;
+ }
+
+ if (nlhdr->nlmsg_type == GENL_ID_CTRL) {
+ printf(" genl-ctrl\n");
+ if (pretty)
+ pretty_print_genlmsg(nlhdr, genlctrl_msg_desc,
+ genlctrl_msg_n_desc, 0);
+ } else {
+ fputc('\n', stdout);
+ if (pretty)
+ pretty_print_genlmsg(nlhdr, NULL, 0, 0);
+ }
+}
+
+static void rtnl_msg_summary(const struct nlmsghdr *nlhdr, bool pretty)
+{
+ unsigned int type = nlhdr->nlmsg_type;
+
+ if (type < rtnl_msg_n_desc && rtnl_msg_desc[type].name)
+ printf(" %s\n", rtnl_msg_desc[type].name);
+ else
+ printf(" type %u\n", type);
+
+ if (pretty)
+ pretty_print_rtnlmsg(nlhdr, 0);
+}
+
+static void debug_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam,
+ int nl_fam, bool outgoing, bool pretty)
+{
+ printf(" msg length %u", nlhdr->nlmsg_len);
+
+ if (nlhdr->nlmsg_type < NLMSG_MIN_TYPE) {
+ ctrl_msg_summary(nlhdr);
+ return;
+ }
+
+ switch(nl_fam) {
+ case NETLINK_GENERIC:
+ genl_msg_summary(nlhdr, ethnl_fam, outgoing, pretty);
+ break;
+ case NETLINK_ROUTE:
+ rtnl_msg_summary(nlhdr, pretty);
+ break;
+ default:
+ fputc('\n', stdout);
+ break;
+ }
+}
+
+static void debug_msg(struct nl_socket *nlsk, const void *msg, unsigned int len,
+ bool outgoing)
+{
+ const char *dirlabel = outgoing ? "sending" : "received";
+ uint32_t debug = nlsk->nlctx->ctx->debug;
+ const struct nlmsghdr *nlhdr = msg;
+ bool summary, dump, pretty;
+ const char *nl_fam_label;
+ int left = len;
+
+ summary = debug_on(debug, DEBUG_NL_MSGS);
+ dump = debug_on(debug,
+ outgoing ? DEBUG_NL_DUMP_SND : DEBUG_NL_DUMP_RCV);
+ pretty = debug_on(debug, DEBUG_NL_PRETTY_MSG);
+ if (!summary && !dump)
+ return;
+ switch(nlsk->nl_fam) {
+ case NETLINK_GENERIC:
+ nl_fam_label = "genetlink";
+ break;
+ case NETLINK_ROUTE:
+ nl_fam_label = "rtnetlink";
+ break;
+ default:
+ nl_fam_label = "netlink";
+ break;
+ }
+ printf("%s %s packet (%u bytes):\n", dirlabel, nl_fam_label, len);
+
+ while (nlhdr && left > 0 && mnl_nlmsg_ok(nlhdr, left)) {
+ if (summary)
+ debug_msg_summary(nlhdr, nlsk->nlctx->ethnl_fam,
+ nlsk->nl_fam, outgoing, pretty);
+ if (dump)
+ mnl_nlmsg_fprintf(stdout, nlhdr, nlhdr->nlmsg_len,
+ GENL_HDRLEN);
+
+ nlhdr = mnl_nlmsg_next(nlhdr, &left);
+ }
+}
+
+/**
+ * nlsock_process_ack() - process NLMSG_ERROR message from kernel
+ * @nlhdr: pointer to netlink header
+ * @len: length of received data (from nlhdr to end of buffer)
+ * @suppress_nlerr: 0 show all errors, 1 silence -EOPNOTSUPP, 2 silence all
+ *
+ * Return: error code extracted from the message
+ */
+static int nlsock_process_ack(struct nlmsghdr *nlhdr, unsigned long len,
+ unsigned int suppress_nlerr, bool pretty)
+{
+ const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned int err_offset = 0;
+ unsigned int tlv_offset;
+ struct nlmsgerr *nlerr;
+ bool silent;
+
+ if ((len < NLMSG_HDRLEN + sizeof(*nlerr)) || (len < nlhdr->nlmsg_len))
+ return -EFAULT;
+ nlerr = mnl_nlmsg_get_payload(nlhdr);
+ silent = suppress_nlerr >= 2 ||
+ (suppress_nlerr && nlerr->error == -EOPNOTSUPP);
+ if (silent || !(nlhdr->nlmsg_flags & NLM_F_ACK_TLVS))
+ goto tlv_done;
+
+ tlv_offset = sizeof(*nlerr);
+ if (!(nlhdr->nlmsg_flags & NLM_F_CAPPED))
+ tlv_offset += MNL_ALIGN(mnl_nlmsg_get_payload_len(&nlerr->msg));
+ if (mnl_attr_parse(nlhdr, tlv_offset, attr_cb, &tb_info) < 0)
+ goto tlv_done;
+
+ if (tb[NLMSGERR_ATTR_MSG]) {
+ const char *msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
+
+ fprintf(stderr, "netlink %s: %s",
+ nlerr->error ? "error" : "warning", msg);
+ if (!pretty && tb[NLMSGERR_ATTR_OFFS])
+ fprintf(stderr, " (offset %u)",
+ mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]));
+ fputc('\n', stderr);
+ }
+ if (tb[NLMSGERR_ATTR_OFFS])
+ err_offset = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
+
+tlv_done:
+ if (nlerr->error && !silent) {
+ errno = -nlerr->error;
+ perror("netlink error");
+ }
+ if (pretty && !(nlhdr->nlmsg_flags & NLM_F_CAPPED) &&
+ nlhdr->nlmsg_len >= NLMSG_HDRLEN + nlerr->msg.nlmsg_len) {
+ fprintf(stderr, "offending message%s:\n",
+ err_offset ? " and attribute" : "");
+ pretty_print_genlmsg(&nlerr->msg, ethnl_umsg_desc,
+ ethnl_umsg_n_desc, err_offset);
+ }
+ return nlerr->error;
+}
+
+/**
+ * nlsock_process_reply() - process reply packet(s) from kernel
+ * @nlsk: netlink socket to read from
+ * @reply_cb: callback to process each message
+ * @data: pointer passed as argument to @reply_cb callback
+ *
+ * Read packets from kernel and pass reply messages to @reply_cb callback
+ * until an error is encountered or NLMSG_ERR message is received. In the
+ * latter case, return value is the error code extracted from it.
+ *
+ * Return: 0 on success or negative error code
+ */
+int nlsock_process_reply(struct nl_socket *nlsk, mnl_cb_t reply_cb, void *data)
+{
+ struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+ struct nlmsghdr *nlhdr;
+ ssize_t len;
+ char *buff;
+ int ret;
+
+ ret = msgbuff_realloc(msgbuff, NLSOCK_RECV_BUFFSIZE);
+ if (ret < 0)
+ return ret;
+ buff = msgbuff->buff;
+
+ do {
+ len = mnl_socket_recvfrom(nlsk->sk, buff, msgbuff->size);
+ if (len <= 0)
+ return (len ? -EFAULT : 0);
+ debug_msg(nlsk, buff, len, false);
+ if (len < NLMSG_HDRLEN)
+ return -EFAULT;
+
+ nlhdr = (struct nlmsghdr *)buff;
+ if (nlhdr->nlmsg_type == NLMSG_ERROR) {
+ unsigned int suppress = nlsk->nlctx->suppress_nlerr;
+ bool pretty;
+
+ pretty = debug_on(nlsk->nlctx->ctx->debug,
+ DEBUG_NL_PRETTY_MSG);
+ return nlsock_process_ack(nlhdr, len, suppress, pretty);
+ }
+
+ msgbuff->nlhdr = nlhdr;
+ msgbuff->genlhdr = mnl_nlmsg_get_payload(nlhdr);
+ msgbuff->payload =
+ mnl_nlmsg_get_payload_offset(nlhdr, GENL_HDRLEN);
+ ret = mnl_cb_run(buff, len, nlsk->seq, nlsk->port, reply_cb,
+ data);
+ } while (ret > 0);
+
+ return ret;
+}
+
+int nlsock_prep_get_request(struct nl_socket *nlsk, unsigned int nlcmd,
+ uint16_t hdr_attrtype, u32 flags)
+{
+ unsigned int nlm_flags = NLM_F_REQUEST | NLM_F_ACK;
+ struct nl_context *nlctx = nlsk->nlctx;
+ const char *devname = nlctx->ctx->devname;
+ int ret;
+
+ if (devname && !strcmp(devname, WILDCARD_DEVNAME)) {
+ devname = NULL;
+ nlm_flags |= NLM_F_DUMP;
+ }
+ nlctx->is_dump = !devname;
+
+ ret = msg_init(nlctx, &nlsk->msgbuff, nlcmd, nlm_flags);
+ if (ret < 0)
+ return ret;
+ if (ethnla_fill_header(&nlsk->msgbuff, hdr_attrtype, devname, flags))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+#ifndef TEST_ETHTOOL
+/**
+ * nlsock_sendmsg() - send a netlink message to kernel
+ * @nlsk: netlink socket
+ * @altbuff: alternative message buffer; if null, use buffer embedded in @nlsk
+ *
+ * Return: sent size or negative error code
+ */
+ssize_t nlsock_sendmsg(struct nl_socket *nlsk, struct nl_msg_buff *altbuff)
+{
+ struct nl_msg_buff *msgbuff = altbuff ?: &nlsk->msgbuff;
+ struct nlmsghdr *nlhdr = msgbuff->nlhdr;
+
+ nlhdr->nlmsg_seq = ++nlsk->seq;
+ debug_msg(nlsk, msgbuff->buff, nlhdr->nlmsg_len, true);
+ return mnl_socket_sendto(nlsk->sk, nlhdr, nlhdr->nlmsg_len);
+}
+#endif
+
+/**
+ * nlsock_send_get_request() - send request and process reply
+ * @nlsk: netlink socket
+ * @cb: callback to process reply message(s)
+ *
+ * This simple helper only handles the most common case when the embedded
+ * message buffer is sent and @cb takes netlink context (struct nl_context)
+ * as last argument.
+ */
+int nlsock_send_get_request(struct nl_socket *nlsk, mnl_cb_t cb)
+{
+ int ret;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ goto err;
+ ret = nlsock_process_reply(nlsk, cb, nlsk->nlctx);
+ if (ret == 0)
+ return 0;
+err:
+ return nlsk->nlctx->exit_code ?: 1;
+}
+
+/**
+ * nlsock_init() - allocate and initialize netlink socket
+ * @nlctx: netlink context
+ * @__nlsk: store pointer to the allocated socket here
+ * @nl_fam: netlink family (e.g. NETLINK_GENERIC or NETLINK_ROUTE)
+ *
+ * Allocate and initialize netlink socket and its embedded message buffer.
+ * Cleans up on error, caller is responsible for destroying the socket with
+ * nlsock_done() on success.
+ *
+ * Return: 0 on success or negative error code
+ */
+int nlsock_init(struct nl_context *nlctx, struct nl_socket **__nlsk, int nl_fam)
+{
+ struct nl_socket *nlsk;
+ int val;
+ int ret;
+
+ nlsk = calloc(1, sizeof(*nlsk));
+ if (!nlsk)
+ return -ENOMEM;
+ nlsk->nlctx = nlctx;
+ msgbuff_init(&nlsk->msgbuff);
+
+ ret = -ECONNREFUSED;
+ nlsk->sk = mnl_socket_open(nl_fam);
+ if (!nlsk->sk)
+ goto out_msgbuff;
+ val = 1;
+ mnl_socket_setsockopt(nlsk->sk, NETLINK_EXT_ACK, &val, sizeof(val));
+ ret = mnl_socket_bind(nlsk->sk, 0, MNL_SOCKET_AUTOPID);
+ if (ret < 0)
+ goto out_close;
+ nlsk->port = mnl_socket_get_portid(nlsk->sk);
+ nlsk->nl_fam = nl_fam;
+
+ *__nlsk = nlsk;
+ return 0;
+
+out_close:
+ if (nlsk->sk)
+ mnl_socket_close(nlsk->sk);
+out_msgbuff:
+ msgbuff_done(&nlsk->msgbuff);
+ free(nlsk);
+ return ret;
+}
+
+/**
+ * nlsock_done() - destroy a netlink socket
+ * @nlsk: netlink socket
+ *
+ * Close the socket and free the structure and related data.
+ */
+void nlsock_done(struct nl_socket *nlsk)
+{
+ if (!nlsk)
+ return;
+ if (nlsk->sk)
+ mnl_socket_close(nlsk->sk);
+ msgbuff_done(&nlsk->msgbuff);
+ memset(nlsk, '\0', sizeof(*nlsk));
+ free(nlsk);
+}
diff --git a/netlink/nlsock.h b/netlink/nlsock.h
new file mode 100644
index 0000000..b015f86
--- /dev/null
+++ b/netlink/nlsock.h
@@ -0,0 +1,45 @@
+/*
+ * nlsock.h - netlink socket
+ *
+ * Declarations of netlink socket structure and related functions.
+ */
+
+#ifndef ETHTOOL_NETLINK_NLSOCK_H__
+#define ETHTOOL_NETLINK_NLSOCK_H__
+
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#include <linux/ethtool_netlink.h>
+#include "msgbuff.h"
+
+struct nl_context;
+
+/**
+ * struct nl_socket - netlink socket abstraction
+ * @nlctx: netlink context
+ * @sk: libmnl socket handle
+ * @msgbuff: embedded message buffer used by default
+ * @port: port number for netlink header
+ * @seq: autoincremented sequence number for netlink header
+ * @nl_fam: netlink family (e.g. NETLINK_GENERIC or NETLINK_ROUTE)
+ */
+struct nl_socket {
+ struct nl_context *nlctx;
+ struct mnl_socket *sk;
+ struct nl_msg_buff msgbuff;
+ unsigned int port;
+ unsigned int seq;
+ int nl_fam;
+};
+
+int nlsock_init(struct nl_context *nlctx, struct nl_socket **__nlsk,
+ int nl_fam);
+void nlsock_done(struct nl_socket *nlsk);
+int nlsock_prep_get_request(struct nl_socket *nlsk, unsigned int nlcmd,
+ uint16_t hdr_attrtype, u32 flags);
+ssize_t nlsock_sendmsg(struct nl_socket *nlsk, struct nl_msg_buff *__msgbuff);
+int nlsock_send_get_request(struct nl_socket *nlsk, mnl_cb_t cb);
+int nlsock_process_reply(struct nl_socket *nlsk, mnl_cb_t reply_cb, void *data);
+
+#endif /* ETHTOOL_NETLINK_NLSOCK_H__ */
diff --git a/netlink/parser.c b/netlink/parser.c
new file mode 100644
index 0000000..6f86361
--- /dev/null
+++ b/netlink/parser.c
@@ -0,0 +1,1141 @@
+/*
+ * parser.c - netlink command line parser
+ *
+ * Implementation of command line parser used by netlink code.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+static void parser_err_unknown_param(struct nl_context *nlctx)
+{
+ fprintf(stderr, "ethtool (%s): unknown parameter '%s'\n", nlctx->cmd,
+ nlctx->param);
+}
+
+static void parser_err_dup_param(struct nl_context *nlctx)
+{
+ fprintf(stderr, "ethtool (%s): duplicate parameter '%s'\n", nlctx->cmd,
+ nlctx->param);
+}
+
+static void parser_err_min_argc(struct nl_context *nlctx, unsigned int min_argc)
+{
+ if (min_argc == 1)
+ fprintf(stderr, "ethtool (%s): no value for parameter '%s'\n",
+ nlctx->cmd, nlctx->param);
+ else
+ fprintf(stderr,
+ "ethtool (%s): parameter '%s' requires %u words\n",
+ nlctx->cmd, nlctx->param, min_argc);
+}
+
+static void parser_err_invalid_value(struct nl_context *nlctx, const char *val)
+{
+ fprintf(stderr, "ethtool (%s): invalid value '%s' for parameter '%s'\n",
+ nlctx->cmd, val, nlctx->param);
+}
+
+static void parser_err_invalid_flag(struct nl_context *nlctx, const char *flag)
+{
+ fprintf(stderr, "ethtool (%s): flag '%s' for parameter '%s' is not followed by 'on' or 'off'\n",
+ nlctx->cmd, flag, nlctx->param);
+}
+
+static bool __prefix_0x(const char *p)
+{
+ return p[0] == '0' && (p[1] == 'x' || p[1] == 'X');
+}
+
+static float parse_float(const char *arg, float *result, float min,
+ float max)
+{
+ char *endptr;
+ float val;
+
+ if (!arg || !arg[0])
+ return -EINVAL;
+ val = strtof(arg, &endptr);
+ if (*endptr || val < min || val > max)
+ return -EINVAL;
+
+ *result = val;
+ return 0;
+}
+
+static int __parse_u32(const char *arg, uint32_t *result, uint32_t min,
+ uint32_t max, int base)
+{
+ unsigned long long val;
+ char *endptr;
+
+ if (!arg || !arg[0])
+ return -EINVAL;
+ val = strtoul(arg, &endptr, base);
+ if (*endptr || val < min || val > max)
+ return -EINVAL;
+
+ *result = (uint32_t)val;
+ return 0;
+}
+
+static int parse_u32d(const char *arg, uint32_t *result)
+{
+ return __parse_u32(arg, result, 0, 0xffffffff, 10);
+}
+
+static int parse_x32(const char *arg, uint32_t *result)
+{
+ return __parse_u32(arg, result, 0, 0xffffffff, 16);
+}
+
+int parse_u32(const char *arg, uint32_t *result)
+{
+ if (!arg)
+ return -EINVAL;
+ if (__prefix_0x(arg))
+ return parse_x32(arg + 2, result);
+ else
+ return parse_u32d(arg, result);
+}
+
+static int parse_u8(const char *arg, uint8_t *result)
+{
+ uint32_t val;
+ int ret = parse_u32(arg, &val);
+
+ if (ret < 0)
+ return ret;
+ if (val > UINT8_MAX)
+ return -EINVAL;
+
+ *result = (uint8_t)val;
+ return 0;
+}
+
+static int lookup_u32(const char *arg, uint32_t *result,
+ const struct lookup_entry_u32 *tbl)
+{
+ if (!arg)
+ return -EINVAL;
+ while (tbl->arg) {
+ if (!strcmp(tbl->arg, arg)) {
+ *result = tbl->val;
+ return 0;
+ }
+ tbl++;
+ }
+
+ return -EINVAL;
+}
+
+static int lookup_u8(const char *arg, uint8_t *result,
+ const struct lookup_entry_u8 *tbl)
+{
+ if (!arg)
+ return -EINVAL;
+ while (tbl->arg) {
+ if (!strcmp(tbl->arg, arg)) {
+ *result = tbl->val;
+ return 0;
+ }
+ tbl++;
+ }
+
+ return -EINVAL;
+}
+
+/* Parser handler for a flag. Expects a name (with no additional argument),
+ * generates NLA_FLAG or sets a bool (if the name was present).
+ */
+int nl_parse_flag(struct nl_context *nlctx __maybe_unused, uint16_t type,
+ const void *data __maybe_unused, struct nl_msg_buff *msgbuff,
+ void *dest)
+{
+ if (dest)
+ *(bool *)dest = true;
+ return (type && ethnla_put_flag(msgbuff, type, true)) ? -EMSGSIZE : 0;
+}
+
+/* Parser handler for null terminated string. Expects a string argument,
+ * generates NLA_NUL_STRING or fills const char *
+ */
+int nl_parse_string(struct nl_context *nlctx, uint16_t type,
+ const void *data __maybe_unused,
+ struct nl_msg_buff *msgbuff, void *dest)
+{
+ const char *arg = *nlctx->argp;
+
+ nlctx->argp++;
+ nlctx->argc--;
+
+ if (dest)
+ *(const char **)dest = arg;
+ return (type && ethnla_put_strz(msgbuff, type, arg)) ? -EMSGSIZE : 0;
+}
+
+/* Parser handler for unsigned 32-bit integer. Expects a numeric argument
+ * (may use 0x prefix), generates NLA_U32 or fills an uint32_t.
+ */
+int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
+ const void *data __maybe_unused,
+ struct nl_msg_buff *msgbuff, void *dest)
+{
+ const char *arg = *nlctx->argp;
+ uint32_t val;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ ret = parse_u32(arg, &val);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+
+ if (dest)
+ *(uint32_t *)dest = val;
+ return (type && ethnla_put_u32(msgbuff, type, val)) ? -EMSGSIZE : 0;
+}
+
+/* Parser handler for unsigned 32-bit integer. Expects a numeric argument
+ * (may use 0x prefix), generates NLA_U32 or fills an uint32_t.
+ */
+int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
+ const void *data __maybe_unused,
+ struct nl_msg_buff *msgbuff, void *dest)
+{
+ const char *arg = *nlctx->argp;
+ uint8_t val;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ ret = parse_u8(arg, &val);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+
+ if (dest)
+ *(uint8_t *)dest = val;
+ return (type && ethnla_put_u8(msgbuff, type, val)) ? -EMSGSIZE : 0;
+}
+
+/* Parser handler for float meters and convert it to cm. Generates
+ * NLA_U32 or fills an uint32_t.
+ */
+int nl_parse_direct_m2cm(struct nl_context *nlctx, uint16_t type,
+ const void *data __maybe_unused,
+ struct nl_msg_buff *msgbuff, void *dest)
+{
+ const char *arg = *nlctx->argp;
+ float meters = 0.0;
+ uint32_t cm;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ ret = parse_float(arg, &meters, 0, 150);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+
+ cm = (uint32_t)(meters * 100 + 0.5);
+ if (dest)
+ *(uint32_t *)dest = cm;
+ return (type && ethnla_put_u32(msgbuff, type, cm)) ? -EMSGSIZE : 0;
+}
+
+/* Parser handler for (tri-state) bool. Expects "name on|off", generates
+ * NLA_U8 which is 1 for "on" and 0 for "off".
+ */
+int nl_parse_u8bool(struct nl_context *nlctx, uint16_t type,
+ const void *data __maybe_unused,
+ struct nl_msg_buff *msgbuff, void *dest)
+{
+ const char *arg = *nlctx->argp;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ if (!strcmp(arg, "on")) {
+ if (dest)
+ *(uint8_t *)dest = 1;
+ ret = type ? ethnla_put_u8(msgbuff, type, 1) : 0;
+ } else if (!strcmp(arg, "off")) {
+ if (dest)
+ *(uint8_t *)dest = 0;
+ ret = type ? ethnla_put_u8(msgbuff, type, 0) : 0;
+ } else {
+ parser_err_invalid_value(nlctx, arg);
+ return -EINVAL;
+ }
+
+ return ret ? -EMSGSIZE : 0;
+}
+
+/* Parser handler for 32-bit lookup value. Expects a string argument, looks it
+ * up in a table, generates NLA_U32 or fills uint32_t variable. The @data
+ * parameter is a null terminated array of struct lookup_entry_u32.
+ */
+int nl_parse_lookup_u32(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest)
+{
+ const char *arg = *nlctx->argp;
+ uint32_t val;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ ret = lookup_u32(arg, &val, data);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+
+ if (dest)
+ *(uint32_t *)dest = val;
+ return (type && ethnla_put_u32(msgbuff, type, val)) ? -EMSGSIZE : 0;
+}
+
+/* Parser handler for 8-bit lookup value. Expects a string argument, looks it
+ * up in a table, generates NLA_U8 or fills uint8_t variable. The @data
+ * parameter is a null terminated array of struct lookup_entry_u8.
+ */
+int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest)
+{
+ const char *arg = *nlctx->argp;
+ uint8_t val;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ ret = lookup_u8(arg, &val, data);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+
+ if (dest)
+ *(uint8_t *)dest = val;
+ return (type && ethnla_put_u8(msgbuff, type, val)) ? -EMSGSIZE : 0;
+}
+
+/* number of significant bits */
+static unsigned int __nsb(uint32_t x)
+{
+ unsigned int ret = 0;
+
+ if (x & 0xffff0000U) {
+ x >>= 16;
+ ret += 16;
+ }
+ if (x & 0xff00U) {
+ x >>= 8;
+ ret += 8;
+ }
+ if (x & 0xf0U) {
+ x >>= 4;
+ ret += 4;
+ }
+ if (x & 0xcU) {
+ x >>= 2;
+ ret += 2;
+ }
+ if (x & 0x2U) {
+ x >>= 1;
+ ret += 1;
+ }
+
+ return ret + x;
+}
+
+static bool __is_hex(char c)
+{
+ if (isdigit(c))
+ return true;
+ else
+ return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
+
+static unsigned int __hex_val(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 0xa;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 0xa;
+ return 0;
+}
+
+static bool __bytestr_delim(const char *p, char delim)
+{
+ return !*p || (delim ? (*p == delim) : !__is_hex(*p));
+}
+
+/* Parser handler for generic byte string in MAC-like format. Expects string
+ * argument in the "[[:xdigit:]]{2}(:[[:xdigit:]]{2})*" format, generates
+ * NLA_BINARY or fills a struct byte_str_value (if @dest is not null and the
+ * handler succeeds, caller is responsible for freeing the value). The @data
+ * parameter points to struct byte_str_parser_data.
+ */
+int nl_parse_byte_str(struct nl_context *nlctx, uint16_t type, const void *data,
+ struct nl_msg_buff *msgbuff, void *dest)
+{
+ const struct byte_str_parser_data *pdata = data;
+ struct byte_str_value *dest_value = dest;
+ const char *arg = *nlctx->argp;
+ uint8_t *val = NULL;
+ unsigned int len, i;
+ const char *p;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+
+ len = 0;
+ p = arg;
+ if (!*p)
+ goto err;
+ while (true) {
+ len++;
+ if (!__bytestr_delim(p, pdata->delim))
+ p++;
+ if (!__bytestr_delim(p, pdata->delim))
+ p++;
+ if (!__bytestr_delim(p, pdata->delim))
+ goto err;
+ if (!*p)
+ break;
+ p++;
+ if (*p && __bytestr_delim(p, pdata->delim))
+ goto err;
+ }
+ if (len < pdata->min_len || (pdata->max_len && len > pdata->max_len))
+ goto err;
+ val = malloc(len);
+ if (!val)
+ return -ENOMEM;
+
+ p = arg;
+ for (i = 0; i < len; i++) {
+ uint8_t byte = 0;
+
+ if (!__is_hex(*p))
+ goto err;
+ while (__is_hex(*p))
+ byte = 16 * byte + __hex_val(*p++);
+ if (!__bytestr_delim(p, pdata->delim))
+ goto err;
+ val[i] = byte;
+ if (*p)
+ p++;
+ }
+ ret = type ? ethnla_put(msgbuff, type, len, val) : 0;
+ if (dest) {
+ dest_value->len = len;
+ dest_value->data = val;
+ } else {
+ free(val);
+ }
+ return ret;
+
+err:
+ free(val);
+ fprintf(stderr, "ethtool (%s): invalid value '%s' of parameter '%s'\n",
+ nlctx->cmd, arg, nlctx->param);
+ return -EINVAL;
+}
+
+/* Parser handler for parameters recognized for backward compatibility but
+ * supposed to fail without passing to kernel. Does not generate any netlink
+ * attributes of fill any variable. The @data parameter points to struct
+ * error_parser_params (error message, return value and number of extra
+ * arguments to skip).
+ */
+int nl_parse_error(struct nl_context *nlctx, uint16_t type __maybe_unused,
+ const void *data, struct nl_msg_buff *msgbuff __maybe_unused,
+ void *dest __maybe_unused)
+{
+ const struct error_parser_data *parser_data = data;
+ unsigned int skip = parser_data->extra_args;
+
+ fprintf(stderr, "ethtool (%s): ", nlctx->cmd);
+ fprintf(stderr, parser_data->err_msg, nlctx->param);
+ if (nlctx->argc < skip) {
+ fprintf(stderr, "ethtool (%s): too few arguments for parameter '%s' (expected %u)\n",
+ nlctx->cmd, nlctx->param, skip);
+ } else {
+ nlctx->argp += skip;
+ nlctx->argc -= skip;
+ }
+
+ return parser_data->ret_val;
+}
+
+/* bitset parser handlers */
+
+/* Return true if a bitset argument should be parsed as numeric, i.e.
+ * (a) it starts with '0x'
+ * (b) it consists only of hex digits and at most one slash which can be
+ * optionally followed by "0x"; if no_mask is true, slash is not allowed
+ */
+static bool is_numeric_bitset(const char *arg, bool no_mask)
+{
+ const char *p = arg;
+ bool has_slash = false;
+
+ if (!arg)
+ return false;
+ if (__prefix_0x(arg))
+ return true;
+ while (*p) {
+ if (*p == '/') {
+ if (has_slash || no_mask)
+ return false;
+ has_slash = true;
+ p++;
+ if (__prefix_0x(p))
+ p += 2;
+ continue;
+ }
+ if (!__is_hex(*p))
+ return false;
+ p++;
+ }
+ return true;
+}
+
+#define __MAX_U32_DIGITS 10
+
+/* Parse hex string (without leading "0x") into a bitmap consisting of 32-bit
+ * words. Caller must make sure arg is at least len characters long and dst has
+ * place for at least (len + 7) / 8 32-bit words. If force_hex is false, allow
+ * also base 10 unsigned 32-bit value.
+ *
+ * Returns number of significant bits in the bitmap on success and negative
+ * value on error.
+ */
+static int __parse_num_string(const char *arg, unsigned int len, uint32_t *dst,
+ bool force_hex)
+{
+ char buff[__MAX_U32_DIGITS + 1] = {};
+ unsigned int nbits = 0;
+ const char *p = arg;
+
+ if (!len)
+ return -EINVAL;
+ if (!force_hex && len <= __MAX_U32_DIGITS) {
+ strncpy(buff, arg, len);
+ if (!buff[__MAX_U32_DIGITS]) {
+ u32 val;
+ int ret;
+
+ ret = parse_u32d(buff, &val);
+ if (!ret) {
+ *dst = val;
+ return __nsb(val);
+ }
+ }
+ }
+
+ dst += (len - 1) / 8;
+ while (len > 0) {
+ unsigned int chunk = (len % 8) ?: 8;
+ unsigned long val;
+ char *endp;
+
+ memcpy(buff, p, chunk);
+ buff[chunk] = '\0';
+ val = strtoul(buff, &endp, 16);
+ if (*endp)
+ return -EINVAL;
+ *dst-- = (uint32_t)val;
+ if (nbits)
+ nbits += 4 * chunk;
+ else
+ nbits = __nsb(val);
+
+ p += chunk;
+ len -= chunk;
+ }
+ return nbits;
+}
+
+/* Parse bitset provided as a base 16 numeric value (@no_mask is true) or pair
+ * of base 16 numeric values separated by '/' (@no_mask is false). The "0x"
+ * prefix is optional. Generates bitset nested attribute in compact form.
+ */
+static int parse_numeric_bitset(struct nl_context *nlctx, uint16_t type,
+ bool no_mask, bool force_hex,
+ struct nl_msg_buff *msgbuff)
+{
+ unsigned int nwords, len1, len2;
+ const char *arg = *nlctx->argp;
+ bool force_hex1 = force_hex;
+ bool force_hex2 = force_hex;
+ uint32_t *value = NULL;
+ uint32_t *mask = NULL;
+ struct nlattr *nest;
+ const char *maskptr;
+ int ret = 0;
+ int nbits;
+
+ if (__prefix_0x(arg)) {
+ force_hex1 = true;
+ arg += 2;
+ }
+
+ maskptr = strchr(arg, '/');
+ if (maskptr && no_mask) {
+ parser_err_invalid_value(nlctx, arg);
+ return -EINVAL;
+ }
+ len1 = maskptr ? (unsigned int)(maskptr - arg) : strlen(arg);
+ nwords = DIV_ROUND_UP(len1, 8);
+ nbits = 0;
+
+ if (maskptr) {
+ maskptr++;
+ if (__prefix_0x(maskptr)) {
+ maskptr += 2;
+ force_hex2 = true;
+ }
+ len2 = strlen(maskptr);
+ if (len2 > len1)
+ nwords = DIV_ROUND_UP(len2, 8);
+ mask = calloc(nwords, sizeof(uint32_t));
+ if (!mask)
+ return -ENOMEM;
+ ret = __parse_num_string(maskptr, strlen(maskptr), mask,
+ force_hex2);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ goto out_free;
+ }
+ nbits = ret;
+ }
+
+ value = calloc(nwords, sizeof(uint32_t));
+ if (!value) {
+ free(mask);
+ return -ENOMEM;
+ }
+ ret = __parse_num_string(arg, len1, value, force_hex1);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ goto out_free;
+ }
+ nbits = (nbits < ret) ? ret : nbits;
+ nwords = (nbits + 31) / 32;
+
+ ret = 0;
+ if (!type)
+ goto out_free;
+ ret = -EMSGSIZE;
+ nest = ethnla_nest_start(msgbuff, type);
+ if (!nest)
+ goto out_free;
+ if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, !mask) ||
+ ethnla_put_u32(msgbuff, ETHTOOL_A_BITSET_SIZE, nbits) ||
+ ethnla_put(msgbuff, ETHTOOL_A_BITSET_VALUE,
+ nwords * sizeof(uint32_t), value) ||
+ (mask &&
+ ethnla_put(msgbuff, ETHTOOL_A_BITSET_MASK,
+ nwords * sizeof(uint32_t), mask)))
+ goto out_free;
+ ethnla_nest_end(msgbuff, nest);
+ ret = 0;
+
+out_free:
+ free(value);
+ free(mask);
+ nlctx->argp++;
+ nlctx->argc--;
+ return ret;
+}
+
+/* Parse bitset provided as series of "name on|off" pairs (@no_mask is false)
+ * or names (@no_mask is true). Generates bitset nested attribute in verbose
+ * form with names from command line.
+ */
+static int parse_name_bitset(struct nl_context *nlctx, uint16_t type,
+ bool no_mask, struct nl_msg_buff *msgbuff)
+{
+ struct nlattr *bitset_attr;
+ struct nlattr *bits_attr;
+ struct nlattr *bit_attr;
+ int ret;
+
+ bitset_attr = ethnla_nest_start(msgbuff, type);
+ if (!bitset_attr)
+ return -EMSGSIZE;
+ ret = -EMSGSIZE;
+ if (no_mask && ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true))
+ goto err;
+ bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS);
+ if (!bits_attr)
+ goto err;
+
+ while (nlctx->argc > 0) {
+ bool bit_val = true;
+
+ if (!strcmp(*nlctx->argp, "--")) {
+ nlctx->argp++;
+ nlctx->argc--;
+ break;
+ }
+ ret = -EINVAL;
+ if (!no_mask) {
+ if (nlctx->argc < 2 ||
+ (strcmp(nlctx->argp[1], "on") &&
+ strcmp(nlctx->argp[1], "off"))) {
+ parser_err_invalid_flag(nlctx, *nlctx->argp);
+ goto err;
+ }
+ bit_val = !strcmp(nlctx->argp[1], "on");
+ }
+
+ ret = -EMSGSIZE;
+ bit_attr = ethnla_nest_start(msgbuff,
+ ETHTOOL_A_BITSET_BITS_BIT);
+ if (!bit_attr)
+ goto err;
+ if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME,
+ nlctx->argp[0]))
+ goto err;
+ if (!no_mask &&
+ ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE,
+ bit_val))
+ goto err;
+ ethnla_nest_end(msgbuff, bit_attr);
+
+ nlctx->argp += (no_mask ? 1 : 2);
+ nlctx->argc -= (no_mask ? 1 : 2);
+ }
+
+ ethnla_nest_end(msgbuff, bits_attr);
+ ethnla_nest_end(msgbuff, bitset_attr);
+ return 0;
+err:
+ ethnla_nest_cancel(msgbuff, bitset_attr);
+ return ret;
+}
+
+static bool is_char_bitset(const char *arg,
+ const struct char_bitset_parser_data *data)
+{
+ bool mask = (arg[0] == '+' || arg[0] == '-');
+ unsigned int i;
+ const char *p;
+
+ for (p = arg; *p; p++) {
+ if (*p == data->reset_char)
+ continue;
+ if (mask && (*p == '+' || *p == '-'))
+ continue;
+ for (i = 0; i < data->nbits; i++)
+ if (*p == data->bit_chars[i])
+ goto found;
+ return false;
+found:
+ ;
+ }
+
+ return true;
+}
+
+/* Parse bitset provided as a string consisting of characters corresponding to
+ * bit indices. The "reset character" resets the no-mask bitset to empty. If
+ * the first character is '+' or '-', generated bitset has mask and '+' and
+ * '-' switch between enabling and disabling the following bits (i.e. their
+ * value being true/false). In such case, "reset character" is not allowed.
+ */
+static int parse_char_bitset(struct nl_context *nlctx, uint16_t type,
+ const struct char_bitset_parser_data *data,
+ struct nl_msg_buff *msgbuff)
+{
+ const char *arg = *nlctx->argp;
+ struct nlattr *bitset_attr;
+ struct nlattr *saved_pos;
+ struct nlattr *bits_attr;
+ struct nlattr *bit_attr;
+ unsigned int idx;
+ bool val = true;
+ const char *p;
+ bool no_mask;
+ int ret;
+
+ no_mask = data->no_mask || !(arg[0] == '+' || arg[0] == '-');
+ bitset_attr = ethnla_nest_start(msgbuff, type);
+ if (!bitset_attr)
+ return -EMSGSIZE;
+ ret = -EMSGSIZE;
+ if (no_mask && ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true))
+ goto err;
+ bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS);
+ if (!bits_attr)
+ goto err;
+ saved_pos = mnl_nlmsg_get_payload_tail(msgbuff->nlhdr);
+
+ for (p = arg; *p; p++) {
+ if (*p == '+' || *p == '-') {
+ if (no_mask) {
+ parser_err_invalid_value(nlctx, arg);
+ ret = -EINVAL;
+ goto err;
+ }
+ val = (*p == '+');
+ continue;
+ }
+ if (*p == data->reset_char) {
+ if (no_mask) {
+ mnl_attr_nest_cancel(msgbuff->nlhdr, saved_pos);
+ continue;
+ }
+ fprintf(stderr, "ethtool (%s): invalid char '%c' in '%s' for parameter '%s'\n",
+ nlctx->cmd, *p, arg, nlctx->param);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (idx = 0; idx < data->nbits; idx++) {
+ if (data->bit_chars[idx] == *p)
+ break;
+ }
+ if (idx >= data->nbits) {
+ fprintf(stderr, "ethtool (%s): invalid char '%c' in '%s' for parameter '%s'\n",
+ nlctx->cmd, *p, arg, nlctx->param);
+ ret = -EINVAL;
+ goto err;
+ }
+ bit_attr = ethnla_nest_start(msgbuff,
+ ETHTOOL_A_BITSET_BITS_BIT);
+ if (!bit_attr)
+ goto err;
+ if (ethnla_put_u32(msgbuff, ETHTOOL_A_BITSET_BIT_INDEX, idx))
+ goto err;
+ if (!no_mask &&
+ ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_BIT_VALUE, val))
+ goto err;
+ ethnla_nest_end(msgbuff, bit_attr);
+ }
+
+ ethnla_nest_end(msgbuff, bits_attr);
+ ethnla_nest_end(msgbuff, bitset_attr);
+ nlctx->argp++;
+ nlctx->argc--;
+ return 0;
+err:
+ ethnla_nest_cancel(msgbuff, bitset_attr);
+ return ret;
+}
+
+/* Parser handler for bitset. Expects either a numeric value (base 16 or 10
+ * (unless force_hex is set)), optionally followed by '/' and another numeric
+ * value (mask, unless no_mask is set), or a series of "name on|off" pairs
+ * (no_mask not set) or names (no_mask set). In the latter case, names are
+ * passed on as they are and kernel performs their interpretation and
+ * validation. The @data parameter points to struct bitset_parser_data.
+ * Generates only a bitset nested attribute. Fails if @type is zero or @dest
+ * is not null.
+ */
+int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data,
+ struct nl_msg_buff *msgbuff, void *dest)
+{
+ const struct bitset_parser_data *parser_data = data;
+
+ if (!type || dest) {
+ fprintf(stderr, "ethtool (%s): internal error parsing '%s'\n",
+ nlctx->cmd, nlctx->param);
+ return -EFAULT;
+ }
+ if (is_numeric_bitset(*nlctx->argp, false))
+ return parse_numeric_bitset(nlctx, type, parser_data->no_mask,
+ parser_data->force_hex, msgbuff);
+ else
+ return parse_name_bitset(nlctx, type, parser_data->no_mask,
+ msgbuff);
+}
+
+/* Parser handler for bitset. Expects either a numeric value (base 10 or 16),
+ * optionally followed by '/' and another numeric value (mask, unless no_mask
+ * is set), or a string consisting of characters corresponding to bit indices.
+ * The @data parameter points to struct char_bitset_parser_data. Generates
+ * biset nested attribute. Fails if type is zero or if @dest is not null.
+ */
+int nl_parse_char_bitset(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest)
+{
+ const struct char_bitset_parser_data *parser_data = data;
+
+ if (!type || dest) {
+ fprintf(stderr, "ethtool (%s): internal error parsing '%s'\n",
+ nlctx->cmd, nlctx->param);
+ return -EFAULT;
+ }
+ if (is_char_bitset(*nlctx->argp, data) ||
+ !is_numeric_bitset(*nlctx->argp, false))
+ return parse_char_bitset(nlctx, type, parser_data, msgbuff);
+ else
+ return parse_numeric_bitset(nlctx, type, parser_data->no_mask,
+ false, msgbuff);
+}
+
+/* parser implementation */
+
+static const struct param_parser *find_parser(const struct param_parser *params,
+ const char *arg)
+{
+ const struct param_parser *parser;
+
+ for (parser = params; parser->arg; parser++)
+ if (!strcmp(arg, parser->arg))
+ return parser;
+ return NULL;
+}
+
+static bool __parser_bit(const uint64_t *map, unsigned int idx)
+{
+ return map[idx / 64] & (1 << (idx % 64));
+}
+
+static void __parser_set(uint64_t *map, unsigned int idx)
+{
+ map[idx / 64] |= (1 << (idx % 64));
+}
+
+static void __parser_set_group(const struct param_parser *params,
+ uint64_t *map, unsigned int alt_group)
+{
+ const struct param_parser *parser;
+ unsigned int idx = 0;
+
+ for (parser = params; parser->arg; parser++, idx++)
+ if (parser->alt_group == alt_group)
+ __parser_set(map, idx);
+}
+
+struct tmp_buff {
+ struct nl_msg_buff *msgbuff;
+ unsigned int id;
+ unsigned int orig_len;
+ struct tmp_buff *next;
+};
+
+static struct tmp_buff *tmp_buff_find(struct tmp_buff *head, unsigned int id)
+{
+ struct tmp_buff *buff;
+
+ for (buff = head; buff; buff = buff->next)
+ if (buff->id == id)
+ break;
+
+ return buff;
+}
+
+static struct tmp_buff *tmp_buff_find_or_create(struct tmp_buff **phead,
+ unsigned int id)
+{
+ struct tmp_buff **pbuff;
+ struct tmp_buff *new_buff;
+
+ for (pbuff = phead; *pbuff; pbuff = &(*pbuff)->next)
+ if ((*pbuff)->id == id)
+ return *pbuff;
+
+ new_buff = malloc(sizeof(*new_buff));
+ if (!new_buff)
+ return NULL;
+ new_buff->id = id;
+ new_buff->msgbuff = malloc(sizeof(*new_buff->msgbuff));
+ if (!new_buff->msgbuff) {
+ free(new_buff);
+ return NULL;
+ }
+ msgbuff_init(new_buff->msgbuff);
+ new_buff->next = NULL;
+ *pbuff = new_buff;
+
+ return new_buff;
+}
+
+static void tmp_buff_destroy(struct tmp_buff *head)
+{
+ struct tmp_buff *buff = head;
+ struct tmp_buff *next;
+
+ while (buff) {
+ next = buff->next;
+ if (buff->msgbuff) {
+ msgbuff_done(buff->msgbuff);
+ free(buff->msgbuff);
+ }
+ free(buff);
+ buff = next;
+ }
+}
+
+/* Main entry point of parser implementation.
+ * @nlctx: netlink context
+ * @params: array of struct param_parser describing expected arguments
+ * and their handlers; the array must be terminated by null
+ * element {}
+ * @dest: optional destination to copy parsed data to (at
+ * param_parser::offset)
+ * @group_style: defines if identifiers in .group represent separate messages,
+ * nested attributes or are not allowed
+ * @msgbuffs: (only used for @group_style = PARSER_GROUP_MSG) array to store
+ * pointers to composed messages; caller must make sure this
+ * array is sufficient, i.e. that it has at least as many entries
+ * as the number of different .group values in params array;
+ * entries are filled from the start, remaining entries are not
+ * modified; caller should zero initialize the array before
+ * calling nl_parser()
+ */
+int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+ void *dest, enum parser_group_style group_style,
+ struct nl_msg_buff **msgbuffs)
+{
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ const struct param_parser *parser;
+ struct tmp_buff *buffs = NULL;
+ unsigned int n_msgbuffs = 0;
+ struct tmp_buff *buff;
+ unsigned int n_params;
+ uint64_t *params_seen;
+ int ret;
+
+ n_params = 0;
+ for (parser = params; parser->arg; parser++) {
+ struct nl_msg_buff *msgbuff;
+ struct nlattr *nest;
+
+ n_params++;
+ if (group_style == PARSER_GROUP_NONE || !parser->group)
+ continue;
+ ret = -ENOMEM;
+ buff = tmp_buff_find_or_create(&buffs, parser->group);
+ if (!buff)
+ goto out_free_buffs;
+ msgbuff = buff->msgbuff;
+ ret = msg_init(nlctx, msgbuff, parser->group,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ goto out_free_buffs;
+
+ switch (group_style) {
+ case PARSER_GROUP_NEST:
+ ret = -EMSGSIZE;
+ nest = ethnla_nest_start(buff->msgbuff, parser->group);
+ if (!nest)
+ goto out_free_buffs;
+ break;
+ case PARSER_GROUP_MSG:
+ if (ethnla_fill_header(msgbuff,
+ ETHTOOL_A_LINKINFO_HEADER,
+ nlctx->devname, 0))
+ goto out_free_buffs;
+ break;
+ default:
+ break;
+ }
+
+ buff->orig_len = msgbuff_len(msgbuff);
+ }
+ ret = -ENOMEM;
+ params_seen = calloc(DIV_ROUND_UP(n_params, 64), sizeof(uint64_t));
+ if (!params_seen)
+ goto out_free_buffs;
+
+ while (nlctx->argc > 0) {
+ struct nl_msg_buff *msgbuff;
+ void *param_dest;
+
+ nlctx->param = *nlctx->argp;
+ ret = -EINVAL;
+ parser = find_parser(params, nlctx->param);
+ if (!parser) {
+ parser_err_unknown_param(nlctx);
+ goto out_free;
+ }
+
+ /* check duplicates and minimum number of arguments */
+ if (__parser_bit(params_seen, parser - params)) {
+ parser_err_dup_param(nlctx);
+ goto out_free;
+ }
+ nlctx->argc--;
+ nlctx->argp++;
+ if (nlctx->argc < parser->min_argc) {
+ parser_err_min_argc(nlctx, parser->min_argc);
+ goto out_free;
+ }
+ if (parser->alt_group)
+ __parser_set_group(params, params_seen,
+ parser->alt_group);
+ else
+ __parser_set(params_seen, parser - params);
+
+ buff = NULL;
+ if (parser->group)
+ buff = tmp_buff_find(buffs, parser->group);
+ msgbuff = buff ? buff->msgbuff : &nlsk->msgbuff;
+
+ param_dest = dest ? ((char *)dest + parser->dest_offset) : NULL;
+ ret = parser->handler(nlctx, parser->type, parser->handler_data,
+ msgbuff, param_dest);
+ if (ret < 0)
+ goto out_free;
+ }
+
+ if (group_style == PARSER_GROUP_MSG) {
+ ret = -EOPNOTSUPP;
+ for (buff = buffs; buff; buff = buff->next)
+ if (msgbuff_len(buff->msgbuff) > buff->orig_len &&
+ netlink_cmd_check(nlctx->ctx, buff->id, false))
+ goto out_free;
+ }
+ for (buff = buffs; buff; buff = buff->next) {
+ struct nl_msg_buff *msgbuff = buff->msgbuff;
+
+ if (group_style == PARSER_GROUP_NONE ||
+ msgbuff_len(msgbuff) == buff->orig_len)
+ continue;
+ switch (group_style) {
+ case PARSER_GROUP_NEST:
+ ethnla_nest_end(msgbuff, msgbuff->payload);
+ ret = msgbuff_append(&nlsk->msgbuff, msgbuff);
+ if (ret < 0)
+ goto out_free;
+ break;
+ case PARSER_GROUP_MSG:
+ msgbuffs[n_msgbuffs++] = msgbuff;
+ buff->msgbuff = NULL;
+ break;
+ default:
+ break;
+ }
+ }
+
+ ret = 0;
+out_free:
+ free(params_seen);
+out_free_buffs:
+ tmp_buff_destroy(buffs);
+ return ret;
+}
diff --git a/netlink/parser.h b/netlink/parser.h
new file mode 100644
index 0000000..8a4e8af
--- /dev/null
+++ b/netlink/parser.h
@@ -0,0 +1,153 @@
+/*
+ * parser.h - netlink command line parser
+ *
+ * Interface for command line parser used by netlink code.
+ */
+
+#ifndef ETHTOOL_NETLINK_PARSER_H__
+#define ETHTOOL_NETLINK_PARSER_H__
+
+#include <stddef.h>
+
+#include "netlink.h"
+
+/* Some commands need to generate multiple netlink messages or multiple nested
+ * attributes from one set of command line parameters. Argument @group_style
+ * of nl_parser() takes one of three values:
+ *
+ * PARSER_GROUP_NONE - no grouping, flat series of attributes (default)
+ * PARSER_GROUP_NEST - one nest for each @group value (@group is nest type)
+ * PARSER_GROUP_MSG - one message for each @group value (@group is command)
+ */
+enum parser_group_style {
+ PARSER_GROUP_NONE = 0,
+ PARSER_GROUP_NEST,
+ PARSER_GROUP_MSG,
+};
+
+typedef int (*param_parser_cb_t)(struct nl_context *, uint16_t, const void *,
+ struct nl_msg_buff *, void *);
+
+struct param_parser {
+ /* command line parameter handled by this entry */
+ const char *arg;
+ /* group id (see enum parser_group_style above) */
+ unsigned int group;
+ /* netlink attribute type */
+ uint16_t type; /* netlink attribute type */
+ /* function called to parse parameter and its value */
+ param_parser_cb_t handler;
+ /* pointer passed as @data argument of the handler */
+ const void *handler_data;
+ /* minimum number of extra arguments after this parameter */
+ unsigned int min_argc;
+ /* if @dest is passed to nl_parser(), offset to store value */
+ unsigned int dest_offset;
+ /* parameter alternative group - only one parameter from a group
+ * can be specified, 0 means no group
+ */
+ unsigned int alt_group;
+};
+
+/* data structures used for handler data */
+
+/* used by nl_parse_lookup_u32() */
+struct lookup_entry_u32 {
+ const char *arg;
+ uint32_t val;
+};
+
+/* used by nl_parse_lookup_u8() */
+struct lookup_entry_u8 {
+ const char *arg;
+ uint8_t val;
+};
+
+/* used by nl_parse_byte_str() */
+struct byte_str_parser_data {
+ unsigned int min_len;
+ unsigned int max_len;
+ char delim;
+};
+
+/* used for value stored by nl_parse_byte_str() */
+struct byte_str_value {
+ unsigned int len;
+ u8 *data;
+};
+
+/* used by nl_parse_error() */
+struct error_parser_data {
+ const char *err_msg;
+ int ret_val;
+ unsigned int extra_args;
+};
+
+/* used by nl_parse_bitset() */
+struct bitset_parser_data {
+ bool force_hex;
+ bool no_mask;
+};
+
+/* used by nl_parse_char_bitset() */
+struct char_bitset_parser_data {
+ const char *bit_chars;
+ unsigned int nbits;
+ char reset_char;
+ bool no_mask;
+};
+
+int parse_u32(const char *arg, uint32_t *result);
+
+/* parser handlers to use as param_parser::handler */
+
+/* NLA_FLAG represented by on | off */
+int nl_parse_flag(struct nl_context *nlctx, uint16_t type, const void *data,
+ struct nl_msg_buff *msgbuff, void *dest);
+/* NLA_NUL_STRING represented by a string argument */
+int nl_parse_string(struct nl_context *nlctx, uint16_t type, const void *data,
+ struct nl_msg_buff *msgbuff, void *dest);
+/* NLA_U32 represented as numeric argument */
+int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest);
+/* NLA_U8 represented as numeric argument */
+int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest);
+/* NLA_U32 represented as float number of meters, converted to cm. */
+int nl_parse_direct_m2cm(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest);
+/* NLA_U8 represented as on | off */
+int nl_parse_u8bool(struct nl_context *nlctx, uint16_t type, const void *data,
+ struct nl_msg_buff *msgbuff, void *dest);
+/* NLA_U32 represented by a string argument (lookup table) */
+int nl_parse_lookup_u32(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest);
+/* NLA_U8 represented by a string argument (lookup table) */
+int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest);
+/* always fail (for deprecated parameters) */
+int nl_parse_error(struct nl_context *nlctx, uint16_t type, const void *data,
+ struct nl_msg_buff *msgbuff, void *dest);
+/* NLA_BINARY represented by %x:%x:...%x (e.g. a MAC address) */
+int nl_parse_byte_str(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest);
+/* bitset represented by %x[/%x] or name on|off ... [--] */
+int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data,
+ struct nl_msg_buff *msgbuff, void *dest);
+/* bitset represented by %u[/%u] or string (characters for bits) */
+int nl_parse_char_bitset(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest);
+
+/* main entry point called to parse the command line */
+int nl_parser(struct nl_context *nlctx, const struct param_parser *params,
+ void *dest, enum parser_group_style group_style,
+ struct nl_msg_buff **msgbuffs);
+
+#endif /* ETHTOOL_NETLINK_PARSER_H__ */
diff --git a/netlink/pause.c b/netlink/pause.c
new file mode 100644
index 0000000..da444bd
--- /dev/null
+++ b/netlink/pause.c
@@ -0,0 +1,331 @@
+/*
+ * pause.c - netlink implementation of pause commands
+ *
+ * Implementation of "ethtool -a <dev>" and "ethtool -A <dev> ..."
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* PAUSE_GET */
+
+struct pause_autoneg_status {
+ bool pause;
+ bool asym_pause;
+};
+
+static void pause_autoneg_walker(unsigned int idx,
+ const char *name __maybe_unused, bool val,
+ void *data)
+{
+ struct pause_autoneg_status *status = data;
+
+ if (idx == ETHTOOL_LINK_MODE_Pause_BIT)
+ status->pause = val;
+ if (idx == ETHTOOL_LINK_MODE_Asym_Pause_BIT)
+ status->asym_pause = val;
+}
+
+static int pause_autoneg_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct pause_autoneg_status ours = {};
+ struct pause_autoneg_status peer = {};
+ struct nl_context *nlctx = data;
+ uint8_t rx_status = false;
+ uint8_t tx_status = false;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ if (!tb[ETHTOOL_A_LINKMODES_OURS] || !tb[ETHTOOL_A_LINKMODES_PEER])
+ return MNL_CB_OK;
+ ret = walk_bitset(tb[ETHTOOL_A_LINKMODES_OURS], NULL,
+ pause_autoneg_walker, &ours);
+ if (ret < 0)
+ return err_ret;
+ ret = walk_bitset(tb[ETHTOOL_A_LINKMODES_PEER], NULL,
+ pause_autoneg_walker, &peer);
+ if (ret < 0)
+ return err_ret;
+
+ if (ours.pause && peer.pause) {
+ rx_status = true;
+ tx_status = true;
+ } else if (ours.asym_pause && peer.asym_pause) {
+ if (ours.pause)
+ rx_status = true;
+ else if (peer.pause)
+ tx_status = true;
+ }
+
+ open_json_object("negotiated");
+ show_bool_val("rx", "RX negotiated: %s\n", &rx_status);
+ show_bool_val("tx", "TX negotiated: %s\n", &tx_status);
+ close_json_object();
+
+ return MNL_CB_OK;
+}
+
+static int show_pause_autoneg_status(struct nl_context *nlctx)
+{
+ const char *saved_devname;
+ int ret;
+
+ saved_devname = nlctx->ctx->devname;
+ nlctx->ctx->devname = nlctx->devname;
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ goto out;
+
+ ret = nlsock_prep_get_request(nlctx->ethnl2_socket,
+ ETHTOOL_MSG_LINKMODES_GET,
+ ETHTOOL_A_LINKMODES_HEADER,
+ ETHTOOL_FLAG_COMPACT_BITSETS);
+ if (ret < 0)
+ goto out;
+ ret = nlsock_send_get_request(nlctx->ethnl2_socket, pause_autoneg_cb);
+
+out:
+ nlctx->ctx->devname = saved_devname;
+ return ret;
+}
+
+static int show_pause_stats(const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHTOOL_A_PAUSE_STAT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ static const struct {
+ unsigned int attr;
+ char *name;
+ } stats[] = {
+ { ETHTOOL_A_PAUSE_STAT_TX_FRAMES, "tx_pause_frames" },
+ { ETHTOOL_A_PAUSE_STAT_RX_FRAMES, "rx_pause_frames" },
+ };
+ bool header = false;
+ unsigned int i;
+ size_t n;
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ open_json_object("statistics");
+ for (i = 0; i < ARRAY_SIZE(stats); i++) {
+ char fmt[32];
+
+ if (!tb[stats[i].attr])
+ continue;
+
+ if (!header && !is_json_context()) {
+ printf("Statistics:\n");
+ header = true;
+ }
+
+ if (mnl_attr_validate(tb[stats[i].attr], MNL_TYPE_U64)) {
+ fprintf(stderr, "malformed netlink message (statistic)\n");
+ goto err_close_stats;
+ }
+
+ n = snprintf(fmt, sizeof(fmt), " %s: %%" PRIu64 "\n",
+ stats[i].name);
+ if (n >= sizeof(fmt)) {
+ fprintf(stderr, "internal error - malformed label\n");
+ goto err_close_stats;
+ }
+
+ print_u64(PRINT_ANY, stats[i].name, fmt,
+ mnl_attr_get_u64(tb[stats[i].attr]));
+ }
+ close_json_object();
+
+ return 0;
+
+err_close_stats:
+ close_json_object();
+ return -1;
+}
+
+int pause_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PAUSE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PAUSE_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ print_nl();
+
+ open_json_object(NULL);
+
+ print_string(PRINT_ANY, "ifname", "Pause parameters for %s:\n",
+ nlctx->devname);
+
+ show_bool("autonegotiate", "Autonegotiate:\t%s\n",
+ tb[ETHTOOL_A_PAUSE_AUTONEG]);
+ show_bool("rx", "RX:\t\t%s\n", tb[ETHTOOL_A_PAUSE_RX]);
+ show_bool("tx", "TX:\t\t%s\n", tb[ETHTOOL_A_PAUSE_TX]);
+
+ if (!nlctx->is_monitor && tb[ETHTOOL_A_PAUSE_AUTONEG] &&
+ mnl_attr_get_u8(tb[ETHTOOL_A_PAUSE_AUTONEG])) {
+ ret = show_pause_autoneg_status(nlctx);
+ if (ret < 0)
+ goto err_close_dev;
+ }
+ if (tb[ETHTOOL_A_PAUSE_STATS]) {
+ ret = show_pause_stats(tb[ETHTOOL_A_PAUSE_STATS]);
+ if (ret < 0)
+ goto err_close_dev;
+ }
+ if (!silent)
+ print_nl();
+
+ close_json_object();
+
+ return MNL_CB_OK;
+
+err_close_dev:
+ close_json_object();
+ return err_ret;
+}
+
+static const struct lookup_entry_u32 stats_src_values[] = {
+ { .arg = "aggregate", .val = ETHTOOL_MAC_STATS_SRC_AGGREGATE },
+ { .arg = "emac", .val = ETHTOOL_MAC_STATS_SRC_EMAC },
+ { .arg = "pmac", .val = ETHTOOL_MAC_STATS_SRC_PMAC },
+ {}
+};
+
+static const struct param_parser gpause_params[] = {
+ {
+ .arg = "--src",
+ .type = ETHTOOL_A_PAUSE_STATS_SRC,
+ .handler = nl_parse_lookup_u32,
+ .handler_data = stats_src_values,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_gpause(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ u32 flags;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PAUSE_GET, true))
+ return -EOPNOTSUPP;
+
+ flags = get_stats_flag(nlctx, ETHTOOL_MSG_PAUSE_GET,
+ ETHTOOL_A_PAUSE_HEADER);
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PAUSE_GET,
+ ETHTOOL_A_PAUSE_HEADER, flags);
+ if (ret < 0)
+ return ret;
+
+ nlctx->cmd = "-a";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+
+ ret = nl_parser(nlctx, gpause_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, pause_reply_cb);
+ delete_json_obj();
+ return ret;
+}
+
+/* PAUSE_SET */
+
+static const struct param_parser spause_params[] = {
+ {
+ .arg = "autoneg",
+ .type = ETHTOOL_A_PAUSE_AUTONEG,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx",
+ .type = ETHTOOL_A_PAUSE_RX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx",
+ .type = ETHTOOL_A_PAUSE_TX,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_spause(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PAUSE_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-A";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PAUSE_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_PAUSE_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, spause_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 76;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 76;
+}
diff --git a/netlink/permaddr.c b/netlink/permaddr.c
new file mode 100644
index 0000000..006eac6
--- /dev/null
+++ b/netlink/permaddr.c
@@ -0,0 +1,114 @@
+/*
+ * permaddr.c - netlink implementation of permanent address request
+ *
+ * Implementation of "ethtool -P <dev>"
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_link.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+
+/* PERMADDR_GET */
+
+static int permaddr_prep_request(struct nl_socket *nlsk)
+{
+ unsigned int nlm_flags = NLM_F_REQUEST | NLM_F_ACK;
+ struct nl_context *nlctx = nlsk->nlctx;
+ const char *devname = nlctx->ctx->devname;
+ struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+ struct ifinfomsg *ifinfo;
+ struct nlmsghdr *nlhdr;
+ int ret;
+
+ if (devname && !strcmp(devname, WILDCARD_DEVNAME)) {
+ devname = NULL;
+ nlm_flags |= NLM_F_DUMP;
+ }
+ nlctx->is_dump = !devname;
+
+ ret = msgbuff_realloc(msgbuff, MNL_SOCKET_BUFFER_SIZE);
+ if (ret < 0)
+ return ret;
+ memset(msgbuff->buff, '\0', NLMSG_HDRLEN + sizeof(*ifinfo));
+
+ nlhdr = mnl_nlmsg_put_header(msgbuff->buff);
+ nlhdr->nlmsg_type = RTM_GETLINK;
+ nlhdr->nlmsg_flags = nlm_flags;
+ msgbuff->nlhdr = nlhdr;
+ ifinfo = mnl_nlmsg_put_extra_header(nlhdr, sizeof(*ifinfo));
+
+ if (devname) {
+ uint16_t type = IFLA_IFNAME;
+
+ if (strlen(devname) >= IFNAMSIZ)
+ type = IFLA_ALT_IFNAME;
+ if (ethnla_put_strz(msgbuff, type, devname))
+ return -EMSGSIZE;
+ }
+ if (ethnla_put_u32(msgbuff, IFLA_EXT_MASK, RTEXT_FILTER_SKIP_STATS))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+int permaddr_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[__IFLA_MAX] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ const uint8_t *permaddr;
+ unsigned int i;
+ int ret;
+
+ if (nlhdr->nlmsg_type != RTM_NEWLINK)
+ goto err;
+ ret = mnl_attr_parse(nlhdr, sizeof(struct ifinfomsg), attr_cb,
+ &tb_info);
+ if (ret < 0 || !tb[IFLA_IFNAME])
+ goto err;
+ nlctx->devname = mnl_attr_get_str(tb[IFLA_IFNAME]);
+ if (!dev_ok(nlctx))
+ goto err;
+
+ if (!tb[IFLA_PERM_ADDRESS]) {
+ if (!nlctx->is_dump)
+ printf("Permanent address: not set\n");
+ return MNL_CB_OK;
+ }
+
+ if (nlctx->is_dump)
+ printf("Permanent address of %s:", nlctx->devname);
+ else
+ printf("Permanent address:");
+ permaddr = mnl_attr_get_payload(tb[IFLA_PERM_ADDRESS]);
+ for (i = 0; i < mnl_attr_get_payload_len(tb[IFLA_PERM_ADDRESS]); i++)
+ printf("%c%02x", i ? ':' : ' ', permaddr[i]);
+ putchar('\n');
+ return MNL_CB_OK;
+
+err:
+ if (nlctx->is_dump || nlctx->is_monitor)
+ return MNL_CB_OK;
+ nlctx->exit_code = 2;
+ return MNL_CB_ERROR;
+}
+
+int nl_permaddr(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ int ret;
+
+ ret = netlink_init_rtnl_socket(nlctx);
+ if (ret < 0)
+ return ret;
+ ret = permaddr_prep_request(nlctx->rtnl_socket);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlctx->rtnl_socket, permaddr_reply_cb);
+}
diff --git a/netlink/plca.c b/netlink/plca.c
new file mode 100644
index 0000000..7d61e3b
--- /dev/null
+++ b/netlink/plca.c
@@ -0,0 +1,296 @@
+/*
+ * plca.c - netlink implementation of plca command
+ *
+ * Implementation of "ethtool --show-plca <dev>" and
+ * "ethtool --set-plca <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* PLCA_GET_CFG */
+
+int plca_get_cfg_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int idv = 255;
+ int err_ret;
+ int val;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ putchar('\n');
+
+ printf("PLCA settings for %s:\n", nlctx->devname);
+
+ // check if PLCA is enabled
+ printf("\tEnabled: ");
+
+ if (!tb[ETHTOOL_A_PLCA_ENABLED]) {
+ printf("not supported");
+ } else {
+ val = mnl_attr_get_u8(tb[ETHTOOL_A_PLCA_ENABLED]);
+ printf(val ? "Yes" : "No");
+ }
+ putchar('\n');
+
+ // get node ID
+ printf("\tlocal node ID: ");
+
+ if (!tb[ETHTOOL_A_PLCA_NODE_ID]) {
+ printf("not supported");
+ } else {
+ idv = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_NODE_ID]);
+ printf("%u (%s)", idv,
+ idv == 0 ? "coordinator" :
+ idv == 255 ? "unconfigured" : "follower");
+ }
+ putchar('\n');
+
+ // get node count
+ printf("\tNode count: ");
+ if (!tb[ETHTOOL_A_PLCA_NODE_CNT]) {
+ printf("not supported");
+ } else {
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_NODE_CNT]);
+ printf("%u", val);
+
+ // The node count is ignored by follower nodes. However, it can
+ // be pre-set to enable fast coordinator role switchover.
+ // Therefore, on a follower node we still wanto to show it,
+ // indicating it is not currently used.
+ if (tb[ETHTOOL_A_PLCA_NODE_ID] && idv != 0)
+ printf(" (ignored)");
+ }
+ putchar('\n');
+
+ // get TO timer (transmit opportunity timer)
+ printf("\tTO timer: ");
+ if (!tb[ETHTOOL_A_PLCA_TO_TMR]) {
+ printf("not supported");
+ } else {
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_TO_TMR]);
+ printf("%u BT", val);
+ }
+ putchar('\n');
+
+ // get burst count
+ printf("\tBurst count: ");
+ if (!tb[ETHTOOL_A_PLCA_BURST_CNT]) {
+ printf("not supported");
+ } else {
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_BURST_CNT]);
+ printf("%u (%s)", val,
+ val > 0 ? "enabled" : "disabled");
+ }
+ putchar('\n');
+
+ // get burst timer
+ printf("\tBurst timer: ");
+ if (!tb[ETHTOOL_A_PLCA_BURST_TMR]) {
+ printf("not supported");
+ } else {
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_BURST_TMR]);
+ printf("%u BT", val);
+ }
+ putchar('\n');
+
+ return MNL_CB_OK;
+}
+
+
+int nl_plca_get_cfg(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PLCA_GET_CFG, true))
+ return -EOPNOTSUPP;
+
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PLCA_GET_CFG,
+ ETHTOOL_A_PLCA_HEADER, 0);
+
+ if (ret < 0)
+ return ret;
+
+ return nlsock_send_get_request(nlsk, plca_get_cfg_reply_cb);
+}
+
+/* PLCA_SET_CFG */
+
+static const struct param_parser set_plca_params[] = {
+ {
+ .arg = "enable",
+ .type = ETHTOOL_A_PLCA_ENABLED,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "node-id",
+ .type = ETHTOOL_A_PLCA_NODE_ID,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "node-cnt",
+ .type = ETHTOOL_A_PLCA_NODE_CNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "to-tmr",
+ .type = ETHTOOL_A_PLCA_TO_TMR,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "burst-cnt",
+ .type = ETHTOOL_A_PLCA_BURST_CNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "burst-tmr",
+ .type = ETHTOOL_A_PLCA_BURST_TMR,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_plca_set_cfg(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PLCA_SET_CFG, false))
+ return -EOPNOTSUPP;
+ if (!ctx->argc) {
+ fprintf(stderr,
+ "ethtool (--set-plca-cfg): parameters missing\n");
+ return 1;
+ }
+
+ nlctx->cmd = "--set-plca-cfg";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PLCA_SET_CFG,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_PLCA_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, set_plca_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 76;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 76;
+}
+
+/* PLCA_GET_STATUS */
+
+int plca_get_status_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+ u8 val;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ putchar('\n');
+
+ printf("PLCA status of %s:\n", nlctx->devname);
+
+ // check whether the Open Alliance TC14 standard memory map is supported
+ printf("\tStatus: ");
+
+ if (!tb[ETHTOOL_A_PLCA_STATUS]) {
+ printf("not supported");
+ } else {
+ val = mnl_attr_get_u8(tb[ETHTOOL_A_PLCA_STATUS]);
+ printf(val ? "on" : "off");
+ }
+ putchar('\n');
+
+ return MNL_CB_OK;
+}
+
+
+int nl_plca_get_status(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PLCA_GET_STATUS, true))
+ return -EOPNOTSUPP;
+
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PLCA_GET_STATUS,
+ ETHTOOL_A_PLCA_HEADER, 0);
+
+ if (ret < 0)
+ return ret;
+
+ return nlsock_send_get_request(nlsk, plca_get_status_reply_cb);
+}
diff --git a/netlink/prettymsg.c b/netlink/prettymsg.c
new file mode 100644
index 0000000..fbf684f
--- /dev/null
+++ b/netlink/prettymsg.c
@@ -0,0 +1,262 @@
+/*
+ * prettymsg.c - human readable message dump
+ *
+ * Support for pretty print of an ethtool netlink message
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <linux/genetlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_link.h>
+#include <libmnl/libmnl.h>
+
+#include "prettymsg.h"
+
+#define __INDENT 4
+#define __DUMP_LINE 16
+#define __DUMP_BLOCK 4
+
+static void __print_binary_short(uint8_t *adata, unsigned int alen)
+{
+ unsigned int i;
+
+ if (!alen)
+ return;
+ printf("%02x", adata[0]);
+ for (i = 1; i < alen; i++)
+ printf("%c%02x", (i % __DUMP_BLOCK) ? ':' : ' ', adata[i]);
+}
+
+static void __print_binary_long(uint8_t *adata, unsigned int alen,
+ unsigned int level)
+{
+ unsigned int i;
+
+ for (i = 0; i < alen; i++) {
+ if (i % __DUMP_LINE == 0)
+ printf("\n%*s", __INDENT * (level + 2), "");
+ else if (i % __DUMP_BLOCK == 0)
+ printf(" ");
+ else
+ putchar(' ');
+ printf("%02x", adata[i]);
+ }
+}
+
+static int pretty_print_attr(const struct nlattr *attr,
+ const struct pretty_nla_desc *desc,
+ unsigned int ndesc, unsigned int level,
+ int err_offset, bool in_array)
+{
+ unsigned int alen = mnl_attr_get_payload_len(attr);
+ unsigned int atype = mnl_attr_get_type(attr);
+ unsigned int desc_idx = in_array ? 0 : atype;
+ void *adata = mnl_attr_get_payload(attr);
+ const struct pretty_nla_desc *adesc;
+ const char *prefix = " ";
+ bool nested;
+
+ adesc = (desc && desc_idx < ndesc) ? &desc[desc_idx] : NULL;
+ nested = (adesc && (adesc->format == NLA_NESTED ||
+ adesc->format == NLA_ARRAY)) ||
+ (attr->nla_type & NLA_F_NESTED);
+ if (err_offset >= 0 &&
+ err_offset < (nested ? NLA_HDRLEN : attr->nla_len)) {
+ prefix = "===>";
+ if (err_offset)
+ fprintf(stderr,
+ "ethtool: bad_attr inside an attribute (offset %d)\n",
+ err_offset);
+ }
+ if (adesc && adesc->name && !in_array)
+ printf("%s%*s%s", prefix, level * __INDENT, "", adesc->name);
+ else
+ printf("%s%*s[%u]", prefix, level * __INDENT, "", atype);
+
+ if (nested) {
+ struct nlattr *child;
+ int ret = 0;
+
+ putchar('\n');
+ mnl_attr_for_each_nested(child, attr) {
+ bool array = adesc && adesc->format == NLA_ARRAY;
+ unsigned int child_off;
+
+ child_off = (const char *)child - (const char *)attr;
+ ret = pretty_print_attr(child,
+ adesc ? adesc->children : NULL,
+ adesc ? adesc->n_children : 0,
+ level + 1,
+ err_offset - child_off, array);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+ }
+
+ printf(" = ");
+ switch(adesc ? adesc->format : NLA_BINARY) {
+ case NLA_U8:
+ printf("%u", mnl_attr_get_u8(attr));
+ break;
+ case NLA_U16:
+ printf("%u", mnl_attr_get_u16(attr));
+ break;
+ case NLA_U32:
+ printf("%u", mnl_attr_get_u32(attr));
+ break;
+ case NLA_U64:
+ printf("%" PRIu64, mnl_attr_get_u64(attr));
+ break;
+ case NLA_X8:
+ printf("0x%02x", mnl_attr_get_u8(attr));
+ break;
+ case NLA_X16:
+ printf("0x%04x", mnl_attr_get_u16(attr));
+ break;
+ case NLA_X32:
+ printf("0x%08x", mnl_attr_get_u32(attr));
+ break;
+ case NLA_X64:
+ printf("%" PRIx64, mnl_attr_get_u64(attr));
+ break;
+ case NLA_S8:
+ printf("%d", (int)mnl_attr_get_u8(attr));
+ break;
+ case NLA_S16:
+ printf("%d", (int)mnl_attr_get_u16(attr));
+ break;
+ case NLA_S32:
+ printf("%d", (int)mnl_attr_get_u32(attr));
+ break;
+ case NLA_S64:
+ printf("%" PRId64, (int64_t)mnl_attr_get_u64(attr));
+ break;
+ case NLA_STRING:
+ printf("\"%.*s\"", alen, (const char *)adata);
+ break;
+ case NLA_FLAG:
+ printf("true");
+ break;
+ case NLA_BOOL:
+ printf("%s", mnl_attr_get_u8(attr) ? "on" : "off");
+ break;
+ case NLA_U8_ENUM:
+ case NLA_U32_ENUM: {
+ uint32_t val;
+
+ if (adesc->format == NLA_U8_ENUM)
+ val = mnl_attr_get_u8(attr);
+ else
+ val = mnl_attr_get_u32(attr);
+
+ if (adesc && val < adesc->n_names && adesc->names[val])
+ printf("%s", adesc->names[val]);
+ else
+ printf("%u", val);
+ break;
+ }
+ default:
+ if (alen <= __DUMP_LINE)
+ __print_binary_short(adata, alen);
+ else
+ __print_binary_long(adata, alen, level);
+ }
+ putchar('\n');
+
+ return 0;
+}
+
+static int pretty_print_nlmsg(const struct nlmsghdr *nlhdr,
+ unsigned int payload_offset,
+ const struct pretty_nla_desc *desc,
+ unsigned int ndesc, unsigned int err_offset)
+{
+ const struct nlattr *attr;
+ int attr_offset;
+ int ret;
+
+ mnl_attr_for_each(attr, nlhdr, payload_offset) {
+ attr_offset = (const char *)attr - (const char *)nlhdr;
+ ret = pretty_print_attr(attr, desc, ndesc, 1,
+ err_offset - attr_offset, false);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int pretty_print_genlmsg(const struct nlmsghdr *nlhdr,
+ const struct pretty_nlmsg_desc *desc,
+ unsigned int ndesc, unsigned int err_offset)
+{
+ const struct pretty_nlmsg_desc *msg_desc;
+ const struct genlmsghdr *genlhdr;
+
+ if (mnl_nlmsg_get_payload_len(nlhdr) < GENL_HDRLEN) {
+ fprintf(stderr, "ethtool: message too short (%u bytes)\n",
+ nlhdr->nlmsg_len);
+ return -EINVAL;
+ }
+ genlhdr = mnl_nlmsg_get_payload(nlhdr);
+ msg_desc = (desc && genlhdr->cmd < ndesc) ? &desc[genlhdr->cmd] : NULL;
+ if (msg_desc && msg_desc->name)
+ printf(" %s\n", msg_desc->name);
+ else
+ printf(" [%u]\n", genlhdr->cmd);
+
+ return pretty_print_nlmsg(nlhdr, GENL_HDRLEN,
+ msg_desc ? msg_desc->attrs : NULL,
+ msg_desc ? msg_desc->n_attrs : 0, err_offset);
+}
+
+static void rtm_link_summary(const struct ifinfomsg *ifinfo)
+{
+ if (ifinfo->ifi_family)
+ printf(" family=%u", ifinfo->ifi_family);
+ if (ifinfo->ifi_type)
+ printf(" type=0x%04x", ifinfo->ifi_type);
+ if (ifinfo->ifi_index)
+ printf(" ifindex=%d", ifinfo->ifi_index);
+ if (ifinfo->ifi_flags)
+ printf(" flags=0x%x", ifinfo->ifi_flags);
+ if (ifinfo->ifi_change)
+ printf(" change=0x%x", ifinfo->ifi_change);
+}
+
+int pretty_print_rtnlmsg(const struct nlmsghdr *nlhdr, unsigned int err_offset)
+{
+ const unsigned int idx = (nlhdr->nlmsg_type - RTM_BASE) / 4;
+ const struct pretty_nlmsg_desc *msg_desc = NULL;
+ unsigned int hdrlen = USHRT_MAX;
+
+ if (nlhdr->nlmsg_type < rtnl_msg_n_desc)
+ msg_desc = &rtnl_msg_desc[nlhdr->nlmsg_type];
+ if (idx < rtnl_msghdr_n_len)
+ hdrlen = rtnl_msghdr_lengths[idx];
+ if (hdrlen < USHRT_MAX && mnl_nlmsg_get_payload_len(nlhdr) < hdrlen) {
+ fprintf(stderr, "ethtool: message too short (%u bytes)\n",
+ nlhdr->nlmsg_len);
+ return -EINVAL;
+ }
+ if (msg_desc && msg_desc->name)
+ printf(" %s", msg_desc->name);
+ else
+ printf(" [%u]", nlhdr->nlmsg_type);
+ if (idx == (RTM_NEWLINK - RTM_BASE) / 4)
+ rtm_link_summary(mnl_nlmsg_get_payload(nlhdr));
+ putchar('\n');
+ if (hdrlen == USHRT_MAX)
+ return 0;
+
+ return pretty_print_nlmsg(nlhdr, hdrlen,
+ msg_desc ? msg_desc->attrs : NULL,
+ msg_desc ? msg_desc->n_attrs : 0, err_offset);
+}
diff --git a/netlink/prettymsg.h b/netlink/prettymsg.h
new file mode 100644
index 0000000..8ca1db3
--- /dev/null
+++ b/netlink/prettymsg.h
@@ -0,0 +1,146 @@
+/*
+ * prettymsg.h - human readable message dump
+ *
+ * Support for pretty print of an ethtool netlink message
+ */
+
+#ifndef ETHTOOL_NETLINK_PRETTYMSG_H__
+#define ETHTOOL_NETLINK_PRETTYMSG_H__
+
+#include <linux/netlink.h>
+
+/* data structures for message format descriptions */
+
+enum pretty_nla_format {
+ NLA_INVALID,
+ NLA_BINARY,
+ NLA_U8,
+ NLA_U16,
+ NLA_U32,
+ NLA_U64,
+ NLA_X8,
+ NLA_X16,
+ NLA_X32,
+ NLA_X64,
+ NLA_S8,
+ NLA_S16,
+ NLA_S32,
+ NLA_S64,
+ NLA_STRING,
+ NLA_FLAG,
+ NLA_BOOL,
+ NLA_NESTED,
+ NLA_ARRAY,
+ NLA_U8_ENUM,
+ NLA_U32_ENUM,
+};
+
+struct pretty_nla_desc {
+ enum pretty_nla_format format;
+ const char *name;
+ union {
+ const struct pretty_nla_desc *children;
+ const char *const *names;
+ };
+ union {
+ unsigned int n_children;
+ unsigned int n_names;
+ };
+};
+
+struct pretty_nlmsg_desc {
+ const char *name;
+ const struct pretty_nla_desc *attrs;
+ unsigned int n_attrs;
+};
+
+/* helper macros for message format descriptions */
+
+#define NLATTR_DESC(_name, _fmt) \
+ [_name] = { \
+ .format = _fmt, \
+ .name = #_name, \
+ }
+
+#define NLATTR_DESC_INVALID(_name) NLATTR_DESC(_name, NLA_INVALID)
+#define NLATTR_DESC_U8(_name) NLATTR_DESC(_name, NLA_U8)
+#define NLATTR_DESC_U16(_name) NLATTR_DESC(_name, NLA_U16)
+#define NLATTR_DESC_U32(_name) NLATTR_DESC(_name, NLA_U32)
+#define NLATTR_DESC_U64(_name) NLATTR_DESC(_name, NLA_U64)
+#define NLATTR_DESC_X8(_name) NLATTR_DESC(_name, NLA_X8)
+#define NLATTR_DESC_X16(_name) NLATTR_DESC(_name, NLA_X16)
+#define NLATTR_DESC_X32(_name) NLATTR_DESC(_name, NLA_X32)
+#define NLATTR_DESC_X64(_name) NLATTR_DESC(_name, NLA_X64)
+#define NLATTR_DESC_S8(_name) NLATTR_DESC(_name, NLA_U8)
+#define NLATTR_DESC_S16(_name) NLATTR_DESC(_name, NLA_U16)
+#define NLATTR_DESC_S32(_name) NLATTR_DESC(_name, NLA_U32)
+#define NLATTR_DESC_S64(_name) NLATTR_DESC(_name, NLA_S64)
+#define NLATTR_DESC_STRING(_name) NLATTR_DESC(_name, NLA_STRING)
+#define NLATTR_DESC_FLAG(_name) NLATTR_DESC(_name, NLA_FLAG)
+#define NLATTR_DESC_BOOL(_name) NLATTR_DESC(_name, NLA_BOOL)
+#define NLATTR_DESC_BINARY(_name) NLATTR_DESC(_name, NLA_BINARY)
+
+#define NLATTR_DESC_NESTED(_name, _children_desc) \
+ [_name] = { \
+ .format = NLA_NESTED, \
+ .name = #_name, \
+ .children = __ ## _children_desc ## _desc, \
+ .n_children = ARRAY_SIZE(__ ## _children_desc ## _desc), \
+ }
+#define NLATTR_DESC_NESTED_NODESC(_name) NLATTR_DESC(_name, NLA_NESTED)
+#define NLATTR_DESC_ARRAY(_name, _children_desc) \
+ [_name] = { \
+ .format = NLA_ARRAY, \
+ .name = #_name, \
+ .children = __ ## _children_desc ## _desc, \
+ .n_children = 1, \
+ }
+#define NLATTR_DESC_U8_ENUM(_name, _names_table) \
+ [_name] = { \
+ .format = NLA_U8_ENUM, \
+ .name = #_name, \
+ .names = __ ## _names_table ## _names, \
+ .n_children = ARRAY_SIZE(__ ## _names_table ## _names), \
+ }
+#define NLATTR_DESC_U32_ENUM(_name, _names_table) \
+ [_name] = { \
+ .format = NLA_U32_ENUM, \
+ .name = #_name, \
+ .names = __ ## _names_table ## _names, \
+ .n_children = ARRAY_SIZE(__ ## _names_table ## _names), \
+ }
+
+#define NLMSG_DESC(_name, _attrs) \
+ [_name] = { \
+ .name = #_name, \
+ .attrs = __ ## _attrs ## _desc, \
+ .n_attrs = ARRAY_SIZE(__ ## _attrs ## _desc), \
+ }
+
+#define NLMSG_DESC_INVALID(_name) \
+ [_name] = { \
+ .name = #_name, \
+ }
+
+/* function to pretty print a genetlink message */
+int pretty_print_genlmsg(const struct nlmsghdr *nlhdr,
+ const struct pretty_nlmsg_desc *desc,
+ unsigned int ndesc, unsigned int err_offset);
+int pretty_print_rtnlmsg(const struct nlmsghdr *nlhdr, unsigned int err_offset);
+
+/* message descriptions */
+
+extern const struct pretty_nlmsg_desc ethnl_umsg_desc[];
+extern const unsigned int ethnl_umsg_n_desc;
+extern const struct pretty_nlmsg_desc ethnl_kmsg_desc[];
+extern const unsigned int ethnl_kmsg_n_desc;
+
+extern const struct pretty_nlmsg_desc genlctrl_msg_desc[];
+extern const unsigned int genlctrl_msg_n_desc;
+
+extern const struct pretty_nlmsg_desc rtnl_msg_desc[];
+extern const unsigned int rtnl_msg_n_desc;
+extern const unsigned short rtnl_msghdr_lengths[];
+extern const unsigned int rtnl_msghdr_n_len;
+
+#endif /* ETHTOOL_NETLINK_PRETTYMSG_H__ */
diff --git a/netlink/privflags.c b/netlink/privflags.c
new file mode 100644
index 0000000..299ccdc
--- /dev/null
+++ b/netlink/privflags.c
@@ -0,0 +1,158 @@
+/*
+ * privflags.c - netlink implementation of private flags commands
+ *
+ * Implementation of "ethtool --show-priv-flags <dev>" and
+ * "ethtool --set-priv-flags <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "strset.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* PRIVFLAGS_GET */
+
+static void privflags_maxlen_walk_cb(unsigned int idx, const char *name,
+ bool val __maybe_unused, void *data)
+{
+ unsigned int *maxlen = data;
+ unsigned int len, n;
+
+ if (name)
+ len = strlen(name);
+ else {
+ len = 3; /* strlen("bit") */
+ for (n = idx ?: 1; n; n /= 10)
+ len++; /* plus number of ditigs */
+ }
+ if (len > *maxlen)
+ *maxlen = len;
+}
+
+static void privflags_dump_walk_cb(unsigned int idx, const char *name, bool val,
+ void *data)
+{
+ unsigned int *maxlen = data;
+ char buff[16];
+
+ if (!name) {
+ snprintf(buff, sizeof(buff) - 1, "bit%u", idx);
+ name = buff;
+ }
+ printf("%-*s: %s\n", *maxlen, name, val ? "on" : "off");
+}
+
+int privflags_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PRIVFLAGS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const struct stringset *flag_names = NULL;
+ struct nl_context *nlctx = data;
+ unsigned int maxlen = 0;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0 || !tb[ETHTOOL_A_PRIVFLAGS_FLAGS])
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PRIVFLAGS_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS])) {
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return err_ret;
+ flag_names = perdev_stringset(nlctx->devname, ETH_SS_PRIV_FLAGS,
+ nlctx->ethnl2_socket);
+ }
+
+ ret = walk_bitset(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], flag_names,
+ privflags_maxlen_walk_cb, &maxlen);
+ if (ret < 0)
+ return err_ret;
+ if (silent)
+ putchar('\n');
+ printf("Private flags for %s:\n", nlctx->devname);
+ ret = walk_bitset(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], flag_names,
+ privflags_dump_walk_cb, &maxlen);
+ return (ret < 0) ? err_ret : MNL_CB_OK;
+}
+
+int nl_gprivflags(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PRIVFLAGS_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PRIVFLAGS_GET,
+ ETHTOOL_A_PRIVFLAGS_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, privflags_reply_cb);
+}
+
+/* PRIVFLAGS_SET */
+
+static const struct bitset_parser_data privflags_parser_data = {
+ .force_hex = false,
+ .no_mask = false,
+};
+
+int nl_sprivflags(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PRIVFLAGS_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "--set-priv-flags";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PRIVFLAGS_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_PRIVFLAGS_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parse_bitset(nlctx, ETHTOOL_A_PRIVFLAGS_FLAGS,
+ &privflags_parser_data, msgbuff, NULL);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 2;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 1;
+}
diff --git a/netlink/pse-pd.c b/netlink/pse-pd.c
new file mode 100644
index 0000000..d6faff8
--- /dev/null
+++ b/netlink/pse-pd.c
@@ -0,0 +1,193 @@
+/*
+ * pse.c - netlink implementation of pse commands
+ *
+ * Implementation of "ethtool --show-pse <dev>" and
+ * "ethtool --set-pse <dev> ..."
+ */
+
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+/* PSE_GET */
+
+static const char *podl_pse_admin_state_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN:
+ return "unknown";
+ case ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED:
+ return "disabled";
+ case ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED:
+ return "enabled";
+ default:
+ return "unsupported";
+ }
+}
+
+static const char *podl_pse_pw_d_status_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN:
+ return "unknown";
+ case ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED:
+ return "disabled";
+ case ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING:
+ return "searching";
+ case ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING:
+ return "delivering power";
+ case ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP:
+ return "sleep";
+ case ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE:
+ return "idle";
+ case ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR:
+ return "error";
+ default:
+ return "unsupported";
+ }
+}
+
+int pse_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PSE_MAX + 1] = {};
+ struct nl_context *nlctx = data;
+ DECLARE_ATTR_TB_INFO(tb);
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PSE_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ print_nl();
+
+ open_json_object(NULL);
+
+ print_string(PRINT_ANY, "ifname", "PSE attributes for %s:\n",
+ nlctx->devname);
+
+ if (tb[ETHTOOL_A_PODL_PSE_ADMIN_STATE]) {
+ u32 val;
+
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_STATE]);
+ print_string(PRINT_ANY, "podl-pse-admin-state",
+ "PoDL PSE Admin State: %s\n",
+ podl_pse_admin_state_name(val));
+ }
+
+ if (tb[ETHTOOL_A_PODL_PSE_PW_D_STATUS]) {
+ u32 val;
+
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_PODL_PSE_PW_D_STATUS]);
+ print_string(PRINT_ANY, "podl-pse-power-detection-status",
+ "PoDL PSE Power Detection Status: %s\n",
+ podl_pse_pw_d_status_name(val));
+ }
+
+ close_json_object();
+
+ return MNL_CB_OK;
+}
+
+int nl_gpse(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PSE_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ nlsk = nlctx->ethnl_socket;
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PSE_GET,
+ ETHTOOL_A_PSE_HEADER, 0);
+ if (ret < 0)
+ return ret;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, pse_reply_cb);
+ delete_json_obj();
+
+ return ret;
+}
+
+/* PSE_SET */
+
+static const struct lookup_entry_u32 podl_pse_admin_control_values[] = {
+ { .arg = "enable", .val = ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED },
+ { .arg = "disable", .val = ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED },
+ {}
+};
+
+static const struct param_parser spse_params[] = {
+ {
+ .arg = "podl-pse-admin-control",
+ .type = ETHTOOL_A_PODL_PSE_ADMIN_CONTROL,
+ .handler = nl_parse_lookup_u32,
+ .handler_data = podl_pse_admin_control_values,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_spse(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PSE_SET, false))
+ return -EOPNOTSUPP;
+ if (!ctx->argc) {
+ fprintf(stderr, "ethtool (--set-pse): parameters missing\n");
+ return 1;
+ }
+
+ nlctx->cmd = "--set-pse";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PSE_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_PSE_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, spse_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 83;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 83;
+}
diff --git a/netlink/rings.c b/netlink/rings.c
new file mode 100644
index 0000000..f9eb67a
--- /dev/null
+++ b/netlink/rings.c
@@ -0,0 +1,237 @@
+/*
+ * rings.c - netlink implementation of ring commands
+ *
+ * Implementation of "ethtool -g <dev>" and "ethtool -G <dev> ..."
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+/* RINGS_GET */
+
+int rings_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_RINGS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ unsigned char tcp_hds;
+ char *tcp_hds_fmt;
+ char *tcp_hds_key;
+ char tcp_hds_buf[256];
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_RINGS_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ open_json_object(NULL);
+
+ if (silent)
+ show_cr();
+ print_string(PRINT_ANY, "ifname", "Ring parameters for %s:\n",
+ nlctx->devname);
+ print_string(PRINT_FP, NULL, "Pre-set maximums:\n", NULL);
+ show_u32("rx-max", "RX:\t\t\t", tb[ETHTOOL_A_RINGS_RX_MAX]);
+ show_u32("rx-mini-max", "RX Mini:\t\t", tb[ETHTOOL_A_RINGS_RX_MINI_MAX]);
+ show_u32("rx-jumbo-max", "RX Jumbo:\t\t",
+ tb[ETHTOOL_A_RINGS_RX_JUMBO_MAX]);
+ show_u32("tx-max", "TX:\t\t\t", tb[ETHTOOL_A_RINGS_TX_MAX]);
+ show_u32("tx-push-buff-max-len", "TX push buff len:\t",
+ tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX]);
+ print_string(PRINT_FP, NULL, "Current hardware settings:\n", NULL);
+ show_u32("rx", "RX:\t\t\t", tb[ETHTOOL_A_RINGS_RX]);
+ show_u32("rx-mini", "RX Mini:\t\t", tb[ETHTOOL_A_RINGS_RX_MINI]);
+ show_u32("rx-jumbo", "RX Jumbo:\t\t", tb[ETHTOOL_A_RINGS_RX_JUMBO]);
+ show_u32("tx", "TX:\t\t\t", tb[ETHTOOL_A_RINGS_TX]);
+ show_u32("rx-buf-len", "RX Buf Len:\t\t", tb[ETHTOOL_A_RINGS_RX_BUF_LEN]);
+ show_u32("cqe-size", "CQE Size:\t\t", tb[ETHTOOL_A_RINGS_CQE_SIZE]);
+ show_bool("tx-push", "TX Push:\t\t%s\n", tb[ETHTOOL_A_RINGS_TX_PUSH]);
+ show_bool("rx-push", "RX Push:\t\t%s\n", tb[ETHTOOL_A_RINGS_RX_PUSH]);
+ show_u32("tx-push-buf-len", "TX push buff len:\t",
+ tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN]);
+
+ tcp_hds_fmt = "TCP data split:\t\t%s\n";
+ tcp_hds_key = "tcp-data-split";
+ tcp_hds = tb[ETHTOOL_A_RINGS_TCP_DATA_SPLIT] ?
+ mnl_attr_get_u8(tb[ETHTOOL_A_RINGS_TCP_DATA_SPLIT]) : 0;
+ switch (tcp_hds) {
+ case ETHTOOL_TCP_DATA_SPLIT_UNKNOWN:
+ print_string(PRINT_FP, tcp_hds_key, tcp_hds_fmt, "n/a");
+ break;
+ case ETHTOOL_TCP_DATA_SPLIT_DISABLED:
+ print_string(PRINT_ANY, tcp_hds_key, tcp_hds_fmt, "off");
+ break;
+ case ETHTOOL_TCP_DATA_SPLIT_ENABLED:
+ print_string(PRINT_ANY, tcp_hds_key, tcp_hds_fmt, "on");
+ break;
+ default:
+ snprintf(tcp_hds_buf, sizeof(tcp_hds_buf),
+ "unknown(%d)\n", tcp_hds);
+ print_string(PRINT_ANY, tcp_hds_key, tcp_hds_fmt, tcp_hds_buf);
+ break;
+ }
+
+ close_json_object();
+
+ return MNL_CB_OK;
+}
+
+int nl_gring(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_RINGS_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_RINGS_GET,
+ ETHTOOL_A_RINGS_HEADER, 0);
+ if (ret < 0)
+ return ret;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, rings_reply_cb);
+ delete_json_obj();
+ return ret;
+}
+
+/* RINGS_SET */
+
+static const struct lookup_entry_u8 tcp_data_split_values[] = {
+ {
+ .arg = "auto",
+ .val = ETHTOOL_TCP_DATA_SPLIT_UNKNOWN,
+ },
+ {
+ .arg = "off",
+ .val = ETHTOOL_TCP_DATA_SPLIT_DISABLED,
+ },
+ {
+ .arg = "on",
+ .val = ETHTOOL_TCP_DATA_SPLIT_ENABLED,
+ },
+ {}
+};
+
+static const struct param_parser sring_params[] = {
+ {
+ .arg = "rx",
+ .type = ETHTOOL_A_RINGS_RX,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-mini",
+ .type = ETHTOOL_A_RINGS_RX_MINI,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-jumbo",
+ .type = ETHTOOL_A_RINGS_RX_JUMBO,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx",
+ .type = ETHTOOL_A_RINGS_TX,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-push-buf-len",
+ .type = ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-buf-len",
+ .type = ETHTOOL_A_RINGS_RX_BUF_LEN,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tcp-data-split",
+ .type = ETHTOOL_A_RINGS_TCP_DATA_SPLIT,
+ .handler = nl_parse_lookup_u8,
+ .handler_data = tcp_data_split_values,
+ .min_argc = 1,
+ },
+ {
+ .arg = "cqe-size",
+ .type = ETHTOOL_A_RINGS_CQE_SIZE,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-push",
+ .type = ETHTOOL_A_RINGS_TX_PUSH,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-push",
+ .type = ETHTOOL_A_RINGS_RX_PUSH,
+ .handler = nl_parse_u8bool,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_sring(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_RINGS_SET, false))
+ return -EOPNOTSUPP;
+
+ nlctx->cmd = "-G";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_RINGS_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_RINGS_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, sring_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 81;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 81;
+}
diff --git a/netlink/rss.c b/netlink/rss.c
new file mode 100644
index 0000000..4ad6065
--- /dev/null
+++ b/netlink/rss.c
@@ -0,0 +1,230 @@
+/*
+ * rss.c - netlink implementation of RSS context commands
+ *
+ * Implementation of "ethtool -x <dev>"
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "strset.h"
+#include "parser.h"
+
+struct cb_args {
+ struct nl_context *nlctx;
+ u32 num_rings;
+};
+
+void dump_json_rss_info(struct cmd_context *ctx, u32 *indir_table,
+ u32 indir_size, u8 *hkey, u32 hkey_size,
+ const struct stringset *hash_funcs, u8 hfunc)
+{
+ unsigned int i;
+
+ open_json_object(NULL);
+ print_string(PRINT_JSON, "ifname", NULL, ctx->devname);
+ if (indir_size) {
+ open_json_array("rss-indirection-table", NULL);
+ for (i = 0; i < indir_size; i++)
+ print_uint(PRINT_JSON, NULL, NULL, indir_table[i]);
+ close_json_array("\n");
+ }
+
+ if (hkey_size) {
+ open_json_array("rss-hash-key", NULL);
+ for (i = 0; i < hkey_size; i++)
+ print_uint(PRINT_JSON, NULL, NULL, (u8)hkey[i]);
+ close_json_array("\n");
+ }
+
+ if (hfunc) {
+ for (i = 0; i < get_count(hash_funcs); i++) {
+ if (hfunc & (1 << i)) {
+ print_string(PRINT_JSON, "rss-hash-function",
+ NULL, get_string(hash_funcs, i));
+ break;
+ }
+ }
+ }
+
+ close_json_object();
+}
+
+int get_channels_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_CHANNELS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct cb_args *args = data;
+ struct nl_context *nlctx = args->nlctx;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_CHANNELS_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+ if (tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT])
+ args->num_rings = mnl_attr_get_u32(tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT]);
+ if (tb[ETHTOOL_A_CHANNELS_RX_COUNT])
+ args->num_rings += mnl_attr_get_u32(tb[ETHTOOL_A_CHANNELS_RX_COUNT]);
+ return MNL_CB_OK;
+}
+
+int rss_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_RSS_MAX + 1] = {};
+ unsigned int indir_bytes = 0, hkey_bytes = 0;
+ DECLARE_ATTR_TB_INFO(tb);
+ struct cb_args *args = data;
+ struct nl_context *nlctx = args->nlctx;
+ const struct stringset *hash_funcs;
+ u32 rss_hfunc = 0, indir_size;
+ u32 *indir_table = NULL;
+ u8 *hkey = NULL;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_RSS_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ show_cr();
+
+ if (tb[ETHTOOL_A_RSS_HFUNC])
+ rss_hfunc = mnl_attr_get_u32(tb[ETHTOOL_A_RSS_HFUNC]);
+
+ if (tb[ETHTOOL_A_RSS_INDIR]) {
+ indir_bytes = mnl_attr_get_payload_len(tb[ETHTOOL_A_RSS_INDIR]);
+ indir_table = mnl_attr_get_payload(tb[ETHTOOL_A_RSS_INDIR]);
+ }
+
+ if (tb[ETHTOOL_A_RSS_HKEY]) {
+ hkey_bytes = mnl_attr_get_payload_len(tb[ETHTOOL_A_RSS_HKEY]);
+ hkey = mnl_attr_get_payload(tb[ETHTOOL_A_RSS_HKEY]);
+ }
+
+ /* Fetch RSS hash functions and their status and print */
+ if (!nlctx->is_monitor) {
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+ }
+ hash_funcs = global_stringset(ETH_SS_RSS_HASH_FUNCS,
+ nlctx->ethnl2_socket);
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return silent ? MNL_CB_OK : MNL_CB_ERROR;
+
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_RSS_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ /* Fetch ring count info into args->num_rings */
+ ret = nlsock_prep_get_request(nlctx->ethnl2_socket,
+ ETHTOOL_MSG_CHANNELS_GET,
+ ETHTOOL_A_CHANNELS_HEADER, 0);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+
+ ret = nlsock_sendmsg(nlctx->ethnl2_socket, NULL);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+
+ ret = nlsock_process_reply(nlctx->ethnl2_socket, get_channels_cb, args);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+
+ indir_size = indir_bytes / sizeof(u32);
+ if (is_json_context()) {
+ dump_json_rss_info(nlctx->ctx, (u32 *)indir_table, indir_size,
+ hkey, hkey_bytes, hash_funcs, rss_hfunc);
+ } else {
+ print_indir_table(nlctx->ctx, args->num_rings,
+ indir_size, (u32 *)indir_table);
+ print_rss_hkey(hkey, hkey_bytes);
+ printf("RSS hash function:\n");
+ if (!rss_hfunc) {
+ printf(" Operation not supported\n");
+ return 0;
+ }
+ for (unsigned int i = 0; i < get_count(hash_funcs); i++) {
+ printf(" %s: %s\n", get_string(hash_funcs, i),
+ (rss_hfunc & (1 << i)) ? "on" : "off");
+ }
+ }
+
+ return MNL_CB_OK;
+}
+
+/* RSS_GET */
+static const struct param_parser grss_params[] = {
+ {
+ .arg = "context",
+ .type = ETHTOOL_A_RSS_CONTEXT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_grss(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ struct nl_msg_buff *msgbuff;
+ struct cb_args args = {};
+ int ret;
+
+ nlctx->cmd = "-x";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_RSS_GET, true))
+ return -EOPNOTSUPP;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_RSS_GET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 1;
+
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_RSS_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, grss_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ goto err;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ goto err;
+
+ args.nlctx = nlctx;
+ new_json_obj(ctx->json);
+ ret = nlsock_process_reply(nlsk, rss_reply_cb, &args);
+ delete_json_obj();
+
+ if (ret == 0)
+ return 0;
+err:
+ return nlctx->exit_code ?: 1;
+}
diff --git a/netlink/settings.c b/netlink/settings.c
new file mode 100644
index 0000000..a506618
--- /dev/null
+++ b/netlink/settings.c
@@ -0,0 +1,1377 @@
+/*
+ * settings.c - netlink implementation of settings commands
+ *
+ * Implementation of "ethtool <dev>" and "ethtool -s <dev> ...".
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "strset.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* GET_SETTINGS */
+
+struct link_mode_info {
+ enum link_mode_class class;
+ u32 speed;
+ u8 duplex;
+};
+
+static const char *const names_duplex[] = {
+ [DUPLEX_HALF] = "Half",
+ [DUPLEX_FULL] = "Full",
+};
+
+static const char *const names_master_slave_state[] = {
+ [MASTER_SLAVE_STATE_UNKNOWN] = "unknown",
+ [MASTER_SLAVE_STATE_MASTER] = "master",
+ [MASTER_SLAVE_STATE_SLAVE] = "slave",
+ [MASTER_SLAVE_STATE_ERR] = "resolution error",
+};
+
+static const char *const names_master_slave_cfg[] = {
+ [MASTER_SLAVE_CFG_UNKNOWN] = "unknown",
+ [MASTER_SLAVE_CFG_MASTER_PREFERRED] = "preferred master",
+ [MASTER_SLAVE_CFG_SLAVE_PREFERRED] = "preferred slave",
+ [MASTER_SLAVE_CFG_MASTER_FORCE] = "forced master",
+ [MASTER_SLAVE_CFG_SLAVE_FORCE] = "forced slave",
+};
+
+static const char *const names_port[] = {
+ [PORT_TP] = "Twisted Pair",
+ [PORT_AUI] = "AUI",
+ [PORT_BNC] = "BNC",
+ [PORT_MII] = "MII",
+ [PORT_FIBRE] = "FIBRE",
+ [PORT_DA] = "Direct Attach Copper",
+ [PORT_NONE] = "None",
+ [PORT_OTHER] = "Other",
+};
+
+static const char *const names_transceiver[] = {
+ [XCVR_INTERNAL] = "internal",
+ [XCVR_EXTERNAL] = "external",
+};
+
+/* the practice of putting completely unrelated flags into link mode bitmaps
+ * is rather unfortunate but as even ethtool_link_ksettings preserved that,
+ * there is little chance of getting them separated any time soon so let's
+ * sort them out ourselves
+ */
+#define __REAL(_speed) \
+ { .class = LM_CLASS_REAL, .speed = _speed, .duplex = DUPLEX_FULL }
+#define __HALF_DUPLEX(_speed) \
+ { .class = LM_CLASS_REAL, .speed = _speed, .duplex = DUPLEX_HALF }
+#define __SPECIAL(_class) \
+ { .class = LM_CLASS_ ## _class }
+
+static const struct link_mode_info link_modes[] = {
+ [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = __HALF_DUPLEX(10),
+ [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = __REAL(10),
+ [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = __HALF_DUPLEX(100),
+ [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = __REAL(100),
+ [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = __HALF_DUPLEX(1000),
+ [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = __REAL(1000),
+ [ETHTOOL_LINK_MODE_Autoneg_BIT] = __SPECIAL(AUTONEG),
+ [ETHTOOL_LINK_MODE_TP_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_AUI_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_MII_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_FIBRE_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_BNC_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_Pause_BIT] = __SPECIAL(PAUSE),
+ [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = __SPECIAL(PAUSE),
+ [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = __REAL(2500),
+ [ETHTOOL_LINK_MODE_Backplane_BIT] = __SPECIAL(PORT),
+ [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = __REAL(1000),
+ [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = __REAL(20000),
+ [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = __REAL(20000),
+ [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = __REAL(40000),
+ [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = __REAL(40000),
+ [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = __REAL(40000),
+ [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = __REAL(40000),
+ [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = __REAL(56000),
+ [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = __REAL(56000),
+ [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = __REAL(56000),
+ [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = __REAL(56000),
+ [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = __REAL(25000),
+ [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = __REAL(25000),
+ [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = __REAL(25000),
+ [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = __REAL(1000),
+ [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = __REAL(10000),
+ [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = __REAL(2500),
+ [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = __REAL(5000),
+ [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = __SPECIAL(FEC),
+ [ETHTOOL_LINK_MODE_FEC_RS_BIT] = __SPECIAL(FEC),
+ [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = __SPECIAL(FEC),
+ [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = __REAL(50000),
+ [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_100baseT1_Full_BIT] = __REAL(100),
+ [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] = __REAL(1000),
+ [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_FEC_LLRS_BIT] = __SPECIAL(FEC),
+ [ETHTOOL_LINK_MODE_100000baseKR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseSR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseCR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_100000baseDR_Full_BIT] = __REAL(100000),
+ [ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT] = __REAL(200000),
+ [ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT] = __REAL(400000),
+ [ETHTOOL_LINK_MODE_100baseFX_Half_BIT] = __HALF_DUPLEX(100),
+ [ETHTOOL_LINK_MODE_100baseFX_Full_BIT] = __REAL(100),
+ [ETHTOOL_LINK_MODE_10baseT1L_Full_BIT] = __REAL(10),
+ [ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT] = __REAL(800000),
+ [ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT] = __REAL(800000),
+ [ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT] = __REAL(800000),
+ [ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT] = __REAL(800000),
+ [ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT] = __REAL(800000),
+ [ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT] = __REAL(800000),
+ [ETHTOOL_LINK_MODE_10baseT1S_Full_BIT] = __REAL(10),
+ [ETHTOOL_LINK_MODE_10baseT1S_Half_BIT] = __HALF_DUPLEX(10),
+ [ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT] = __HALF_DUPLEX(10),
+};
+const unsigned int link_modes_count = ARRAY_SIZE(link_modes);
+
+#undef __REAL
+#undef __HALF_DUPLEX
+#undef __SPECIAL
+
+static bool lm_class_match(unsigned int mode, enum link_mode_class class)
+{
+ unsigned int mode_class = (mode < link_modes_count) ?
+ link_modes[mode].class : LM_CLASS_UNKNOWN;
+
+ return mode_class == class ||
+ (class == LM_CLASS_REAL && mode_class == LM_CLASS_UNKNOWN);
+}
+
+static void print_enum(const char *const *info, unsigned int n_info,
+ unsigned int val, const char *label)
+{
+ if (val >= n_info || !info[val])
+ printf("\t%s: Unknown! (%d)\n", label, val);
+ else
+ printf("\t%s: %s\n", label, info[val]);
+}
+
+static int dump_pause(const struct nlattr *attr, bool mask, const char *label)
+{
+ bool pause, asym;
+ int ret = 0;
+
+ pause = bitset_get_bit(attr, mask, ETHTOOL_LINK_MODE_Pause_BIT, &ret);
+ if (ret < 0)
+ goto err;
+ asym = bitset_get_bit(attr, mask, ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ &ret);
+ if (ret < 0)
+ goto err;
+
+ printf("\t%s", label);
+ if (pause)
+ printf("%s\n", asym ? "Symmetric Receive-only" : "Symmetric");
+ else
+ printf("%s\n", asym ? "Transmit-only" : "No");
+
+ return 0;
+err:
+ fprintf(stderr, "malformed netlink message (pause modes)\n");
+ return ret;
+}
+
+static void print_banner(struct nl_context *nlctx)
+{
+ if (nlctx->no_banner)
+ return;
+ printf("Settings for %s:\n", nlctx->devname);
+ nlctx->no_banner = true;
+}
+
+int dump_link_modes(struct nl_context *nlctx, const struct nlattr *bitset,
+ bool mask, unsigned int class, const char *before,
+ const char *between, const char *after, const char *if_none)
+{
+ const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(bitset_tb);
+ const unsigned int before_len = strlen(before);
+ unsigned int prev = UINT_MAX - 1;
+ const struct nlattr *bits;
+ const struct nlattr *bit;
+ bool first = true;
+ bool nomask;
+ int ret;
+
+ ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
+ if (ret < 0)
+ goto err_nonl;
+
+ nomask = bitset_tb[ETHTOOL_A_BITSET_NOMASK];
+ /* Trying to print the mask of a "no mask" bitset doesn't make sense */
+ if (mask && nomask) {
+ ret = -EFAULT;
+ goto err_nonl;
+ }
+
+ bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
+
+ if (!bits) {
+ const struct stringset *lm_strings;
+ unsigned int count;
+ unsigned int idx;
+ const char *name;
+
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ goto err_nonl;
+ lm_strings = global_stringset(ETH_SS_LINK_MODES,
+ nlctx->ethnl2_socket);
+ bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] :
+ bitset_tb[ETHTOOL_A_BITSET_VALUE];
+ ret = -EFAULT;
+ if (!bits || !bitset_tb[ETHTOOL_A_BITSET_SIZE])
+ goto err_nonl;
+ count = mnl_attr_get_u32(bitset_tb[ETHTOOL_A_BITSET_SIZE]);
+ if (mnl_attr_get_payload_len(bits) / 4 < (count + 31) / 32)
+ goto err_nonl;
+
+ printf("\t%s", before);
+ for (idx = 0; idx < count; idx++) {
+ const uint32_t *raw_data = mnl_attr_get_payload(bits);
+ char buff[14];
+
+ if (!(raw_data[idx / 32] & (1U << (idx % 32))))
+ continue;
+ if (!lm_class_match(idx, class))
+ continue;
+ name = get_string(lm_strings, idx);
+ if (!name) {
+ snprintf(buff, sizeof(buff), "BIT%u", idx);
+ name = buff;
+ }
+ if (first)
+ first = false;
+ /* ugly hack to preserve old output format */
+ if (class == LM_CLASS_REAL && (idx == prev + 1) &&
+ prev < link_modes_count &&
+ link_modes[prev].class == LM_CLASS_REAL &&
+ link_modes[prev].duplex == DUPLEX_HALF)
+ putchar(' ');
+ else if (between)
+ printf("\t%s", between);
+ else
+ printf("\n\t%*s", before_len, "");
+ printf("%s", name);
+ prev = idx;
+ }
+ goto after;
+ }
+
+ printf("\t%s", before);
+ mnl_attr_for_each_nested(bit, bits) {
+ const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned int idx;
+ const char *name;
+
+ if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT)
+ continue;
+ ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+ ret = -EFAULT;
+ if (!tb[ETHTOOL_A_BITSET_BIT_INDEX] ||
+ !tb[ETHTOOL_A_BITSET_BIT_NAME])
+ goto err;
+ if (!mask && !nomask && !tb[ETHTOOL_A_BITSET_BIT_VALUE])
+ continue;
+
+ idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
+ name = mnl_attr_get_str(tb[ETHTOOL_A_BITSET_BIT_NAME]);
+ if (!lm_class_match(idx, class))
+ continue;
+ if (first) {
+ first = false;
+ } else {
+ /* ugly hack to preserve old output format */
+ if ((class == LM_CLASS_REAL) && (idx == prev + 1) &&
+ (prev < link_modes_count) &&
+ (link_modes[prev].class == LM_CLASS_REAL) &&
+ (link_modes[prev].duplex == DUPLEX_HALF))
+ putchar(' ');
+ else if (between)
+ printf("\t%s", between);
+ else
+ printf("\n\t%*s", before_len, "");
+ }
+ printf("%s", name);
+ prev = idx;
+ }
+after:
+ if (first && if_none)
+ printf("%s", if_none);
+ printf("%s", after);
+
+ return 0;
+err:
+ putchar('\n');
+err_nonl:
+ fflush(stdout);
+ fprintf(stderr, "malformed netlink message (link_modes)\n");
+ return ret;
+}
+
+static int dump_our_modes(struct nl_context *nlctx, const struct nlattr *attr)
+{
+ bool autoneg;
+ int ret;
+
+ print_banner(nlctx);
+ ret = dump_link_modes(nlctx, attr, true, LM_CLASS_PORT,
+ "Supported ports: [ ", " ", " ]\n", NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = dump_link_modes(nlctx, attr, true, LM_CLASS_REAL,
+ "Supported link modes: ", NULL, "\n",
+ "Not reported");
+ if (ret < 0)
+ return ret;
+ ret = dump_pause(attr, true, "Supported pause frame use: ");
+ if (ret < 0)
+ return ret;
+
+ autoneg = bitset_get_bit(attr, true, ETHTOOL_LINK_MODE_Autoneg_BIT,
+ &ret);
+ if (ret < 0)
+ return ret;
+ printf("\tSupports auto-negotiation: %s\n", autoneg ? "Yes" : "No");
+
+ ret = dump_link_modes(nlctx, attr, true, LM_CLASS_FEC,
+ "Supported FEC modes: ", " ", "\n",
+ "Not reported");
+ if (ret < 0)
+ return ret;
+
+ ret = dump_link_modes(nlctx, attr, false, LM_CLASS_REAL,
+ "Advertised link modes: ", NULL, "\n",
+ "Not reported");
+ if (ret < 0)
+ return ret;
+
+ ret = dump_pause(attr, false, "Advertised pause frame use: ");
+ if (ret < 0)
+ return ret;
+ autoneg = bitset_get_bit(attr, false, ETHTOOL_LINK_MODE_Autoneg_BIT,
+ &ret);
+ if (ret < 0)
+ return ret;
+ printf("\tAdvertised auto-negotiation: %s\n", autoneg ? "Yes" : "No");
+
+ ret = dump_link_modes(nlctx, attr, false, LM_CLASS_FEC,
+ "Advertised FEC modes: ", " ", "\n",
+ "Not reported");
+ return ret;
+}
+
+static int dump_peer_modes(struct nl_context *nlctx, const struct nlattr *attr)
+{
+ bool autoneg;
+ int ret;
+
+ print_banner(nlctx);
+ ret = dump_link_modes(nlctx, attr, false, LM_CLASS_REAL,
+ "Link partner advertised link modes: ",
+ NULL, "\n", "Not reported");
+ if (ret < 0)
+ return ret;
+
+ ret = dump_pause(attr, false,
+ "Link partner advertised pause frame use: ");
+ if (ret < 0)
+ return ret;
+
+ autoneg = bitset_get_bit(attr, false,
+ ETHTOOL_LINK_MODE_Autoneg_BIT, &ret);
+ if (ret < 0)
+ return ret;
+ printf("\tLink partner advertised auto-negotiation: %s\n",
+ autoneg ? "Yes" : "No");
+
+ ret = dump_link_modes(nlctx, attr, false, LM_CLASS_FEC,
+ "Link partner advertised FEC modes: ",
+ " ", "\n", "Not reported");
+ return ret;
+}
+
+int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ int ret;
+
+ if (nlctx->is_dump || nlctx->is_monitor)
+ nlctx->no_banner = false;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_LINKMODES_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (tb[ETHTOOL_A_LINKMODES_OURS]) {
+ ret = dump_our_modes(nlctx, tb[ETHTOOL_A_LINKMODES_OURS]);
+ if (ret < 0)
+ goto err;
+ }
+ if (tb[ETHTOOL_A_LINKMODES_PEER]) {
+ ret = dump_peer_modes(nlctx, tb[ETHTOOL_A_LINKMODES_PEER]);
+ if (ret < 0)
+ goto err;
+ }
+ if (tb[ETHTOOL_A_LINKMODES_SPEED]) {
+ uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKMODES_SPEED]);
+
+ print_banner(nlctx);
+ if (val == 0 || val == (uint16_t)(-1) || val == (uint32_t)(-1))
+ printf("\tSpeed: Unknown!\n");
+ else
+ printf("\tSpeed: %uMb/s\n", val);
+ }
+ if (tb[ETHTOOL_A_LINKMODES_LANES]) {
+ uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKMODES_LANES]);
+
+ print_banner(nlctx);
+ printf("\tLanes: %u\n", val);
+ }
+ if (tb[ETHTOOL_A_LINKMODES_DUPLEX]) {
+ uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_DUPLEX]);
+
+ print_banner(nlctx);
+ print_enum(names_duplex, ARRAY_SIZE(names_duplex), val,
+ "Duplex");
+ }
+ if (tb[ETHTOOL_A_LINKMODES_AUTONEG]) {
+ int autoneg = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_AUTONEG]);
+
+ print_banner(nlctx);
+ printf("\tAuto-negotiation: %s\n",
+ (autoneg == AUTONEG_DISABLE) ? "off" : "on");
+ }
+ if (tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]) {
+ uint8_t val;
+
+ val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]);
+
+ print_banner(nlctx);
+ print_enum(names_master_slave_cfg,
+ ARRAY_SIZE(names_master_slave_cfg), val,
+ "master-slave cfg");
+ }
+ if (tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]) {
+ uint8_t val;
+
+ val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]);
+ print_banner(nlctx);
+ print_enum(names_master_slave_state,
+ ARRAY_SIZE(names_master_slave_state), val,
+ "master-slave status");
+ }
+
+ return MNL_CB_OK;
+err:
+ if (nlctx->is_monitor || nlctx->is_dump)
+ return MNL_CB_OK;
+ fputs("No data available\n", stdout);
+ nlctx->exit_code = 75;
+ return MNL_CB_ERROR;
+}
+
+int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_LINKINFO_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ int port = -1;
+ int ret;
+
+ if (nlctx->is_dump || nlctx->is_monitor)
+ nlctx->no_banner = false;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_LINKINFO_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (tb[ETHTOOL_A_LINKINFO_PORT]) {
+ uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_PORT]);
+
+ print_banner(nlctx);
+ print_enum(names_port, ARRAY_SIZE(names_port), val, "Port");
+ port = val;
+ }
+ if (tb[ETHTOOL_A_LINKINFO_PHYADDR]) {
+ uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_PHYADDR]);
+
+ print_banner(nlctx);
+ printf("\tPHYAD: %u\n", val);
+ }
+ if (tb[ETHTOOL_A_LINKINFO_TRANSCEIVER]) {
+ uint8_t val;
+
+ val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_TRANSCEIVER]);
+ print_banner(nlctx);
+ print_enum(names_transceiver, ARRAY_SIZE(names_transceiver),
+ val, "Transceiver");
+ }
+ if (tb[ETHTOOL_A_LINKINFO_TP_MDIX] && tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] &&
+ port == PORT_TP) {
+ uint8_t mdix = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_TP_MDIX]);
+ uint8_t mdix_ctrl =
+ mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL]);
+
+ print_banner(nlctx);
+ dump_mdix(mdix, mdix_ctrl);
+ }
+
+ return MNL_CB_OK;
+}
+
+static const char *get_enum_string(const char *const *string_table, unsigned int n_string_table,
+ unsigned int val)
+{
+ if (val >= n_string_table || !string_table[val])
+ return NULL;
+ else
+ return string_table[val];
+}
+
+static const char *const names_link_ext_state[] = {
+ [ETHTOOL_LINK_EXT_STATE_AUTONEG] = "Autoneg",
+ [ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE] = "Link training failure",
+ [ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH] = "Logical mismatch",
+ [ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY] = "Bad signal integrity",
+ [ETHTOOL_LINK_EXT_STATE_NO_CABLE] = "No cable",
+ [ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE] = "Cable issue",
+ [ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE] = "EEPROM issue",
+ [ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE] = "Calibration failure",
+ [ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED] = "Power budget exceeded",
+ [ETHTOOL_LINK_EXT_STATE_OVERHEAT] = "Overheat",
+ [ETHTOOL_LINK_EXT_STATE_MODULE] = "Module",
+};
+
+static const char *const names_autoneg_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED] =
+ "No partner detected",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED] =
+ "Ack not received",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED] =
+ "Next page exchange failed",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE] =
+ "No partner detected during force mode",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE] =
+ "FEC mismatch during override",
+ [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD] =
+ "No HCD",
+};
+
+static const char *const names_link_training_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED] =
+ "KR frame lock not acquired",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT] =
+ "KR link inhibit timeout",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY] =
+ "KR Link partner did not set receiver ready",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT] =
+ "Remote side is not ready yet",
+};
+
+static const char *const names_link_logical_mismatch_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK] =
+ "PCS did not acquire block lock",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK] =
+ "PCS did not acquire AM lock",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS] =
+ "PCS did not get align_status",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED] =
+ "FC FEC is not locked",
+ [ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED] =
+ "RS FEC is not locked",
+};
+
+static const char *const names_bad_signal_integrity_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS] =
+ "Large number of physical errors",
+ [ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE] =
+ "Unsupported rate",
+ [ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_REFERENCE_CLOCK_LOST] =
+ "Serdes reference clock lost",
+ [ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_ALOS] =
+ "Serdes ALOS",
+};
+
+static const char *const names_cable_issue_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE] =
+ "Unsupported cable",
+ [ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE] =
+ "Cable test failure",
+};
+
+static const char *const names_module_link_ext_substate[] = {
+ [ETHTOOL_LINK_EXT_SUBSTATE_MODULE_CMIS_NOT_READY] =
+ "CMIS module is not in ModuleReady state",
+};
+
+static const char *link_ext_substate_get(uint8_t link_ext_state_val, uint8_t link_ext_substate_val)
+{
+ switch (link_ext_state_val) {
+ case ETHTOOL_LINK_EXT_STATE_AUTONEG:
+ return get_enum_string(names_autoneg_link_ext_substate,
+ ARRAY_SIZE(names_autoneg_link_ext_substate),
+ link_ext_substate_val);
+ case ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE:
+ return get_enum_string(names_link_training_link_ext_substate,
+ ARRAY_SIZE(names_link_training_link_ext_substate),
+ link_ext_substate_val);
+ case ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH:
+ return get_enum_string(names_link_logical_mismatch_link_ext_substate,
+ ARRAY_SIZE(names_link_logical_mismatch_link_ext_substate),
+ link_ext_substate_val);
+ case ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY:
+ return get_enum_string(names_bad_signal_integrity_link_ext_substate,
+ ARRAY_SIZE(names_bad_signal_integrity_link_ext_substate),
+ link_ext_substate_val);
+ case ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE:
+ return get_enum_string(names_cable_issue_link_ext_substate,
+ ARRAY_SIZE(names_cable_issue_link_ext_substate),
+ link_ext_substate_val);
+ case ETHTOOL_LINK_EXT_STATE_MODULE:
+ return get_enum_string(names_module_link_ext_substate,
+ ARRAY_SIZE(names_module_link_ext_substate),
+ link_ext_substate_val);
+ default:
+ return NULL;
+ }
+}
+
+static void linkstate_link_ext_substate_print(const struct nlattr *tb[],
+ uint8_t link_ext_state_val)
+{
+ uint8_t link_ext_substate_val;
+ const char *link_ext_substate_str;
+
+ if (!tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE])
+ return;
+
+ link_ext_substate_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE]);
+
+ link_ext_substate_str = link_ext_substate_get(link_ext_state_val, link_ext_substate_val);
+ if (!link_ext_substate_str)
+ printf(", %u", link_ext_substate_val);
+ else
+ printf(", %s", link_ext_substate_str);
+}
+
+static void linkstate_link_ext_state_print(const struct nlattr *tb[])
+{
+ uint8_t link_ext_state_val;
+ const char *link_ext_state_str;
+
+ if (!tb[ETHTOOL_A_LINKSTATE_EXT_STATE])
+ return;
+
+ link_ext_state_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_STATE]);
+
+ link_ext_state_str = get_enum_string(names_link_ext_state,
+ ARRAY_SIZE(names_link_ext_state),
+ link_ext_state_val);
+ if (!link_ext_state_str)
+ printf(" (%u", link_ext_state_val);
+ else
+ printf(" (%s", link_ext_state_str);
+
+ linkstate_link_ext_substate_print(tb, link_ext_state_val);
+ printf(")");
+}
+
+int linkstate_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_LINKSTATE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ int ret;
+
+ if (nlctx->is_dump || nlctx->is_monitor)
+ nlctx->no_banner = false;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_LINKSTATE_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (tb[ETHTOOL_A_LINKSTATE_LINK]) {
+ uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_LINK]);
+
+ print_banner(nlctx);
+ printf("\tLink detected: %s", val ? "yes" : "no");
+ linkstate_link_ext_state_print(tb);
+ printf("\n");
+ }
+
+ if (tb[ETHTOOL_A_LINKSTATE_SQI]) {
+ uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_SQI]);
+
+ print_banner(nlctx);
+ printf("\tSQI: %u", val);
+
+ if (tb[ETHTOOL_A_LINKSTATE_SQI_MAX]) {
+ uint32_t max;
+
+ max = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_SQI_MAX]);
+ printf("/%u\n", max);
+ } else {
+ printf("\n");
+ }
+ }
+
+ if (tb[ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT]) {
+ uint32_t val;
+
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT]);
+ printf("\tLink Down Events: %u\n", val);
+ }
+
+ return MNL_CB_OK;
+}
+
+void wol_modes_cb(unsigned int idx, const char *name __maybe_unused, bool val,
+ void *data)
+{
+ struct ethtool_wolinfo *wol = data;
+
+ if (idx >= 32)
+ return;
+ wol->supported |= (1U << idx);
+ if (val)
+ wol->wolopts |= (1U << idx);
+}
+
+int wol_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_WOL_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ struct ethtool_wolinfo wol = {};
+ int ret;
+
+ if (nlctx->is_dump || nlctx->is_monitor)
+ nlctx->no_banner = false;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_WOL_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (tb[ETHTOOL_A_WOL_MODES])
+ walk_bitset(tb[ETHTOOL_A_WOL_MODES], NULL, wol_modes_cb, &wol);
+ if (tb[ETHTOOL_A_WOL_SOPASS]) {
+ unsigned int len;
+
+ len = mnl_attr_get_payload_len(tb[ETHTOOL_A_WOL_SOPASS]);
+ if (len != SOPASS_MAX)
+ fprintf(stderr, "invalid SecureOn password length %u (should be %u)\n",
+ len, SOPASS_MAX);
+ else
+ memcpy(wol.sopass,
+ mnl_attr_get_payload(tb[ETHTOOL_A_WOL_SOPASS]),
+ SOPASS_MAX);
+ }
+ print_banner(nlctx);
+ dump_wol(&wol);
+
+ return MNL_CB_OK;
+}
+
+void msgmask_cb(unsigned int idx, const char *name __maybe_unused, bool val,
+ void *data)
+{
+ u32 *msg_mask = data;
+
+ if (idx >= 32)
+ return;
+ if (val)
+ *msg_mask |= (1U << idx);
+}
+
+void msgmask_cb2(unsigned int idx __maybe_unused, const char *name,
+ bool val, void *data __maybe_unused)
+{
+ if (val)
+ printf(" %s", name);
+}
+
+int debug_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_DEBUG_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const struct stringset *msgmask_strings = NULL;
+ struct nl_context *nlctx = data;
+ u32 msg_mask = 0;
+ int ret;
+
+ if (nlctx->is_dump || nlctx->is_monitor)
+ nlctx->no_banner = false;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_DEBUG_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (!tb[ETHTOOL_A_DEBUG_MSGMASK])
+ return MNL_CB_OK;
+ if (bitset_is_compact(tb[ETHTOOL_A_DEBUG_MSGMASK])) {
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return MNL_CB_OK;
+ msgmask_strings = global_stringset(ETH_SS_MSG_CLASSES,
+ nlctx->ethnl2_socket);
+ }
+
+ print_banner(nlctx);
+ walk_bitset(tb[ETHTOOL_A_DEBUG_MSGMASK], NULL, msgmask_cb, &msg_mask);
+ printf(" Current message level: 0x%08x (%u)\n"
+ " ",
+ msg_mask, msg_mask);
+ walk_bitset(tb[ETHTOOL_A_DEBUG_MSGMASK], msgmask_strings, msgmask_cb2,
+ NULL);
+ fputc('\n', stdout);
+
+ return MNL_CB_OK;
+}
+
+int plca_cfg_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ int ret;
+
+ if (nlctx->is_dump || nlctx->is_monitor)
+ nlctx->no_banner = false;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ print_banner(nlctx);
+ printf("\tPLCA support: ");
+
+ if (tb[ETHTOOL_A_PLCA_VERSION]) {
+ uint16_t val = mnl_attr_get_u16(tb[ETHTOOL_A_PLCA_VERSION]);
+
+ printf("OPEN Alliance v%u.%u",
+ (unsigned int)((val >> 4) & 0xF),
+ (unsigned int)(val & 0xF));
+ } else
+ printf("non-standard");
+
+ printf("\n");
+
+ return MNL_CB_OK;
+}
+
+int plca_status_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ int ret;
+
+ if (nlctx->is_dump || nlctx->is_monitor)
+ nlctx->no_banner = false;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ print_banner(nlctx);
+ printf("\tPLCA status: ");
+
+ if (tb[ETHTOOL_A_PLCA_STATUS]) {
+ uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_PLCA_STATUS]);
+
+ printf(val ? "up" : "down");
+ } else
+ printf("unknown");
+
+ printf("\n");
+
+ return MNL_CB_OK;
+}
+
+static int gset_request(struct cmd_context *ctx, uint8_t msg_type,
+ uint16_t hdr_attr, mnl_cb_t cb)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ u32 flags;
+ int ret;
+
+ if (netlink_cmd_check(ctx, msg_type, true))
+ return 0;
+
+ flags = get_stats_flag(nlctx, msg_type, hdr_attr);
+
+ ret = nlsock_prep_get_request(nlsk, msg_type, hdr_attr, flags);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, cb);
+}
+
+int nl_gset(struct cmd_context *ctx)
+{
+ int ret;
+
+ /* Check for the base set of commands */
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_LINKMODES_GET, true) ||
+ netlink_cmd_check(ctx, ETHTOOL_MSG_LINKINFO_GET, true) ||
+ netlink_cmd_check(ctx, ETHTOOL_MSG_WOL_GET, true) ||
+ netlink_cmd_check(ctx, ETHTOOL_MSG_DEBUG_GET, true) ||
+ netlink_cmd_check(ctx, ETHTOOL_MSG_LINKSTATE_GET, true))
+ return -EOPNOTSUPP;
+
+ ctx->nlctx->suppress_nlerr = 1;
+
+ ret = gset_request(ctx, ETHTOOL_MSG_LINKMODES_GET,
+ ETHTOOL_A_LINKMODES_HEADER, linkmodes_reply_cb);
+ if (ret == -ENODEV)
+ return ret;
+
+ ret = gset_request(ctx, ETHTOOL_MSG_LINKINFO_GET,
+ ETHTOOL_A_LINKINFO_HEADER, linkinfo_reply_cb);
+ if (ret == -ENODEV)
+ return ret;
+
+ ret = gset_request(ctx, ETHTOOL_MSG_WOL_GET, ETHTOOL_A_WOL_HEADER,
+ wol_reply_cb);
+ if (ret == -ENODEV)
+ return ret;
+
+ ret = gset_request(ctx, ETHTOOL_MSG_PLCA_GET_CFG,
+ ETHTOOL_A_PLCA_HEADER, plca_cfg_reply_cb);
+ if (ret == -ENODEV)
+ return ret;
+
+ ret = gset_request(ctx, ETHTOOL_MSG_DEBUG_GET, ETHTOOL_A_DEBUG_HEADER,
+ debug_reply_cb);
+ if (ret == -ENODEV)
+ return ret;
+
+ ret = gset_request(ctx, ETHTOOL_MSG_LINKSTATE_GET,
+ ETHTOOL_A_LINKSTATE_HEADER, linkstate_reply_cb);
+ if (ret == -ENODEV)
+ return ret;
+
+ ret = gset_request(ctx, ETHTOOL_MSG_PLCA_GET_STATUS,
+ ETHTOOL_A_PLCA_HEADER, plca_status_reply_cb);
+ if (ret == -ENODEV)
+ return ret;
+
+ if (!ctx->nlctx->no_banner) {
+ printf("No data available\n");
+ return 75;
+ }
+
+ return 0;
+}
+
+/* SET_SETTINGS */
+
+enum {
+ WAKE_PHY_BIT = 0,
+ WAKE_UCAST_BIT = 1,
+ WAKE_MCAST_BIT = 2,
+ WAKE_BCAST_BIT = 3,
+ WAKE_ARP_BIT = 4,
+ WAKE_MAGIC_BIT = 5,
+ WAKE_MAGICSECURE_BIT = 6,
+ WAKE_FILTER_BIT = 7,
+};
+
+#define WAKE_ALL (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_ARP | \
+ WAKE_MAGIC | WAKE_MAGICSECURE)
+
+static const struct lookup_entry_u8 port_values[] = {
+ { .arg = "tp", .val = PORT_TP },
+ { .arg = "aui", .val = PORT_AUI },
+ { .arg = "mii", .val = PORT_MII },
+ { .arg = "fibre", .val = PORT_FIBRE },
+ { .arg = "bnc", .val = PORT_BNC },
+ { .arg = "da", .val = PORT_DA },
+ {}
+};
+
+static const struct lookup_entry_u8 mdix_values[] = {
+ { .arg = "auto", .val = ETH_TP_MDI_AUTO },
+ { .arg = "on", .val = ETH_TP_MDI_X },
+ { .arg = "off", .val = ETH_TP_MDI },
+ {}
+};
+
+static const struct error_parser_data xcvr_parser_data = {
+ .err_msg = "deprecated parameter '%s' not supported by kernel\n",
+ .ret_val = -EINVAL,
+ .extra_args = 1,
+};
+
+static const struct lookup_entry_u8 autoneg_values[] = {
+ { .arg = "off", .val = AUTONEG_DISABLE },
+ { .arg = "on", .val = AUTONEG_ENABLE },
+ {}
+};
+
+static const struct bitset_parser_data advertise_parser_data = {
+ .no_mask = false,
+ .force_hex = true,
+};
+
+static const struct lookup_entry_u8 duplex_values[] = {
+ { .arg = "half", .val = DUPLEX_HALF },
+ { .arg = "full", .val = DUPLEX_FULL },
+ {}
+};
+
+static const struct lookup_entry_u8 master_slave_values[] = {
+ { .arg = "preferred-master", .val = MASTER_SLAVE_CFG_MASTER_PREFERRED },
+ { .arg = "preferred-slave", .val = MASTER_SLAVE_CFG_SLAVE_PREFERRED },
+ { .arg = "forced-master", .val = MASTER_SLAVE_CFG_MASTER_FORCE },
+ { .arg = "forced-slave", .val = MASTER_SLAVE_CFG_SLAVE_FORCE },
+ {}
+};
+
+char wol_bit_chars[WOL_MODE_COUNT] = {
+ [WAKE_PHY_BIT] = 'p',
+ [WAKE_UCAST_BIT] = 'u',
+ [WAKE_MCAST_BIT] = 'm',
+ [WAKE_BCAST_BIT] = 'b',
+ [WAKE_ARP_BIT] = 'a',
+ [WAKE_MAGIC_BIT] = 'g',
+ [WAKE_MAGICSECURE_BIT] = 's',
+ [WAKE_FILTER_BIT] = 'f',
+};
+
+const struct char_bitset_parser_data wol_parser_data = {
+ .bit_chars = wol_bit_chars,
+ .nbits = WOL_MODE_COUNT,
+ .reset_char = 'd',
+};
+
+const struct byte_str_parser_data sopass_parser_data = {
+ .min_len = 6,
+ .max_len = 6,
+ .delim = ':',
+};
+
+static const struct bitset_parser_data msglvl_parser_data = {
+ .no_mask = false,
+ .force_hex = false,
+};
+
+static const struct param_parser sset_params[] = {
+ {
+ .arg = "port",
+ .group = ETHTOOL_MSG_LINKINFO_SET,
+ .type = ETHTOOL_A_LINKINFO_PORT,
+ .handler = nl_parse_lookup_u8,
+ .handler_data = port_values,
+ .min_argc = 1,
+ },
+ {
+ .arg = "mdix",
+ .group = ETHTOOL_MSG_LINKINFO_SET,
+ .type = ETHTOOL_A_LINKINFO_TP_MDIX_CTRL,
+ .handler = nl_parse_lookup_u8,
+ .handler_data = mdix_values,
+ .min_argc = 1,
+ },
+ {
+ .arg = "phyad",
+ .group = ETHTOOL_MSG_LINKINFO_SET,
+ .type = ETHTOOL_A_LINKINFO_PHYADDR,
+ .handler = nl_parse_direct_u8,
+ .min_argc = 1,
+ },
+ {
+ .arg = "xcvr",
+ .group = ETHTOOL_MSG_LINKINFO_SET,
+ .handler = nl_parse_error,
+ .handler_data = &xcvr_parser_data,
+ .min_argc = 1,
+ },
+ {
+ .arg = "autoneg",
+ .group = ETHTOOL_MSG_LINKMODES_SET,
+ .type = ETHTOOL_A_LINKMODES_AUTONEG,
+ .handler = nl_parse_lookup_u8,
+ .handler_data = autoneg_values,
+ .min_argc = 1,
+ },
+ {
+ .arg = "advertise",
+ .group = ETHTOOL_MSG_LINKMODES_SET,
+ .type = ETHTOOL_A_LINKMODES_OURS,
+ .handler = nl_parse_bitset,
+ .handler_data = &advertise_parser_data,
+ .min_argc = 1,
+ },
+ {
+ .arg = "speed",
+ .group = ETHTOOL_MSG_LINKMODES_SET,
+ .type = ETHTOOL_A_LINKMODES_SPEED,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "lanes",
+ .group = ETHTOOL_MSG_LINKMODES_SET,
+ .type = ETHTOOL_A_LINKMODES_LANES,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "duplex",
+ .group = ETHTOOL_MSG_LINKMODES_SET,
+ .type = ETHTOOL_A_LINKMODES_DUPLEX,
+ .handler = nl_parse_lookup_u8,
+ .handler_data = duplex_values,
+ .min_argc = 1,
+ },
+ {
+ .arg = "master-slave",
+ .group = ETHTOOL_MSG_LINKMODES_SET,
+ .type = ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG,
+ .handler = nl_parse_lookup_u8,
+ .handler_data = master_slave_values,
+ .min_argc = 1,
+ },
+ {
+ .arg = "wol",
+ .group = ETHTOOL_MSG_WOL_SET,
+ .type = ETHTOOL_A_WOL_MODES,
+ .handler = nl_parse_char_bitset,
+ .handler_data = &wol_parser_data,
+ .min_argc = 1,
+ },
+ {
+ .arg = "sopass",
+ .group = ETHTOOL_MSG_WOL_SET,
+ .type = ETHTOOL_A_WOL_SOPASS,
+ .handler = nl_parse_byte_str,
+ .handler_data = &sopass_parser_data,
+ .min_argc = 1,
+ },
+ {
+ .arg = "msglvl",
+ .group = ETHTOOL_MSG_DEBUG_SET,
+ .type = ETHTOOL_A_DEBUG_MSGMASK,
+ .handler = nl_parse_bitset,
+ .handler_data = &msglvl_parser_data,
+ .min_argc = 1,
+ },
+ {}
+};
+
+/* Maximum number of request messages sent to kernel; must be equal to the
+ * number of different .group values in sset_params[] array.
+ */
+#define SSET_MAX_MSGS 4
+
+static int linkmodes_reply_advert_all_cb(const struct nlmsghdr *nlhdr,
+ void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_msg_buff *req_msgbuff = data;
+ const struct nlattr *ours_attr;
+ struct nlattr *req_bitset;
+ uint32_t *supported_modes;
+ unsigned int modes_count;
+ unsigned int i;
+ int ret;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+ ours_attr = tb[ETHTOOL_A_LINKMODES_OURS];
+ if (!ours_attr)
+ return MNL_CB_ERROR;
+ modes_count = bitset_get_count(tb[ETHTOOL_A_LINKMODES_OURS], &ret);
+ if (ret < 0)
+ return MNL_CB_ERROR;
+ supported_modes = get_compact_bitset_mask(tb[ETHTOOL_A_LINKMODES_OURS]);
+ if (!supported_modes)
+ return MNL_CB_ERROR;
+
+ /* keep only "real" link modes */
+ for (i = 0; i < modes_count; i++)
+ if (!lm_class_match(i, LM_CLASS_REAL))
+ supported_modes[i / 32] &= ~((uint32_t)1 << (i % 32));
+
+ req_bitset = ethnla_nest_start(req_msgbuff, ETHTOOL_A_LINKMODES_OURS);
+ if (!req_bitset)
+ return MNL_CB_ERROR;
+
+ if (ethnla_put_u32(req_msgbuff, ETHTOOL_A_BITSET_SIZE, modes_count) ||
+ ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_VALUE,
+ DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
+ supported_modes) ||
+ ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_MASK,
+ DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
+ supported_modes)) {
+ ethnla_nest_cancel(req_msgbuff, req_bitset);
+ return MNL_CB_ERROR;
+ }
+
+ ethnla_nest_end(req_msgbuff, req_bitset);
+ return MNL_CB_OK;
+}
+
+/* For compatibility reasons with ioctl-based ethtool, when "autoneg on" is
+ * specified without "advertise", "speed", "duplex" and "lanes", we need to
+ * query the supported link modes from the kernel and advertise all the "real"
+ * ones.
+ */
+static int nl_sset_compat_linkmodes(struct nl_context *nlctx,
+ struct nl_msg_buff *msgbuff)
+{
+ const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ ret = mnl_attr_parse(msgbuff->nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ if (!tb[ETHTOOL_A_LINKMODES_AUTONEG] || tb[ETHTOOL_A_LINKMODES_OURS] ||
+ tb[ETHTOOL_A_LINKMODES_SPEED] || tb[ETHTOOL_A_LINKMODES_DUPLEX] ||
+ tb[ETHTOOL_A_LINKMODES_LANES])
+ return 0;
+ if (!mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_AUTONEG]))
+ return 0;
+
+ /* all conditions satisfied, create ETHTOOL_A_LINKMODES_OURS */
+ if (netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_GET, false) ||
+ netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_SET, false))
+ return -EOPNOTSUPP;
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_LINKMODES_GET,
+ ETHTOOL_A_LINKMODES_HEADER,
+ ETHTOOL_FLAG_COMPACT_BITSETS);
+ if (ret < 0)
+ return ret;
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return ret;
+ return nlsock_process_reply(nlsk, linkmodes_reply_advert_all_cb,
+ msgbuff);
+}
+
+int nl_sset(struct cmd_context *ctx)
+{
+ struct nl_msg_buff *msgbuffs[SSET_MAX_MSGS] = {};
+ struct nl_context *nlctx = ctx->nlctx;
+ unsigned int i;
+ int ret;
+
+ nlctx->cmd = "-s";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+
+ ret = nl_parser(nlctx, sset_params, NULL, PARSER_GROUP_MSG, msgbuffs);
+ if (ret == -EOPNOTSUPP)
+ return ret;
+
+ if (ret < 0) {
+ ret = 1;
+ goto out_free;
+ }
+
+ for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+
+ if (msgbuffs[i]->genlhdr->cmd == ETHTOOL_MSG_LINKMODES_SET) {
+ ret = nl_sset_compat_linkmodes(nlctx, msgbuffs[i]);
+ if (ret < 0)
+ goto out_free;
+ }
+ ret = nlsock_sendmsg(nlsk, msgbuffs[i]);
+ if (ret < 0)
+ goto out_free;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, NULL);
+ if (ret < 0)
+ goto out_free;
+ }
+
+out_free:
+ for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
+ msgbuff_done(msgbuffs[i]);
+ free(msgbuffs[i]);
+ }
+ if (ret >= 0)
+ return ret;
+ return nlctx->exit_code ?: 75;
+}
diff --git a/netlink/stats.c b/netlink/stats.c
new file mode 100644
index 0000000..8620d8d
--- /dev/null
+++ b/netlink/stats.c
@@ -0,0 +1,333 @@
+/*
+ * stats.c - netlink implementation of stats
+ *
+ * Implementation of "ethtool -S <dev> [--groups <types>] etc."
+ */
+
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+#include "strset.h"
+
+static int parse_rmon_hist_one(const char *grp_name, const struct nlattr *hist,
+ const char *dir)
+{
+ const struct nlattr *tb[ETHTOOL_A_STATS_GRP_HIST_VAL + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned long long val;
+ unsigned int low, hi;
+ int ret;
+
+ ret = mnl_attr_parse_nested(hist, attr_cb, &tb_info);
+ if (ret < 0) {
+ fprintf(stderr, "invalid kernel response - malformed histogram entry\n");
+ return 1;
+ }
+
+ if (!tb[ETHTOOL_A_STATS_GRP_HIST_BKT_LOW] ||
+ !tb[ETHTOOL_A_STATS_GRP_HIST_BKT_HI] ||
+ !tb[ETHTOOL_A_STATS_GRP_HIST_VAL]) {
+ fprintf(stderr, "invalid kernel response - histogram entry missing attributes\n");
+ return 1;
+ }
+
+ low = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_HIST_BKT_LOW]);
+ hi = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_HIST_BKT_HI]);
+ val = mnl_attr_get_u64(tb[ETHTOOL_A_STATS_GRP_HIST_VAL]);
+
+ if (!is_json_context()) {
+ fprintf(stdout, "%s-%s-etherStatsPkts", dir, grp_name);
+
+ if (low && hi) {
+ fprintf(stdout, "%uto%uOctets: %llu\n", low, hi, val);
+ } else if (hi) {
+ fprintf(stdout, "%uOctets: %llu\n", hi, val);
+ } else if (low) {
+ fprintf(stdout, "%utoMaxOctets: %llu\n", low, val);
+ } else {
+ fprintf(stderr, "invalid kernel response - bad histogram entry bounds\n");
+ return 1;
+ }
+ } else {
+ open_json_object(NULL);
+ print_uint(PRINT_JSON, "low", NULL, low);
+ print_uint(PRINT_JSON, "high", NULL, hi);
+ print_u64(PRINT_JSON, "val", NULL, val);
+ close_json_object();
+ }
+
+ return 0;
+}
+
+static int parse_rmon_hist(const struct nlattr *grp, const char *grp_name,
+ const char *name, const char *dir, unsigned int type)
+{
+ const struct nlattr *attr;
+
+ open_json_array(name, "");
+
+ mnl_attr_for_each_nested(attr, grp) {
+ if (mnl_attr_get_type(attr) == type &&
+ parse_rmon_hist_one(grp_name, attr, dir))
+ goto err_close_rmon;
+ }
+ close_json_array("");
+
+ return 0;
+
+err_close_rmon:
+ close_json_array("");
+ return 1;
+}
+
+static int parse_grp(struct nl_context *nlctx, const struct nlattr *grp,
+ const struct stringset *std_str)
+{
+ const struct nlattr *tb[ETHTOOL_A_STATS_GRP_SS_ID + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ bool hist_rx = false, hist_tx = false;
+ const struct stringset *stat_str;
+ const struct nlattr *attr, *stat;
+ const char *std_name, *name;
+ unsigned int ss_id, id, s;
+ unsigned long long val;
+ int ret;
+
+ ret = mnl_attr_parse_nested(grp, attr_cb, &tb_info);
+ if (ret < 0)
+ return 1;
+
+ if (!tb[ETHTOOL_A_STATS_GRP_ID])
+ return 1;
+ if (!tb[ETHTOOL_A_STATS_GRP_SS_ID])
+ return 0;
+
+ id = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_ID]);
+ ss_id = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_SS_ID]);
+
+ stat_str = global_stringset(ss_id, nlctx->ethnl2_socket);
+
+ std_name = get_string(std_str, id);
+ open_json_object(std_name);
+
+ mnl_attr_for_each_nested(attr, grp) {
+ switch (mnl_attr_get_type(attr)) {
+ case ETHTOOL_A_STATS_GRP_STAT:
+ break;
+ case ETHTOOL_A_STATS_GRP_HIST_RX:
+ hist_rx = true;
+ continue;
+ case ETHTOOL_A_STATS_GRP_HIST_TX:
+ hist_tx = true;
+ continue;
+ default:
+ continue;
+ }
+
+ stat = mnl_attr_get_payload(attr);
+ ret = mnl_attr_validate(stat, MNL_TYPE_U64);
+ if (ret) {
+ fprintf(stderr, "invalid kernel response - bad statistic entry\n");
+ goto err_close_grp;
+ }
+ s = mnl_attr_get_type(stat);
+ name = get_string(stat_str, s);
+ if (!name || !name[0])
+ continue;
+
+ if (!is_json_context())
+ fprintf(stdout, "%s-%s: ", std_name, name);
+
+ val = mnl_attr_get_u64(stat);
+ print_u64(PRINT_ANY, name, "%llu\n", val);
+ }
+
+ if (hist_rx)
+ parse_rmon_hist(grp, std_name, "rx-pktsNtoM", "rx",
+ ETHTOOL_A_STATS_GRP_HIST_RX);
+ if (hist_tx)
+ parse_rmon_hist(grp, std_name, "tx-pktsNtoM", "tx",
+ ETHTOOL_A_STATS_GRP_HIST_TX);
+
+ close_json_object();
+
+ return 0;
+
+err_close_grp:
+ close_json_object();
+ return 1;
+}
+
+static int stats_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_STATS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ const struct stringset *std_str;
+ const struct nlattr *attr;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_STATS_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return err_ret;
+ std_str = global_stringset(ETH_SS_STATS_STD, nlctx->ethnl2_socket);
+
+ if (silent)
+ print_nl();
+
+ open_json_object(NULL);
+
+ print_string(PRINT_ANY, "ifname", "Standard stats for %s:\n",
+ nlctx->devname);
+
+ mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
+ if (mnl_attr_get_type(attr) == ETHTOOL_A_STATS_GRP) {
+ ret = parse_grp(nlctx, attr, std_str);
+ if (ret)
+ goto err_close_dev;
+ }
+ }
+
+ close_json_object();
+
+ return MNL_CB_OK;
+
+err_close_dev:
+ close_json_object();
+ return err_ret;
+}
+
+static const struct bitset_parser_data stats_parser_data = {
+ .no_mask = true,
+ .force_hex = false,
+};
+
+static int stats_parse_all_groups(struct nl_context *nlctx, uint16_t type,
+ const void *data, struct nl_msg_buff *msgbuff,
+ void *dest)
+{
+ const struct stringset *std_str;
+ struct nlattr *nest;
+ int i, ret, nbits;
+ uint32_t *bits;
+
+ if (data || dest)
+ return -EFAULT;
+
+ /* ethnl2 and strset code already does caching */
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return ret;
+ std_str = global_stringset(ETH_SS_STATS_STD, nlctx->ethnl2_socket);
+
+ nbits = get_count(std_str);
+ bits = calloc(DIV_ROUND_UP(nbits, 32), sizeof(uint32_t));
+ if (!bits)
+ return -ENOMEM;
+
+ for (i = 0; i < nbits; i++)
+ bits[i / 32] |= 1U << (i % 32);
+
+ ret = -EMSGSIZE;
+ nest = ethnla_nest_start(msgbuff, type);
+ if (!nest)
+ goto err_free;
+
+ if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true) ||
+ ethnla_put_u32(msgbuff, ETHTOOL_A_BITSET_SIZE, nbits) ||
+ ethnla_put(msgbuff, ETHTOOL_A_BITSET_VALUE,
+ DIV_ROUND_UP(nbits, 32) * sizeof(uint32_t), bits))
+ goto err_cancel;
+
+ ethnla_nest_end(msgbuff, nest);
+ free(bits);
+ return 0;
+
+err_cancel:
+ ethnla_nest_cancel(msgbuff, nest);
+err_free:
+ free(bits);
+ return ret;
+}
+
+static const struct lookup_entry_u32 stats_src_values[] = {
+ { .arg = "aggregate", .val = ETHTOOL_MAC_STATS_SRC_AGGREGATE },
+ { .arg = "emac", .val = ETHTOOL_MAC_STATS_SRC_EMAC },
+ { .arg = "pmac", .val = ETHTOOL_MAC_STATS_SRC_PMAC },
+ {}
+};
+
+static const struct param_parser stats_params[] = {
+ {
+ .arg = "--groups",
+ .type = ETHTOOL_A_STATS_GROUPS,
+ .handler = nl_parse_bitset,
+ .handler_data = &stats_parser_data,
+ .min_argc = 1,
+ .alt_group = 1,
+ },
+ {
+ .arg = "--all-groups",
+ .type = ETHTOOL_A_STATS_GROUPS,
+ .handler = stats_parse_all_groups,
+ .alt_group = 1,
+ },
+ {
+ .arg = "--src",
+ .type = ETHTOOL_A_STATS_SRC,
+ .handler = nl_parse_lookup_u32,
+ .handler_data = stats_src_values,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_gstats(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_STATS_GET,
+ ETHTOOL_A_STATS_HEADER, 0);
+ if (ret < 0)
+ return ret;
+
+ nlctx->cmd = "-S";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+
+ ret = nl_parser(nlctx, stats_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ new_json_obj(ctx->json);
+ ret = nlsock_send_get_request(nlsk, stats_reply_cb);
+ delete_json_obj();
+ return ret;
+}
+
+bool nl_gstats_chk(struct cmd_context *ctx)
+{
+ return ctx->argc;
+}
diff --git a/netlink/strset.c b/netlink/strset.c
new file mode 100644
index 0000000..949d597
--- /dev/null
+++ b/netlink/strset.c
@@ -0,0 +1,297 @@
+/*
+ * strset.c - string set handling
+ *
+ * Implementation of local cache of ethtool string sets.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "../internal.h"
+#include "netlink.h"
+#include "nlsock.h"
+#include "msgbuff.h"
+
+struct stringset {
+ const char **strings;
+ void *raw_data;
+ unsigned int count;
+ bool loaded;
+};
+
+struct perdev_strings {
+ int ifindex;
+ char devname[ALTIFNAMSIZ];
+ struct stringset strings[ETH_SS_COUNT];
+ struct perdev_strings *next;
+};
+
+/* universal string sets */
+static struct stringset global_strings[ETH_SS_COUNT];
+/* linked list of string sets related to network devices */
+static struct perdev_strings *device_strings;
+
+static void drop_stringset(struct stringset *set)
+{
+ if (!set)
+ return;
+
+ free(set->strings);
+ free(set->raw_data);
+ memset(set, 0, sizeof(*set));
+}
+
+static int import_stringset(struct stringset *dest, const struct nlattr *nest)
+{
+ const struct nlattr *tb_stringset[ETHTOOL_A_STRINGSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb_stringset);
+ const struct nlattr *string;
+ unsigned int size;
+ unsigned int count;
+ unsigned int idx;
+ int ret;
+
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_stringset_info);
+ if (ret < 0)
+ return ret;
+ if (!tb_stringset[ETHTOOL_A_STRINGSET_ID] ||
+ !tb_stringset[ETHTOOL_A_STRINGSET_COUNT] ||
+ !tb_stringset[ETHTOOL_A_STRINGSET_STRINGS])
+ return -EFAULT;
+ idx = mnl_attr_get_u32(tb_stringset[ETHTOOL_A_STRINGSET_ID]);
+ if (idx >= ETH_SS_COUNT)
+ return 0;
+ if (dest[idx].loaded)
+ drop_stringset(&dest[idx]);
+ dest[idx].loaded = true;
+ count = mnl_attr_get_u32(tb_stringset[ETHTOOL_A_STRINGSET_COUNT]);
+ if (count == 0)
+ return 0;
+
+ size = mnl_attr_get_len(tb_stringset[ETHTOOL_A_STRINGSET_STRINGS]);
+ ret = -ENOMEM;
+ dest[idx].raw_data = malloc(size);
+ if (!dest[idx].raw_data)
+ goto err;
+ memcpy(dest[idx].raw_data, tb_stringset[ETHTOOL_A_STRINGSET_STRINGS],
+ size);
+ dest[idx].strings = calloc(count, sizeof(dest[idx].strings[0]));
+ if (!dest[idx].strings)
+ goto err;
+ dest[idx].count = count;
+
+ nest = dest[idx].raw_data;
+ mnl_attr_for_each_nested(string, nest) {
+ const struct nlattr *tb[ETHTOOL_A_STRING_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned int i;
+
+ if (mnl_attr_get_type(string) != ETHTOOL_A_STRINGS_STRING)
+ continue;
+ ret = mnl_attr_parse_nested(string, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+ ret = -EFAULT;
+ if (!tb[ETHTOOL_A_STRING_INDEX] || !tb[ETHTOOL_A_STRING_VALUE])
+ goto err;
+
+ i = mnl_attr_get_u32(tb[ETHTOOL_A_STRING_INDEX]);
+ if (i >= count)
+ goto err;
+ dest[idx].strings[i] =
+ mnl_attr_get_payload(tb[ETHTOOL_A_STRING_VALUE]);
+ }
+
+ return 0;
+err:
+ drop_stringset(&dest[idx]);
+ return ret;
+}
+
+static struct perdev_strings *get_perdev_by_ifindex(int ifindex)
+{
+ struct perdev_strings *perdev = device_strings;
+
+ while (perdev && perdev->ifindex != ifindex)
+ perdev = perdev->next;
+ if (perdev)
+ return perdev;
+
+ /* not found, allocate and insert into list */
+ perdev = calloc(1, sizeof(*perdev));
+ if (!perdev)
+ return NULL;
+ perdev->ifindex = ifindex;
+ perdev->next = device_strings;
+ device_strings = perdev;
+
+ return perdev;
+}
+
+static int strset_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_STRSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ char devname[ALTIFNAMSIZ] = "";
+ struct stringset *dest;
+ struct nlattr *attr;
+ int ifindex = 0;
+ int ret;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ if (tb[ETHTOOL_A_STRSET_HEADER]) {
+ ret = get_dev_info(tb[ETHTOOL_A_STRSET_HEADER], &ifindex,
+ devname);
+ if (ret < 0)
+ return MNL_CB_OK;
+ if (ifindex && nlctx->filter_devname &&
+ strncmp(devname, nlctx->filter_devname, ALTIFNAMSIZ))
+ return MNL_CB_OK;
+ }
+
+ if (ifindex) {
+ struct perdev_strings *perdev;
+
+ perdev = get_perdev_by_ifindex(ifindex);
+ if (!perdev)
+ return MNL_CB_OK;
+ copy_devname(perdev->devname, devname);
+ dest = perdev->strings;
+ } else {
+ dest = global_strings;
+ }
+
+ if (!tb[ETHTOOL_A_STRSET_STRINGSETS])
+ return MNL_CB_OK;
+ mnl_attr_for_each_nested(attr, tb[ETHTOOL_A_STRSET_STRINGSETS]) {
+ if (mnl_attr_get_type(attr) ==
+ ETHTOOL_A_STRINGSETS_STRINGSET)
+ import_stringset(dest, attr);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int fill_stringset_id(struct nl_msg_buff *msgbuff, unsigned int type)
+{
+ struct nlattr *nest_sets;
+ struct nlattr *nest_set;
+
+ nest_sets = ethnla_nest_start(msgbuff, ETHTOOL_A_STRSET_STRINGSETS);
+ if (!nest_sets)
+ return -EMSGSIZE;
+ nest_set = ethnla_nest_start(msgbuff, ETHTOOL_A_STRINGSETS_STRINGSET);
+ if (!nest_set)
+ goto err;
+ if (ethnla_put_u32(msgbuff, ETHTOOL_A_STRINGSET_ID, type))
+ goto err;
+ ethnla_nest_end(msgbuff, nest_set);
+ ethnla_nest_end(msgbuff, nest_sets);
+ return 0;
+
+err:
+ ethnla_nest_cancel(msgbuff, nest_sets);
+ return -EMSGSIZE;
+}
+
+static int stringset_load_request(struct nl_socket *nlsk, const char *devname,
+ int type, bool is_dump)
+{
+ struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+ int ret;
+
+ ret = msg_init(nlsk->nlctx, msgbuff, ETHTOOL_MSG_STRSET_GET,
+ NLM_F_REQUEST | NLM_F_ACK | (is_dump ? NLM_F_DUMP : 0));
+ if (ret < 0)
+ return ret;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_STRSET_HEADER, devname, 0))
+ return -EMSGSIZE;
+ if (type >= 0) {
+ ret = fill_stringset_id(msgbuff, type);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = nlsock_send_get_request(nlsk, strset_reply_cb);
+ return ret;
+}
+
+/* interface */
+
+const struct stringset *global_stringset(unsigned int type,
+ struct nl_socket *nlsk)
+{
+ int ret;
+
+ if (type >= ETH_SS_COUNT)
+ return NULL;
+ if (global_strings[type].loaded)
+ return &global_strings[type];
+ ret = stringset_load_request(nlsk, NULL, type, false);
+ return ret < 0 ? NULL : &global_strings[type];
+}
+
+const struct stringset *perdev_stringset(const char *devname, unsigned int type,
+ struct nl_socket *nlsk)
+{
+ const struct perdev_strings *p;
+ int ret;
+
+ if (type >= ETH_SS_COUNT)
+ return NULL;
+ for (p = device_strings; p; p = p->next)
+ if (!strcmp(p->devname, devname))
+ return &p->strings[type];
+
+ ret = stringset_load_request(nlsk, devname, type, false);
+ if (ret < 0)
+ return NULL;
+ for (p = device_strings; p; p = p->next)
+ if (!strcmp(p->devname, devname))
+ return &p->strings[type];
+
+ return NULL;
+}
+
+unsigned int get_count(const struct stringset *set)
+{
+ return set->count;
+}
+
+const char *get_string(const struct stringset *set, unsigned int idx)
+{
+ if (!set || idx >= set->count)
+ return NULL;
+ return set->strings[idx];
+}
+
+int preload_global_strings(struct nl_socket *nlsk)
+{
+ return stringset_load_request(nlsk, NULL, -1, false);
+}
+
+int preload_perdev_strings(struct nl_socket *nlsk, const char *dev)
+{
+ return stringset_load_request(nlsk, dev, -1, !dev);
+}
+
+void cleanup_all_strings(void)
+{
+ struct perdev_strings *perdev;
+ unsigned int i;
+
+ for (i = 0; i < ETH_SS_COUNT; i++)
+ drop_stringset(&global_strings[i]);
+
+ perdev = device_strings;
+ while (perdev) {
+ device_strings = perdev->next;
+ for (i = 0; i < ETH_SS_COUNT; i++)
+ drop_stringset(&perdev->strings[i]);
+ free(perdev);
+ perdev = device_strings;
+ }
+}
diff --git a/netlink/strset.h b/netlink/strset.h
new file mode 100644
index 0000000..72a4a39
--- /dev/null
+++ b/netlink/strset.h
@@ -0,0 +1,25 @@
+/*
+ * strset.h - string set handling
+ *
+ * Interface for local cache of ethtool string sets.
+ */
+
+#ifndef ETHTOOL_NETLINK_STRSET_H__
+#define ETHTOOL_NETLINK_STRSET_H__
+
+struct nl_socket;
+struct stringset;
+
+const struct stringset *global_stringset(unsigned int type,
+ struct nl_socket *nlsk);
+const struct stringset *perdev_stringset(const char *dev, unsigned int type,
+ struct nl_socket *nlsk);
+
+unsigned int get_count(const struct stringset *set);
+const char *get_string(const struct stringset *set, unsigned int idx);
+
+int preload_global_strings(struct nl_socket *nlsk);
+int preload_perdev_strings(struct nl_socket *nlsk, const char *dev);
+void cleanup_all_strings(void);
+
+#endif /* ETHTOOL_NETLINK_STRSET_H__ */
diff --git a/netlink/tsinfo.c b/netlink/tsinfo.c
new file mode 100644
index 0000000..c6571ff
--- /dev/null
+++ b/netlink/tsinfo.c
@@ -0,0 +1,124 @@
+/*
+ * tsinfo.c - netlink implementation of timestamping commands
+ *
+ * Implementation of "ethtool -T <dev>"
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+
+/* TSINFO_GET */
+
+static void tsinfo_dump_cb(unsigned int idx, const char *name, bool val,
+ void *data __maybe_unused)
+{
+ if (!val)
+ return;
+
+ if (name)
+ printf("\t%s\n", name);
+ else
+ printf("\tbit%u\n", idx);
+}
+
+static int tsinfo_dump_list(struct nl_context *nlctx, const struct nlattr *attr,
+ const char *label, const char *if_empty,
+ unsigned int stringset_id)
+{
+ const struct stringset *strings = NULL;
+ int ret;
+
+ printf("%s:", label);
+ ret = 0;
+ if (!attr || bitset_is_empty(attr, false, &ret)) {
+ printf("%s\n", if_empty);
+ return ret;
+ }
+ putchar('\n');
+ if (ret < 0)
+ return ret;
+
+ if (bitset_is_compact(attr)) {
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return ret;
+ strings = global_stringset(stringset_id, nlctx->ethnl2_socket);
+ }
+ return walk_bitset(attr, strings, tsinfo_dump_cb, NULL);
+}
+
+int tsinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_TSINFO_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_TSINFO_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ putchar('\n');
+ printf("Time stamping parameters for %s:\n", nlctx->devname);
+
+ ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TIMESTAMPING],
+ "Capabilities", "", ETH_SS_SOF_TIMESTAMPING);
+ if (ret < 0)
+ return err_ret;
+
+ printf("PTP Hardware Clock: ");
+ if (tb[ETHTOOL_A_TSINFO_PHC_INDEX])
+ printf("%d\n",
+ mnl_attr_get_u32(tb[ETHTOOL_A_TSINFO_PHC_INDEX]));
+ else
+ printf("none\n");
+
+ ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TX_TYPES],
+ "Hardware Transmit Timestamp Modes", " none",
+ ETH_SS_TS_TX_TYPES);
+ if (ret < 0)
+ return err_ret;
+
+ ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_RX_FILTERS],
+ "Hardware Receive Filter Modes", " none",
+ ETH_SS_TS_RX_FILTERS);
+ if (ret < 0)
+ return err_ret;
+
+ return MNL_CB_OK;
+}
+
+int nl_tsinfo(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_TSINFO_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TSINFO_GET,
+ ETHTOOL_A_TSINFO_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, tsinfo_reply_cb);
+}
diff --git a/netlink/tunnels.c b/netlink/tunnels.c
new file mode 100644
index 0000000..b464046
--- /dev/null
+++ b/netlink/tunnels.c
@@ -0,0 +1,236 @@
+/*
+ * tunnel.c - device tunnel information
+ *
+ * Implementation of "ethtool --show-tunnels <dev>"
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#include "../common.h"
+#include "../internal.h"
+
+#include "bitset.h"
+#include "netlink.h"
+#include "strset.h"
+
+/* TUNNEL_INFO_GET */
+
+static int
+tunnel_info_strings_load(struct nl_context *nlctx,
+ const struct stringset **strings)
+{
+ int ret;
+
+ if (*strings)
+ return 0;
+ ret = netlink_init_ethnl2_socket(nlctx);
+ if (ret < 0)
+ return ret;
+ *strings = global_stringset(ETH_SS_UDP_TUNNEL_TYPES,
+ nlctx->ethnl2_socket);
+ return 0;
+}
+
+static int
+tunnel_info_dump_udp_entry(struct nl_context *nlctx, const struct nlattr *entry,
+ const struct stringset **strings)
+{
+ const struct nlattr *entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(entry_tb);
+ const struct nlattr *attr;
+ unsigned int port, type;
+ int ret;
+
+ if (tunnel_info_strings_load(nlctx, strings))
+ return 1;
+
+ ret = mnl_attr_parse_nested(entry, attr_cb, &entry_tb_info);
+ if (ret < 0) {
+ fprintf(stderr, "malformed netlink message (udp entry)\n");
+ return 1;
+ }
+
+ attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT];
+ if (!attr || mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+ fprintf(stderr, "malformed netlink message (port)\n");
+ return 1;
+ }
+ port = ntohs(mnl_attr_get_u16(attr));
+
+ attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE];
+ if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ fprintf(stderr, "malformed netlink message (tunnel type)\n");
+ return 1;
+ }
+ type = mnl_attr_get_u32(attr);
+
+ printf(" port %d, %s\n", port, get_string(*strings, type));
+
+ return 0;
+}
+
+static void tunnel_info_dump_cb(unsigned int idx, const char *name, bool val,
+ void *data)
+{
+ bool *first = data;
+
+ if (!val)
+ return;
+
+ if (!*first)
+ putchar(',');
+ *first = false;
+
+ if (name)
+ printf(" %s", name);
+ else
+ printf(" bit%u", idx);
+}
+
+static int
+tunnel_info_dump_list(struct nl_context *nlctx, const struct nlattr *attr,
+ const struct stringset **strings)
+{
+ bool first = true;
+ int ret;
+
+ if (bitset_is_empty(attr, false, &ret) || ret) {
+ if (ret)
+ return 1;
+
+ printf(" Types: none (static entries)\n");
+ return 0;
+ }
+
+ if (bitset_is_compact(attr) &&
+ tunnel_info_strings_load(nlctx, strings))
+ return 1;
+
+ printf(" Types:");
+ ret = walk_bitset(attr, *strings, tunnel_info_dump_cb, &first);
+ putchar('\n');
+ return ret;
+}
+
+static int
+tunnel_info_dump_udp_table(const struct nlattr *table, struct nl_context *nlctx,
+ const struct stringset **strings)
+{
+ const struct nlattr *table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(table_tb);
+ const struct nlattr *attr;
+ int i, ret;
+
+ ret = mnl_attr_parse_nested(table, attr_cb, &table_tb_info);
+ if (ret < 0) {
+ fprintf(stderr, "malformed netlink message (udp table)\n");
+ return 1;
+ }
+
+ attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE];
+ if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ fprintf(stderr, "malformed netlink message (table size)\n");
+ return 1;
+ }
+ printf(" Size: %d\n", mnl_attr_get_u32(attr));
+
+ attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES];
+ if (!attr || tunnel_info_dump_list(nlctx, attr, strings)) {
+ fprintf(stderr, "malformed netlink message (types)\n");
+ return 1;
+ }
+
+ if (!table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY]) {
+ printf(" No entries\n");
+ return 0;
+ }
+
+ i = 0;
+ mnl_attr_for_each_nested(attr, table) {
+ if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY)
+ i++;
+ }
+
+ printf(" Entries (%d):\n", i++);
+ mnl_attr_for_each_nested(attr, table) {
+ if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY)
+ if (tunnel_info_dump_udp_entry(nlctx, attr, strings))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+tunnel_info_dump_udp(const struct nlattr *udp_ports, struct nl_context *nlctx)
+{
+ const struct stringset *strings = NULL;
+ const struct nlattr *table;
+ int i, err;
+
+ i = 0;
+ mnl_attr_for_each_nested(table, udp_ports) {
+ if (mnl_attr_get_type(table) != ETHTOOL_A_TUNNEL_UDP_TABLE)
+ continue;
+
+ printf(" UDP port table %d: \n", i++);
+ err = tunnel_info_dump_udp_table(table, nlctx, &strings);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int tunnel_info_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_TUNNEL_INFO_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const struct nlattr *udp_ports;
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_TUNNEL_INFO_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ printf("Tunnel information for %s:\n", nlctx->devname);
+
+ udp_ports = tb[ETHTOOL_A_TUNNEL_INFO_UDP_PORTS];
+ if (udp_ports)
+ if (tunnel_info_dump_udp(udp_ports, nlctx))
+ return err_ret;
+
+ return MNL_CB_OK;
+}
+
+int nl_gtunnels(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_TUNNEL_INFO_GET, true))
+ return -EOPNOTSUPP;
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TUNNEL_INFO_GET,
+ ETHTOOL_A_TUNNEL_INFO_HEADER, 0);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlsk, tunnel_info_reply_cb);
+}
diff --git a/pcnet32.c b/pcnet32.c
new file mode 100644
index 0000000..b87cee7
--- /dev/null
+++ b/pcnet32.c
@@ -0,0 +1,249 @@
+/* Copyright 2004 IBM Corporation (jklewis@us.ibm.com) */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "internal.h"
+
+#define BIT0 0x0001
+#define BIT1 0x0002
+#define BIT2 0x0004
+#define BIT3 0x0008
+#define BIT4 0x0010
+#define BIT5 0x0020
+#define BIT6 0x0040
+#define BIT7 0x0080
+#define BIT8 0x0100
+#define BIT9 0x0200
+#define BIT10 0x0400
+#define BIT11 0x0800
+#define BIT12 0x1000
+#define BIT13 0x2000
+#define BIT14 0x4000
+#define BIT15 0x8000
+
+int pcnet32_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs)
+{
+ int i, csr;
+ u16 *data = (u16 *) regs->data;
+ int len = regs->len / 2;
+ u16 temp,*ptr;
+
+ printf("Driver: %s\n",info->driver);
+ printf("Version: %s\n",info->version);
+
+ printf("APROM: ");
+ for (i=0; i<8; i++)
+ printf(" %04x ", data[i]);
+ printf("\n");
+
+ csr = i;
+ for (; i<100; i++)
+ {
+ if (((i-csr) & 7) == 0) printf("CSR%02d: ", i-csr);
+ printf(" %04x ", data[i]);
+ if (((i-csr) & 7) == 7) printf("\n");
+ }
+ if (((i-csr) & 7) != 7) printf("\n");
+
+ csr = i;
+ for (; i<136; i++)
+ {
+ if (((i-csr) & 7) == 0) printf("BCR%02d: ", i-csr);
+ printf(" %04x ", data[i]);
+ if (((i-csr) & 7) == 7) printf("\n");
+ }
+ if (((i-csr) & 7) != 7) printf("\n");
+
+ csr = i;
+ for (; i<len; i++)
+ {
+ if (((i-csr) & 7) == 0) printf("MII%02d: ", (i-csr) & 0x1f);
+ printf(" %04x ", data[i]);
+ if (((i-csr) & 7) == 7) printf("\n");
+ }
+ if (((i-csr) & 7) != 7) printf("\n");
+ printf("\n");
+
+ ptr=&data[8]; /* start of the CSRs */
+
+ printf("CSR0: Status and Control 0x%04x\n ",ptr[0]);
+ temp=ptr[0];
+
+ if(temp & BIT15) printf("ERR ");
+ if(temp & BIT14) printf("BABL ");
+ if(temp & BIT13) printf("CERR ");
+ if(temp & BIT12) printf("MISS ");
+ if(temp & BIT11) printf("MERR ");
+ if(temp & BIT10) printf("RINT ");
+ if(temp & BIT9) printf("TINT ");
+ if(temp & BIT8) printf("IDON ");
+ if(temp & BIT7) printf("INTR ");
+ if(temp & BIT6) printf("INT ");
+ if(temp & BIT5) printf("RXON ");
+ if(temp & BIT4) printf("TXON ");
+ if(temp & BIT3) printf("TDMD ");
+ if(temp & BIT2) printf("STOP ");
+ if(temp & BIT1) printf("STRT ");
+ if(temp & BIT0) printf("INIT ");
+
+ printf("\n");
+
+ printf("CSR3: Interrupt Mask 0x%04x\n ",ptr[3]);
+ temp=ptr[3];
+
+ if(temp & BIT14) printf("BABLM ");
+ if(temp & BIT12) printf("MISSM ");
+ if(temp & BIT11) printf("MERRM ");
+ if(temp & BIT10) printf("RINTM ");
+ if(temp & BIT9) printf("TINTM ");
+ if(temp & BIT8) printf("IDONM ");
+ if(temp & BIT6) printf("DXSUFLO ");
+ if(temp & BIT5) printf("LAPPEN ");
+ if(temp & BIT4) printf("DXMT2PD ");
+ if(temp & BIT3) printf("EMBA ");
+ if(temp & BIT2) printf("BSWP ");
+
+ printf("\n");
+
+ printf("CSR4: Test and Features 0x%04x\n ",ptr[4]);
+ temp=ptr[4];
+
+ if(temp & BIT15) printf("EN124 ");
+ if(temp & BIT14) printf("DMAPLUS ");
+ if(temp & BIT12) printf("TXDPOLL ");
+ if(temp & BIT11) printf("APAD_XMT ");
+ if(temp & BIT10) printf("ASTRP_RCV ");
+ if(temp & BIT9) printf("MFCO ");
+ if(temp & BIT8) printf("MFCON ");
+ if(temp & BIT7) printf("UINTCMD ");
+ if(temp & BIT6) printf("UINT ");
+ if(temp & BIT5) printf("RCVCCO ");
+ if(temp & BIT4) printf("RCVCCOM ");
+ if(temp & BIT3) printf("TXSTRT ");
+ if(temp & BIT2) printf("TXSTRTM ");
+ if(temp & BIT1) printf("JAB ");
+ if(temp & BIT0) printf("JABM ");
+
+ printf("\n");
+
+ printf("CSR5: Ext Control and Int 1 0x%04x\n ",ptr[5]);
+ temp=ptr[5];
+
+ if(temp & BIT15) printf("TOKINTD ");
+ if(temp & BIT14) printf("LTINTEN ");
+ if(temp & BIT11) printf("SINT ");
+ if(temp & BIT10) printf("SINTE ");
+ if(temp & BIT9) printf("SLPINT ");
+ if(temp & BIT8) printf("SLPINTE ");
+ if(temp & BIT7) printf("EXDINT ");
+ if(temp & BIT6) printf("EXDINTE ");
+ if(temp & BIT5) printf("MPPLBA ");
+ if(temp & BIT4) printf("MPINT ");
+ if(temp & BIT3) printf("MPINTE ");
+ if(temp & BIT2) printf("MPEN ");
+ if(temp & BIT1) printf("MPMODE ");
+ if(temp & BIT0) printf("SPND ");
+
+ printf("\n");
+
+ printf("CSR7: Ext Control and Int 2 0x%04x\n ",ptr[7]);
+ temp=ptr[7];
+
+ if(temp & BIT15) printf("FASTSPNDE ");
+ if(temp & BIT14) printf("RXFRTG ");
+ if(temp & BIT13) printf("RDMD ");
+ if(temp & BIT12) printf("RXDPOLL ");
+ if(temp & BIT11) printf("STINT ");
+ if(temp & BIT10) printf("STINTE ");
+ if(temp & BIT9) printf("MREINT ");
+ if(temp & BIT8) printf("MREINTE ");
+ if(temp & BIT7) printf("MAPINT ");
+ if(temp & BIT6) printf("MAPINTE ");
+ if(temp & BIT5) printf("MCCINT ");
+ if(temp & BIT4) printf("MCCINTE ");
+ if(temp & BIT3) printf("MCCIINT ");
+ if(temp & BIT2) printf("MCCIINTE ");
+ if(temp & BIT1) printf("MIIPDTINT ");
+ if(temp & BIT0) printf("MIIPDTINTE ");
+
+ printf("\n");
+
+ printf("CSR15: Mode 0x%04x\n",ptr[15]);
+ printf("CSR40: Current RX Byte Count 0x%04x\n",ptr[40]);
+ printf("CSR41: Current RX Status 0x%04x\n",ptr[41]);
+ printf("CSR42: Current TX Byte Count 0x%04x\n",ptr[42]);
+ printf("CSR43: Current TX Status 0x%04x\n",ptr[43]);
+ printf("CSR88: Chip ID Lower 0x%04x\n",ptr[88]);
+ temp = (((ptr[89] << 16) | ptr[88]) >> 12) & 0xffff;
+ switch (temp) {
+ case 0x2420:
+ printf(" PCnet/PCI 79C970\n");
+ break;
+ case 0x2621:
+ printf(" PCnet/PCI II 79C970A\n");
+ break;
+ case 0x2623:
+ printf(" PCnet/FAST 79C971\n");
+ break;
+ case 0x2624:
+ printf(" PCnet/FAST+ 79C972\n");
+ break;
+ case 0x2625:
+ printf(" PCnet/FAST III 79C973\n");
+ break;
+ case 0x2626:
+ printf(" PCnet/Home 79C978\n");
+ break;
+ case 0x2627:
+ printf(" PCnet/FAST III 79C975\n");
+ break;
+ case 0x2628:
+ printf(" PCnet/PRO 79C976\n");
+ break;
+ }
+
+ printf("CSR89: Chip ID Upper 0x%04x\n ",ptr[89]);
+ temp=ptr[89];
+ printf("VER: %04x PARTIDU: %04x\n",temp >> 12,temp & 0x00000fff);
+
+ printf("CSR112: Missed Frame Count 0x%04x\n",ptr[90]); /* 90 is 112 */
+ printf("CSR114: RX Collision Count 0x%04x\n",ptr[91]);
+
+ printf("\n");
+
+ ptr=&data[100]; /* point to BCR 0 */
+
+ printf("BCR2: Misc. Configuration 0x%04x\n ",ptr[2]);
+ temp=ptr[2];
+
+ if(temp & BIT14) printf("TMAULOOP ");
+ if(temp & BIT12) printf("LEDPE ");
+
+ if(temp & BIT8) printf("APROMWE ");
+ if(temp & BIT7) printf("INTLEVEL ");
+
+ if(temp & BIT3) printf("EADISEL ");
+ if(temp & BIT2) printf("AWAKE ");
+ if(temp & BIT1) printf("ASEL ");
+ if(temp & BIT0) printf("XMAUSEL ");
+
+ printf("\n");
+
+ printf("BCR9: Full-Duplex Control 0x%04x\n",ptr[9]);
+ printf("BCR18: Burst and Bus Control 0x%04x\n",ptr[18]);
+
+ printf("BCR19: EEPROM Control and Status 0x%04x\n ",ptr[19]);
+ temp=ptr[19];
+ if(temp & BIT15) printf("PVALID ");
+ if(temp & BIT13) printf("EEDET ");
+ printf("\n");
+
+ printf("BCR23: PCI Subsystem Vendor ID 0x%04x\n",ptr[23]);
+ printf("BCR24: PCI Subsystem ID 0x%04x\n",ptr[24]);
+ printf("BCR31: Software Timer 0x%04x\n",ptr[31]);
+ printf("BCR32: MII Control and Status 0x%04x\n",ptr[32]);
+ printf("BCR35: PCI Vendor ID 0x%04x\n",ptr[35]);
+
+ return(0);
+}
+
diff --git a/qsfp.c b/qsfp.c
new file mode 100644
index 0000000..a2921fb
--- /dev/null
+++ b/qsfp.c
@@ -0,0 +1,1059 @@
+/*
+ * qsfp.c: Implements SFF-8636 based QSFP+/QSFP28 Diagnostics Memory map.
+ *
+ * Copyright 2010 Solarflare Communications Inc.
+ * Aurelien Guillaume <aurelien@iwi.me> (C) 2012
+ * Copyright (C) 2014 Cumulus networks Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Freeoftware Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Vidya Ravipati <vidya@cumulusnetworks.com>
+ * This implementation is loosely based on current SFP parser
+ * and SFF-8636 spec Rev 2.7 (ftp://ftp.seagate.com/pub/sff/SFF-8636.PDF)
+ * by SFF Committee.
+ */
+
+/*
+ * Description:
+ * a) The register/memory layout is up to 5 128 byte pages defined by
+ * a "pages valid" register and switched via a "page select"
+ * register. Memory of 256 bytes can be memory mapped at a time
+ * according to SFF 8636.
+ * b) SFF 8636 based 640 bytes memory layout is presented for parser
+ *
+ * SFF 8636 based QSFP Memory Map
+ *
+ * 2-Wire Serial Address: 1010000x
+ *
+ * Lower Page 00h (128 bytes)
+ * ======================
+ * | |
+ * |Page Select Byte(127)|
+ * ======================
+ * |
+ * V
+ * ----------------------------------------
+ * | | | |
+ * V V V V
+ * ---------- ---------- --------- ------------
+ * | Upper | | Upper | | Upper | | Upper |
+ * | Page 00h | | Page 01h | | Page 02h | | Page 03h |
+ * | | |(Optional)| |(Optional)| | (Optional) |
+ * | | | | | | | |
+ * | | | | | | | |
+ * | ID | | AST | | User | | For |
+ * | Fields | | Table | | EEPROM | | Cable |
+ * | | | | | Data | | Assemblies |
+ * | | | | | | | |
+ * | | | | | | | |
+ * ----------- ----------- ---------- --------------
+ *
+ *
+ **/
+#include <stdio.h>
+#include <math.h>
+#include <errno.h>
+#include "internal.h"
+#include "sff-common.h"
+#include "qsfp.h"
+#include "cmis.h"
+#include "netlink/extapi.h"
+
+struct sff8636_memory_map {
+ const __u8 *lower_memory;
+ const __u8 *upper_memory[4];
+#define page_00h upper_memory[0x0]
+#define page_03h upper_memory[0x3]
+};
+
+#define SFF8636_PAGE_SIZE 0x80
+#define SFF8636_I2C_ADDRESS 0x50
+#define SFF8636_MAX_CHANNEL_NUM 4
+
+#define MAX_DESC_SIZE 42
+
+static struct sff8636_aw_flags {
+ const char *str; /* Human-readable string, null at the end */
+ int offset;
+ __u8 value; /* Alarm is on if (offset & value) != 0. */
+} sff8636_aw_flags[] = {
+ { "Laser bias current high alarm (Chan 1)",
+ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_HALARM) },
+ { "Laser bias current low alarm (Chan 1)",
+ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_LALARM) },
+ { "Laser bias current high warning (Chan 1)",
+ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_HWARN) },
+ { "Laser bias current low warning (Chan 1)",
+ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_1_LWARN) },
+
+ { "Laser bias current high alarm (Chan 2)",
+ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_HALARM) },
+ { "Laser bias current low alarm (Chan 2)",
+ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_LALARM) },
+ { "Laser bias current high warning (Chan 2)",
+ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_HWARN) },
+ { "Laser bias current low warning (Chan 2)",
+ SFF8636_TX_BIAS_12_AW_OFFSET, (SFF8636_TX_BIAS_2_LWARN) },
+
+ { "Laser bias current high alarm (Chan 3)",
+ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_HALARM) },
+ { "Laser bias current low alarm (Chan 3)",
+ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_LALARM) },
+ { "Laser bias current high warning (Chan 3)",
+ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_HWARN) },
+ { "Laser bias current low warning (Chan 3)",
+ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_3_LWARN) },
+
+ { "Laser bias current high alarm (Chan 4)",
+ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_HALARM) },
+ { "Laser bias current low alarm (Chan 4)",
+ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_LALARM) },
+ { "Laser bias current high warning (Chan 4)",
+ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_HWARN) },
+ { "Laser bias current low warning (Chan 4)",
+ SFF8636_TX_BIAS_34_AW_OFFSET, (SFF8636_TX_BIAS_4_LWARN) },
+
+ { "Module temperature high alarm",
+ SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_HALARM_STATUS) },
+ { "Module temperature low alarm",
+ SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_LALARM_STATUS) },
+ { "Module temperature high warning",
+ SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_HWARN_STATUS) },
+ { "Module temperature low warning",
+ SFF8636_TEMP_AW_OFFSET, (SFF8636_TEMP_LWARN_STATUS) },
+
+ { "Module voltage high alarm",
+ SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_HALARM_STATUS) },
+ { "Module voltage low alarm",
+ SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_LALARM_STATUS) },
+ { "Module voltage high warning",
+ SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_HWARN_STATUS) },
+ { "Module voltage low warning",
+ SFF8636_VCC_AW_OFFSET, (SFF8636_VCC_LWARN_STATUS) },
+
+ { "Laser tx power high alarm (Channel 1)",
+ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_HALARM) },
+ { "Laser tx power low alarm (Channel 1)",
+ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_LALARM) },
+ { "Laser tx power high warning (Channel 1)",
+ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_HWARN) },
+ { "Laser tx power low warning (Channel 1)",
+ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_1_LWARN) },
+
+ { "Laser tx power high alarm (Channel 2)",
+ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_HALARM) },
+ { "Laser tx power low alarm (Channel 2)",
+ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_LALARM) },
+ { "Laser tx power high warning (Channel 2)",
+ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_HWARN) },
+ { "Laser tx power low warning (Channel 2)",
+ SFF8636_TX_PWR_12_AW_OFFSET, (SFF8636_TX_PWR_2_LWARN) },
+
+ { "Laser tx power high alarm (Channel 3)",
+ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_HALARM) },
+ { "Laser tx power low alarm (Channel 3)",
+ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_LALARM) },
+ { "Laser tx power high warning (Channel 3)",
+ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_HWARN) },
+ { "Laser tx power low warning (Channel 3)",
+ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_3_LWARN) },
+
+ { "Laser tx power high alarm (Channel 4)",
+ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_HALARM) },
+ { "Laser tx power low alarm (Channel 4)",
+ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_LALARM) },
+ { "Laser tx power high warning (Channel 4)",
+ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_HWARN) },
+ { "Laser tx power low warning (Channel 4)",
+ SFF8636_TX_PWR_34_AW_OFFSET, (SFF8636_TX_PWR_4_LWARN) },
+
+ { "Laser rx power high alarm (Channel 1)",
+ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_HALARM) },
+ { "Laser rx power low alarm (Channel 1)",
+ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_LALARM) },
+ { "Laser rx power high warning (Channel 1)",
+ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_HWARN) },
+ { "Laser rx power low warning (Channel 1)",
+ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_1_LWARN) },
+
+ { "Laser rx power high alarm (Channel 2)",
+ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_HALARM) },
+ { "Laser rx power low alarm (Channel 2)",
+ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_LALARM) },
+ { "Laser rx power high warning (Channel 2)",
+ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_HWARN) },
+ { "Laser rx power low warning (Channel 2)",
+ SFF8636_RX_PWR_12_AW_OFFSET, (SFF8636_RX_PWR_2_LWARN) },
+
+ { "Laser rx power high alarm (Channel 3)",
+ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_HALARM) },
+ { "Laser rx power low alarm (Channel 3)",
+ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_LALARM) },
+ { "Laser rx power high warning (Channel 3)",
+ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_HWARN) },
+ { "Laser rx power low warning (Channel 3)",
+ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_3_LWARN) },
+
+ { "Laser rx power high alarm (Channel 4)",
+ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_HALARM) },
+ { "Laser rx power low alarm (Channel 4)",
+ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_LALARM) },
+ { "Laser rx power high warning (Channel 4)",
+ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_HWARN) },
+ { "Laser rx power low warning (Channel 4)",
+ SFF8636_RX_PWR_34_AW_OFFSET, (SFF8636_RX_PWR_4_LWARN) },
+
+ { NULL, 0, 0 },
+};
+
+static void sff8636_show_identifier(const struct sff8636_memory_map *map)
+{
+ sff8024_show_identifier(map->lower_memory, SFF8636_ID_OFFSET);
+}
+
+static void sff8636_show_ext_identifier(const struct sff8636_memory_map *map)
+{
+ printf("\t%-41s : 0x%02x\n", "Extended identifier",
+ map->page_00h[SFF8636_EXT_ID_OFFSET]);
+
+ static const char *pfx =
+ "\tExtended identifier description :";
+
+ switch (map->page_00h[SFF8636_EXT_ID_OFFSET] &
+ SFF8636_EXT_ID_PWR_CLASS_MASK) {
+ case SFF8636_EXT_ID_PWR_CLASS_1:
+ printf("%s 1.5W max. Power consumption\n", pfx);
+ break;
+ case SFF8636_EXT_ID_PWR_CLASS_2:
+ printf("%s 2.0W max. Power consumption\n", pfx);
+ break;
+ case SFF8636_EXT_ID_PWR_CLASS_3:
+ printf("%s 2.5W max. Power consumption\n", pfx);
+ break;
+ case SFF8636_EXT_ID_PWR_CLASS_4:
+ printf("%s 3.5W max. Power consumption\n", pfx);
+ break;
+ }
+
+ if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK)
+ printf("%s CDR present in TX,", pfx);
+ else
+ printf("%s No CDR in TX,", pfx);
+
+ if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK)
+ printf(" CDR present in RX\n");
+ else
+ printf(" No CDR in RX\n");
+
+ switch (map->page_00h[SFF8636_EXT_ID_OFFSET] &
+ SFF8636_EXT_ID_EPWR_CLASS_MASK) {
+ case SFF8636_EXT_ID_PWR_CLASS_LEGACY:
+ printf("%s", pfx);
+ break;
+ case SFF8636_EXT_ID_PWR_CLASS_5:
+ printf("%s 4.0W max. Power consumption,", pfx);
+ break;
+ case SFF8636_EXT_ID_PWR_CLASS_6:
+ printf("%s 4.5W max. Power consumption, ", pfx);
+ break;
+ case SFF8636_EXT_ID_PWR_CLASS_7:
+ printf("%s 5.0W max. Power consumption, ", pfx);
+ break;
+ }
+ if (map->lower_memory[SFF8636_PWR_MODE_OFFSET] &
+ SFF8636_HIGH_PWR_ENABLE)
+ printf(" High Power Class (> 3.5 W) enabled\n");
+ else
+ printf(" High Power Class (> 3.5 W) not enabled\n");
+ printf("\t%-41s : ", "Power set");
+ printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] &
+ SFF8636_LOW_PWR_SET));
+ printf("\t%-41s : ", "Power override");
+ printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] &
+ SFF8636_PWR_OVERRIDE));
+}
+
+static void sff8636_show_connector(const struct sff8636_memory_map *map)
+{
+ sff8024_show_connector(map->page_00h, SFF8636_CTOR_OFFSET);
+}
+
+static void sff8636_show_transceiver(const struct sff8636_memory_map *map)
+{
+ static const char *pfx =
+ "\tTransceiver type :";
+
+ printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ "Transceiver codes",
+ map->page_00h[SFF8636_ETHERNET_COMP_OFFSET],
+ map->page_00h[SFF8636_SONET_COMP_OFFSET],
+ map->page_00h[SFF8636_SAS_COMP_OFFSET],
+ map->page_00h[SFF8636_GIGE_COMP_OFFSET],
+ map->page_00h[SFF8636_FC_LEN_OFFSET],
+ map->page_00h[SFF8636_FC_TECH_OFFSET],
+ map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET],
+ map->page_00h[SFF8636_FC_SPEED_OFFSET]);
+
+ /* 10G/40G Ethernet Compliance Codes */
+ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
+ SFF8636_ETHERNET_10G_LRM)
+ printf("%s 10G Ethernet: 10G Base-LRM\n", pfx);
+ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
+ SFF8636_ETHERNET_10G_LR)
+ printf("%s 10G Ethernet: 10G Base-LR\n", pfx);
+ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
+ SFF8636_ETHERNET_10G_SR)
+ printf("%s 10G Ethernet: 10G Base-SR\n", pfx);
+ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
+ SFF8636_ETHERNET_40G_CR4)
+ printf("%s 40G Ethernet: 40G Base-CR4\n", pfx);
+ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
+ SFF8636_ETHERNET_40G_SR4)
+ printf("%s 40G Ethernet: 40G Base-SR4\n", pfx);
+ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
+ SFF8636_ETHERNET_40G_LR4)
+ printf("%s 40G Ethernet: 40G Base-LR4\n", pfx);
+ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
+ SFF8636_ETHERNET_40G_ACTIVE)
+ printf("%s 40G Ethernet: 40G Active Cable (XLPPI)\n", pfx);
+ /* Extended Specification Compliance Codes from SFF-8024 */
+ if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
+ SFF8636_ETHERNET_RSRVD) {
+ switch (map->page_00h[SFF8636_OPTION_1_OFFSET]) {
+ case SFF8636_ETHERNET_UNSPECIFIED:
+ printf("%s (reserved or unknown)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_AOC:
+ printf("%s 100G Ethernet: 100G AOC or 25GAUI C2M AOC with worst BER of 5x10^(-5)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100G_SR4:
+ printf("%s 100G Ethernet: 100G Base-SR4 or 25GBase-SR\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100G_LR4:
+ printf("%s 100G Ethernet: 100G Base-LR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_ER4:
+ printf("%s 100G Ethernet: 100G Base-ER4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_SR10:
+ printf("%s 100G Ethernet: 100G Base-SR10\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_CWDM4_FEC:
+ printf("%s 100G Ethernet: 100G CWDM4 MSA with FEC\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_PSM4:
+ printf("%s 100G Ethernet: 100G PSM4 Parallel SMF\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_ACC:
+ printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 5x10^(-5)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100G_CWDM4_NO_FEC:
+ printf("%s 100G Ethernet: 100G CWDM4 MSA without FEC\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_RSVD1:
+ printf("%s (reserved or unknown)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_CR4:
+ printf("%s 100G Ethernet: 100G Base-CR4 or 25G Base-CR CA-L\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_25G_CR_CA_S:
+ printf("%s 25G Ethernet: 25G Base-CR CA-S\n", pfx);
+ break;
+ case SFF8636_ETHERNET_25G_CR_CA_N:
+ printf("%s 25G Ethernet: 25G Base-CR CA-N\n", pfx);
+ break;
+ case SFF8636_ETHERNET_40G_ER4:
+ printf("%s 40G Ethernet: 40G Base-ER4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_4X10_SR:
+ printf("%s 4x10G Ethernet: 10G Base-SR\n", pfx);
+ break;
+ case SFF8636_ETHERNET_40G_PSM4:
+ printf("%s 40G Ethernet: 40G PSM4 Parallel SMF\n", pfx);
+ break;
+ case SFF8636_ETHERNET_G959_P1I1_2D1:
+ printf("%s Ethernet: G959.1 profile P1I1-2D1 (10709 MBd, 2km, 1310nm SM)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_G959_P1S1_2D2:
+ printf("%s Ethernet: G959.1 profile P1S1-2D2 (10709 MBd, 40km, 1550nm SM)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_G959_P1L1_2D2:
+ printf("%s Ethernet: G959.1 profile P1L1-2D2 (10709 MBd, 80km, 1550nm SM)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_10GT_SFI:
+ printf("%s 10G Ethernet: 10G Base-T with SFI electrical interface\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100G_CLR4:
+ printf("%s 100G Ethernet: 100G CLR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_AOC2:
+ printf("%s 100G Ethernet: 100G AOC or 25GAUI C2M AOC with worst BER of 10^(-12)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100G_ACC2:
+ printf("%s 100G Ethernet: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100GE_DWDM2:
+ printf("%s 100GE-DWDM2 (DWDM transceiver using 2 wavelengths on a 1550 nm DWDM grid with a reach up to 80 km)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100G_1550NM_WDM:
+ printf("%s 100G 1550nm WDM (4 wavelengths)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_10G_BASET_SR:
+ printf("%s 10GBASE-T Short Reach (30 meters)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_5G_BASET:
+ printf("%s 5GBASE-T\n", pfx);
+ break;
+ case SFF8636_ETHERNET_2HALFG_BASET:
+ printf("%s 2.5GBASE-T\n", pfx);
+ break;
+ case SFF8636_ETHERNET_40G_SWDM4:
+ printf("%s 40G SWDM4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_SWDM4:
+ printf("%s 100G SWDM4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_PAM4_BIDI:
+ printf("%s 100G PAM4 BiDi\n", pfx);
+ break;
+ case SFF8636_ETHERNET_4WDM10_MSA:
+ printf("%s 4WDM-10 MSA (10km version of 100G CWDM4 with same RS(528,514) FEC in host system)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_4WDM20_MSA:
+ printf("%s 4WDM-20 MSA (20km version of 100GBASE-LR4 with RS(528,514) FEC in host system)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_4WDM40_MSA:
+ printf("%s 4WDM-40 MSA (40km reach with APD receiver and RS(528,514) FEC in host system)\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_100G_DR:
+ printf("%s 100GBASE-DR (clause 140), CAUI-4 (no FEC)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_FR_NOFEC:
+ printf("%s 100G-FR or 100GBASE-FR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_100G_LR_NOFEC:
+ printf("%s 100G-LR or 100GBASE-LR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_ACC1:
+ printf("%s Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_200G_AOC1:
+ printf("%s Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_200G_ACC2:
+ printf("%s Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_200G_A0C2:
+ printf("%s Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
+ pfx);
+ break;
+ case SFF8636_ETHERNET_200G_CR4:
+ printf("%s 50GBASE-CR, 100GBASE-CR2, or 200GBASE-CR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_SR4:
+ printf("%s 50GBASE-SR, 100GBASE-SR2, or 200GBASE-SR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_DR4:
+ printf("%s 50GBASE-FR or 200GBASE-DR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_FR4:
+ printf("%s 200GBASE-FR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_PSM4:
+ printf("%s 200G 1550 nm PSM4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_50G_LR:
+ printf("%s 50GBASE-LR\n", pfx);
+ break;
+ case SFF8636_ETHERNET_200G_LR4:
+ printf("%s 200GBASE-LR4\n", pfx);
+ break;
+ case SFF8636_ETHERNET_64G_EA:
+ printf("%s 64GFC EA\n", pfx);
+ break;
+ case SFF8636_ETHERNET_64G_SW:
+ printf("%s 64GFC SW\n", pfx);
+ break;
+ case SFF8636_ETHERNET_64G_LW:
+ printf("%s 64GFC LW\n", pfx);
+ break;
+ case SFF8636_ETHERNET_128FC_EA:
+ printf("%s 128GFC EA\n", pfx);
+ break;
+ case SFF8636_ETHERNET_128FC_SW:
+ printf("%s 128GFC SW\n", pfx);
+ break;
+ case SFF8636_ETHERNET_128FC_LW:
+ printf("%s 128GFC LW\n", pfx);
+ break;
+ default:
+ printf("%s (reserved or unknown)\n", pfx);
+ break;
+ }
+ }
+
+ /* SONET Compliance Codes */
+ if (map->page_00h[SFF8636_SONET_COMP_OFFSET] &
+ (SFF8636_SONET_40G_OTN))
+ printf("%s 40G OTN (OTU3B/OTU3C)\n", pfx);
+ if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR))
+ printf("%s SONET: OC-48, long reach\n", pfx);
+ if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR))
+ printf("%s SONET: OC-48, intermediate reach\n", pfx);
+ if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR))
+ printf("%s SONET: OC-48, short reach\n", pfx);
+
+ /* SAS/SATA Compliance Codes */
+ if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G))
+ printf("%s SAS 6.0G\n", pfx);
+ if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G))
+ printf("%s SAS 3.0G\n", pfx);
+
+ /* Ethernet Compliance Codes */
+ if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T)
+ printf("%s Ethernet: 1000BASE-T\n", pfx);
+ if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX)
+ printf("%s Ethernet: 1000BASE-CX\n", pfx);
+ if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX)
+ printf("%s Ethernet: 1000BASE-LX\n", pfx);
+ if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX)
+ printf("%s Ethernet: 1000BASE-SX\n", pfx);
+
+ /* Fibre Channel link length */
+ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG)
+ printf("%s FC: very long distance (V)\n", pfx);
+ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT)
+ printf("%s FC: short distance (S)\n", pfx);
+ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT)
+ printf("%s FC: intermediate distance (I)\n", pfx);
+ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG)
+ printf("%s FC: long distance (L)\n", pfx);
+ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED)
+ printf("%s FC: medium distance (M)\n", pfx);
+
+ /* Fibre Channel transmitter technology */
+ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC)
+ printf("%s FC: Longwave laser (LC)\n", pfx);
+ if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER)
+ printf("%s FC: Electrical inter-enclosure (EL)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA)
+ printf("%s FC: Electrical intra-enclosure (EL)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TECH_OFFSET] &
+ SFF8636_FC_TECH_SHORT_WO_OFC)
+ printf("%s FC: Shortwave laser w/o OFC (SN)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC)
+ printf("%s FC: Shortwave laser with OFC (SL)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL)
+ printf("%s FC: Longwave laser (LL)\n", pfx);
+
+ /* Fibre Channel transmission media */
+ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
+ SFF8636_FC_TRANS_MEDIA_TW)
+ printf("%s FC: Twin Axial Pair (TW)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
+ SFF8636_FC_TRANS_MEDIA_TP)
+ printf("%s FC: Twisted Pair (TP)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
+ SFF8636_FC_TRANS_MEDIA_MI)
+ printf("%s FC: Miniature Coax (MI)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
+ SFF8636_FC_TRANS_MEDIA_TV)
+ printf("%s FC: Video Coax (TV)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
+ SFF8636_FC_TRANS_MEDIA_M6)
+ printf("%s FC: Multimode, 62.5m (M6)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
+ SFF8636_FC_TRANS_MEDIA_M5)
+ printf("%s FC: Multimode, 50m (M5)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
+ SFF8636_FC_TRANS_MEDIA_OM3)
+ printf("%s FC: Multimode, 50um (OM3)\n", pfx);
+ if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
+ SFF8636_FC_TRANS_MEDIA_SM)
+ printf("%s FC: Single Mode (SM)\n", pfx);
+
+ /* Fibre Channel speed */
+ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS)
+ printf("%s FC: 1200 MBytes/sec\n", pfx);
+ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS)
+ printf("%s FC: 800 MBytes/sec\n", pfx);
+ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS)
+ printf("%s FC: 1600 MBytes/sec\n", pfx);
+ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS)
+ printf("%s FC: 400 MBytes/sec\n", pfx);
+ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS)
+ printf("%s FC: 200 MBytes/sec\n", pfx);
+ if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS)
+ printf("%s FC: 100 MBytes/sec\n", pfx);
+}
+
+static void sff8636_show_encoding(const struct sff8636_memory_map *map)
+{
+ sff8024_show_encoding(map->page_00h, SFF8636_ENCODING_OFFSET,
+ ETH_MODULE_SFF_8636);
+}
+
+static void sff8636_show_rate_identifier(const struct sff8636_memory_map *map)
+{
+ /* TODO: Need to fix rate select logic */
+ printf("\t%-41s : 0x%02x\n", "Rate identifier",
+ map->page_00h[SFF8636_EXT_RS_OFFSET]);
+}
+
+static void
+sff8636_show_wavelength_or_copper_compliance(const struct sff8636_memory_map *map)
+{
+ printf("\t%-41s : 0x%02x", "Transmitter technology",
+ map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
+ SFF8636_TRANS_TECH_MASK);
+
+ switch (map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
+ SFF8636_TRANS_TECH_MASK) {
+ case SFF8636_TRANS_850_VCSEL:
+ printf(" (850 nm VCSEL)\n");
+ break;
+ case SFF8636_TRANS_1310_VCSEL:
+ printf(" (1310 nm VCSEL)\n");
+ break;
+ case SFF8636_TRANS_1550_VCSEL:
+ printf(" (1550 nm VCSEL)\n");
+ break;
+ case SFF8636_TRANS_1310_FP:
+ printf(" (1310 nm FP)\n");
+ break;
+ case SFF8636_TRANS_1310_DFB:
+ printf(" (1310 nm DFB)\n");
+ break;
+ case SFF8636_TRANS_1550_DFB:
+ printf(" (1550 nm DFB)\n");
+ break;
+ case SFF8636_TRANS_1310_EML:
+ printf(" (1310 nm EML)\n");
+ break;
+ case SFF8636_TRANS_1550_EML:
+ printf(" (1550 nm EML)\n");
+ break;
+ case SFF8636_TRANS_OTHERS:
+ printf(" (Others/Undefined)\n");
+ break;
+ case SFF8636_TRANS_1490_DFB:
+ printf(" (1490 nm DFB)\n");
+ break;
+ case SFF8636_TRANS_COPPER_PAS_UNEQUAL:
+ printf(" (Copper cable unequalized)\n");
+ break;
+ case SFF8636_TRANS_COPPER_PAS_EQUAL:
+ printf(" (Copper cable passive equalized)\n");
+ break;
+ case SFF8636_TRANS_COPPER_LNR_FAR_EQUAL:
+ printf(" (Copper cable, near and far end limiting active equalizers)\n");
+ break;
+ case SFF8636_TRANS_COPPER_FAR_EQUAL:
+ printf(" (Copper cable, far end limiting active equalizers)\n");
+ break;
+ case SFF8636_TRANS_COPPER_NEAR_EQUAL:
+ printf(" (Copper cable, near end limiting active equalizers)\n");
+ break;
+ case SFF8636_TRANS_COPPER_LNR_EQUAL:
+ printf(" (Copper cable, linear active equalizers)\n");
+ break;
+ }
+
+ if ((map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
+ SFF8636_TRANS_TECH_MASK) >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) {
+ printf("\t%-41s : %udb\n", "Attenuation at 2.5GHz",
+ map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]);
+ printf("\t%-41s : %udb\n", "Attenuation at 5.0GHz",
+ map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]);
+ printf("\t%-41s : %udb\n", "Attenuation at 7.0GHz",
+ map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]);
+ printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
+ map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]);
+ } else {
+ printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
+ (((map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) |
+ map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]) * 0.05));
+ printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
+ (((map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) |
+ map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]) * 0.005));
+ }
+}
+
+/*
+ * 2-byte internal temperature conversions:
+ * First byte is a signed 8-bit integer, which is the temp decimal part
+ * Second byte are 1/256th of degree, which are added to the dec part.
+ */
+#define SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
+
+static void sff8636_dom_parse(const struct sff8636_memory_map *map,
+ struct sff_diags *sd)
+{
+ const __u8 *id = map->lower_memory;
+ int i = 0;
+
+ /* Monitoring Thresholds for Alarms and Warnings */
+ sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(id, SFF8636_VCC_CURR);
+ sd->sfp_temp[MCURR] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_CURR);
+
+ if (!map->page_03h)
+ goto out;
+
+ sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_VCC_HALRM);
+ sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_VCC_LALRM);
+ sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_VCC_HWARN);
+ sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_VCC_LWARN);
+
+ sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TEMP_HALRM);
+ sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TEMP_LALRM);
+ sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TEMP_HWARN);
+ sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TEMP_LWARN);
+
+ sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TX_BIAS_HALRM);
+ sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TX_BIAS_LALRM);
+ sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TX_BIAS_HWARN);
+ sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TX_BIAS_LWARN);
+
+ sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TX_PWR_HALRM);
+ sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TX_PWR_LALRM);
+ sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TX_PWR_HWARN);
+ sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_TX_PWR_LWARN);
+
+ sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_RX_PWR_HALRM);
+ sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_RX_PWR_LALRM);
+ sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_RX_PWR_HWARN);
+ sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
+ SFF8636_RX_PWR_LWARN);
+
+out:
+ /* Channel Specific Data */
+ for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
+ u8 rx_power_offset, tx_bias_offset;
+ u8 tx_power_offset;
+
+ switch (i) {
+ case 0:
+ rx_power_offset = SFF8636_RX_PWR_1_OFFSET;
+ tx_power_offset = SFF8636_TX_PWR_1_OFFSET;
+ tx_bias_offset = SFF8636_TX_BIAS_1_OFFSET;
+ break;
+ case 1:
+ rx_power_offset = SFF8636_RX_PWR_2_OFFSET;
+ tx_power_offset = SFF8636_TX_PWR_2_OFFSET;
+ tx_bias_offset = SFF8636_TX_BIAS_2_OFFSET;
+ break;
+ case 2:
+ rx_power_offset = SFF8636_RX_PWR_3_OFFSET;
+ tx_power_offset = SFF8636_TX_PWR_3_OFFSET;
+ tx_bias_offset = SFF8636_TX_BIAS_3_OFFSET;
+ break;
+ case 3:
+ rx_power_offset = SFF8636_RX_PWR_4_OFFSET;
+ tx_power_offset = SFF8636_TX_PWR_4_OFFSET;
+ tx_bias_offset = SFF8636_TX_BIAS_4_OFFSET;
+ break;
+ }
+ sd->scd[i].bias_cur = OFFSET_TO_U16(tx_bias_offset);
+ sd->scd[i].rx_power = OFFSET_TO_U16(rx_power_offset);
+ sd->scd[i].tx_power = OFFSET_TO_U16(tx_power_offset);
+ }
+}
+
+static void sff8636_show_dom(const struct sff8636_memory_map *map)
+{
+ struct sff_diags sd = {0};
+ char *rx_power_string = NULL;
+ char power_string[MAX_DESC_SIZE];
+ int i;
+
+ /*
+ * There is no clear identifier to signify the existence of
+ * optical diagnostics similar to SFF-8472. So checking existence
+ * of page 3, will provide the gurantee for existence of alarms
+ * and thresholds
+ * If pagging support exists, then supports_alarms is marked as 1
+ */
+ if (map->page_03h)
+ sd.supports_alarms = 1;
+
+ sd.rx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] &
+ SFF8636_RX_PWR_TYPE_MASK;
+ sd.tx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] &
+ SFF8636_RX_PWR_TYPE_MASK;
+
+ sff8636_dom_parse(map, &sd);
+
+ PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]);
+ PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]);
+
+ /*
+ * SFF-8636/8436 spec is not clear whether RX power/ TX bias
+ * current fields are supported or not. A valid temperature
+ * reading is used as existence for TX/RX power.
+ */
+ if ((sd.sfp_temp[MCURR] == 0x0) ||
+ (sd.sfp_temp[MCURR] == (__s16)0xFFFF))
+ return;
+
+ printf("\t%-41s : %s\n", "Alarm/warning flags implemented",
+ (sd.supports_alarms ? "Yes" : "No"));
+
+ for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
+ snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)",
+ "Laser tx bias current", i+1);
+ PRINT_BIAS(power_string, sd.scd[i].bias_cur);
+ }
+
+ for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
+ snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)",
+ "Transmit avg optical power", i+1);
+ PRINT_xX_PWR(power_string, sd.scd[i].tx_power);
+ }
+
+ if (!sd.rx_power_type)
+ rx_power_string = "Receiver signal OMA";
+ else
+ rx_power_string = "Rcvr signal avg optical power";
+
+ for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
+ snprintf(power_string, MAX_DESC_SIZE, "%s(Channel %d)",
+ rx_power_string, i+1);
+ PRINT_xX_PWR(power_string, sd.scd[i].rx_power);
+ }
+
+ if (sd.supports_alarms) {
+ for (i = 0; sff8636_aw_flags[i].str; ++i) {
+ printf("\t%-41s : %s\n", sff8636_aw_flags[i].str,
+ map->lower_memory[sff8636_aw_flags[i].offset]
+ & sff8636_aw_flags[i].value ? "On" : "Off");
+ }
+
+ sff_show_thresholds(sd);
+ }
+}
+
+static void sff8636_show_signals(const struct sff8636_memory_map *map)
+{
+ unsigned int v;
+
+ /* There appears to be no Rx LOS support bit, use Tx for both */
+ if (map->page_00h[SFF8636_OPTION_4_OFFSET] & SFF8636_O4_TX_LOS) {
+ v = map->lower_memory[SFF8636_LOS_AW_OFFSET] & 0xf;
+ sff_show_lane_status("Rx loss of signal", 4, "Yes", "No", v);
+ v = map->lower_memory[SFF8636_LOS_AW_OFFSET] >> 4;
+ sff_show_lane_status("Tx loss of signal", 4, "Yes", "No", v);
+ }
+
+ v = map->lower_memory[SFF8636_LOL_AW_OFFSET] & 0xf;
+ if (map->page_00h[SFF8636_OPTION_3_OFFSET] & SFF8636_O3_RX_LOL)
+ sff_show_lane_status("Rx loss of lock", 4, "Yes", "No", v);
+
+ v = map->lower_memory[SFF8636_LOL_AW_OFFSET] >> 4;
+ if (map->page_00h[SFF8636_OPTION_3_OFFSET] & SFF8636_O3_TX_LOL)
+ sff_show_lane_status("Tx loss of lock", 4, "Yes", "No", v);
+
+ v = map->lower_memory[SFF8636_FAULT_AW_OFFSET] & 0xf;
+ if (map->page_00h[SFF8636_OPTION_4_OFFSET] & SFF8636_O4_TX_FAULT)
+ sff_show_lane_status("Tx fault", 4, "Yes", "No", v);
+
+ v = map->lower_memory[SFF8636_FAULT_AW_OFFSET] >> 4;
+ if (map->page_00h[SFF8636_OPTION_2_OFFSET] & SFF8636_O2_TX_EQ_AUTO)
+ sff_show_lane_status("Tx adaptive eq fault", 4, "Yes", "No", v);
+}
+
+static void sff8636_show_page_zero(const struct sff8636_memory_map *map)
+{
+ sff8636_show_ext_identifier(map);
+ sff8636_show_connector(map);
+ sff8636_show_transceiver(map);
+ sff8636_show_encoding(map);
+ sff_show_value_with_unit(map->page_00h, SFF8636_BR_NOMINAL_OFFSET,
+ "BR, Nominal", 100, "Mbps");
+ sff8636_show_rate_identifier(map);
+ sff_show_value_with_unit(map->page_00h, SFF8636_SM_LEN_OFFSET,
+ "Length (SMF,km)", 1, "km");
+ sff_show_value_with_unit(map->page_00h, SFF8636_OM3_LEN_OFFSET,
+ "Length (OM3 50um)", 2, "m");
+ sff_show_value_with_unit(map->page_00h, SFF8636_OM2_LEN_OFFSET,
+ "Length (OM2 50um)", 1, "m");
+ sff_show_value_with_unit(map->page_00h, SFF8636_OM1_LEN_OFFSET,
+ "Length (OM1 62.5um)", 1, "m");
+ sff_show_value_with_unit(map->page_00h, SFF8636_CBL_LEN_OFFSET,
+ "Length (Copper or Active cable)", 1, "m");
+ sff8636_show_wavelength_or_copper_compliance(map);
+ sff_show_ascii(map->page_00h, SFF8636_VENDOR_NAME_START_OFFSET,
+ SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name");
+ sff8024_show_oui(map->page_00h, SFF8636_VENDOR_OUI_OFFSET);
+ sff_show_ascii(map->page_00h, SFF8636_VENDOR_PN_START_OFFSET,
+ SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN");
+ sff_show_ascii(map->page_00h, SFF8636_VENDOR_REV_START_OFFSET,
+ SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
+ sff_show_ascii(map->page_00h, SFF8636_VENDOR_SN_START_OFFSET,
+ SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
+ sff_show_ascii(map->page_00h, SFF8636_DATE_YEAR_OFFSET,
+ SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+ sff_show_revision_compliance(map->lower_memory,
+ SFF8636_REV_COMPLIANCE_OFFSET);
+ sff8636_show_signals(map);
+}
+
+static void sff8636_show_all_common(const struct sff8636_memory_map *map)
+{
+ sff8636_show_identifier(map);
+ switch (map->lower_memory[SFF8636_ID_OFFSET]) {
+ case SFF8024_ID_QSFP:
+ case SFF8024_ID_QSFP_PLUS:
+ case SFF8024_ID_QSFP28:
+ sff8636_show_page_zero(map);
+ sff8636_show_dom(map);
+ break;
+ }
+}
+
+static void sff8636_memory_map_init_buf(struct sff8636_memory_map *map,
+ const __u8 *id, __u32 eeprom_len)
+{
+ /* Lower Memory and Page 00h are always present.
+ *
+ * Offset into Upper Memory is between page size and twice the page
+ * size. Therefore, set the base address of each page to base address
+ * plus page size multiplied by the page number.
+ */
+ map->lower_memory = id;
+ map->page_00h = id;
+
+ /* Page 03h is only present when the module memory model is paged and
+ * not flat and when we got a big enough buffer from the kernel.
+ */
+ if (map->lower_memory[SFF8636_STATUS_2_OFFSET] &
+ SFF8636_STATUS_PAGE_3_PRESENT ||
+ eeprom_len != ETH_MODULE_SFF_8636_MAX_LEN)
+ return;
+
+ map->page_03h = id + 3 * SFF8636_PAGE_SIZE;
+}
+
+void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len)
+{
+ struct sff8636_memory_map map = {};
+
+ switch (id[SFF8636_ID_OFFSET]) {
+ case SFF8024_ID_QSFP_DD:
+ case SFF8024_ID_OSFP:
+ case SFF8024_ID_DSFP:
+ case SFF8024_ID_QSFP_PLUS_CMIS:
+ case SFF8024_ID_SFP_DD_CMIS:
+ case SFF8024_ID_SFP_PLUS_CMIS:
+ cmis_show_all_ioctl(id);
+ break;
+ default:
+ sff8636_memory_map_init_buf(&map, id, eeprom_len);
+ sff8636_show_all_common(&map);
+ break;
+ }
+}
+
+static void sff8636_request_init(struct ethtool_module_eeprom *request, u8 page,
+ u32 offset)
+{
+ request->offset = offset;
+ request->length = SFF8636_PAGE_SIZE;
+ request->page = page;
+ request->bank = 0;
+ request->i2c_address = SFF8636_I2C_ADDRESS;
+ request->data = NULL;
+}
+
+static int
+sff8636_memory_map_init_pages(struct cmd_context *ctx,
+ struct sff8636_memory_map *map)
+{
+ struct ethtool_module_eeprom request;
+ int ret;
+
+ /* Lower Memory and Page 00h are always present.
+ *
+ * Offset into Upper Memory is between page size and twice the page
+ * size. Therefore, set the base address of each page to its base
+ * address minus page size.
+ */
+ sff8636_request_init(&request, 0x0, 0);
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+ map->lower_memory = request.data;
+
+ sff8636_request_init(&request, 0x0, SFF8636_PAGE_SIZE);
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+ map->page_00h = request.data - SFF8636_PAGE_SIZE;
+
+ /* Page 03h is only present when the module memory model is paged and
+ * not flat.
+ */
+ if (map->lower_memory[SFF8636_STATUS_2_OFFSET] &
+ SFF8636_STATUS_PAGE_3_PRESENT)
+ return 0;
+
+ sff8636_request_init(&request, 0x3, SFF8636_PAGE_SIZE);
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (ret < 0)
+ return ret;
+ map->page_03h = request.data - SFF8636_PAGE_SIZE;
+
+ return 0;
+}
+
+int sff8636_show_all_nl(struct cmd_context *ctx)
+{
+ struct sff8636_memory_map map = {};
+ int ret;
+
+ ret = sff8636_memory_map_init_pages(ctx, &map);
+ if (ret < 0)
+ return ret;
+ sff8636_show_all_common(&map);
+
+ return 0;
+}
diff --git a/qsfp.h b/qsfp.h
new file mode 100644
index 0000000..9f0cb0f
--- /dev/null
+++ b/qsfp.h
@@ -0,0 +1,636 @@
+/*
+ * SFF 8636 standards based QSFP EEPROM Field Definitions
+ *
+ * Vidya Ravipati <vidya@cumulusnetworks.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef QSFP_H__
+#define QSFP_H__
+
+/*------------------------------------------------------------------------------
+ *
+ * QSFP EEPROM data structures
+ *
+ * register info from SFF-8636 Rev 2.7
+ */
+
+/*------------------------------------------------------------------------------
+ *
+ * Lower Memory Page 00h
+ * Measurement, Diagnostic and Control Functions
+ *
+ */
+/* Identifier - 0 */
+/* Values are defined under SFF8024_ID_OFFSET */
+#define SFF8636_ID_OFFSET 0x00
+
+#define SFF8636_REV_COMPLIANCE_OFFSET 0x01
+
+#define SFF8636_STATUS_2_OFFSET 0x02
+/* Flat Memory:0- Paging, 1- Page 0 only */
+#define SFF8636_STATUS_PAGE_3_PRESENT (1 << 2)
+#define SFF8636_STATUS_INTL_OUTPUT (1 << 1)
+#define SFF8636_STATUS_DATA_NOT_READY (1 << 0)
+
+/* Channel Status Interrupt Flags - 3-5 */
+#define SFF8636_LOS_AW_OFFSET 0x03
+#define SFF8636_TX4_LOS_AW (1 << 7)
+#define SFF8636_TX3_LOS_AW (1 << 6)
+#define SFF8636_TX2_LOS_AW (1 << 5)
+#define SFF8636_TX1_LOS_AW (1 << 4)
+#define SFF8636_RX4_LOS_AW (1 << 3)
+#define SFF8636_RX3_LOS_AW (1 << 2)
+#define SFF8636_RX2_LOS_AW (1 << 1)
+#define SFF8636_RX1_LOS_AW (1 << 0)
+
+#define SFF8636_FAULT_AW_OFFSET 0x04
+#define SFF8636_TX4_FAULT_AW (1 << 3)
+#define SFF8636_TX3_FAULT_AW (1 << 2)
+#define SFF8636_TX2_FAULT_AW (1 << 1)
+#define SFF8636_TX1_FAULT_AW (1 << 0)
+
+#define SFF8636_LOL_AW_OFFSET 0x05
+
+/* Module Monitor Interrupt Flags - 6-8 */
+#define SFF8636_TEMP_AW_OFFSET 0x06
+#define SFF8636_TEMP_HALARM_STATUS (1 << 7)
+#define SFF8636_TEMP_LALARM_STATUS (1 << 6)
+#define SFF8636_TEMP_HWARN_STATUS (1 << 5)
+#define SFF8636_TEMP_LWARN_STATUS (1 << 4)
+
+#define SFF8636_VCC_AW_OFFSET 0x07
+#define SFF8636_VCC_HALARM_STATUS (1 << 7)
+#define SFF8636_VCC_LALARM_STATUS (1 << 6)
+#define SFF8636_VCC_HWARN_STATUS (1 << 5)
+#define SFF8636_VCC_LWARN_STATUS (1 << 4)
+
+/* Channel Monitor Interrupt Flags - 9-21 */
+#define SFF8636_RX_PWR_12_AW_OFFSET 0x09
+#define SFF8636_RX_PWR_1_HALARM (1 << 7)
+#define SFF8636_RX_PWR_1_LALARM (1 << 6)
+#define SFF8636_RX_PWR_1_HWARN (1 << 5)
+#define SFF8636_RX_PWR_1_LWARN (1 << 4)
+#define SFF8636_RX_PWR_2_HALARM (1 << 3)
+#define SFF8636_RX_PWR_2_LALARM (1 << 2)
+#define SFF8636_RX_PWR_2_HWARN (1 << 1)
+#define SFF8636_RX_PWR_2_LWARN (1 << 0)
+
+#define SFF8636_RX_PWR_34_AW_OFFSET 0x0A
+#define SFF8636_RX_PWR_3_HALARM (1 << 7)
+#define SFF8636_RX_PWR_3_LALARM (1 << 6)
+#define SFF8636_RX_PWR_3_HWARN (1 << 5)
+#define SFF8636_RX_PWR_3_LWARN (1 << 4)
+#define SFF8636_RX_PWR_4_HALARM (1 << 3)
+#define SFF8636_RX_PWR_4_LALARM (1 << 2)
+#define SFF8636_RX_PWR_4_HWARN (1 << 1)
+#define SFF8636_RX_PWR_4_LWARN (1 << 0)
+
+#define SFF8636_TX_BIAS_12_AW_OFFSET 0x0B
+#define SFF8636_TX_BIAS_1_HALARM (1 << 7)
+#define SFF8636_TX_BIAS_1_LALARM (1 << 6)
+#define SFF8636_TX_BIAS_1_HWARN (1 << 5)
+#define SFF8636_TX_BIAS_1_LWARN (1 << 4)
+#define SFF8636_TX_BIAS_2_HALARM (1 << 3)
+#define SFF8636_TX_BIAS_2_LALARM (1 << 2)
+#define SFF8636_TX_BIAS_2_HWARN (1 << 1)
+#define SFF8636_TX_BIAS_2_LWARN (1 << 0)
+
+#define SFF8636_TX_BIAS_34_AW_OFFSET 0xC
+#define SFF8636_TX_BIAS_3_HALARM (1 << 7)
+#define SFF8636_TX_BIAS_3_LALARM (1 << 6)
+#define SFF8636_TX_BIAS_3_HWARN (1 << 5)
+#define SFF8636_TX_BIAS_3_LWARN (1 << 4)
+#define SFF8636_TX_BIAS_4_HALARM (1 << 3)
+#define SFF8636_TX_BIAS_4_LALARM (1 << 2)
+#define SFF8636_TX_BIAS_4_HWARN (1 << 1)
+#define SFF8636_TX_BIAS_4_LWARN (1 << 0)
+
+#define SFF8636_TX_PWR_12_AW_OFFSET 0x0D
+#define SFF8636_TX_PWR_1_HALARM (1 << 7)
+#define SFF8636_TX_PWR_1_LALARM (1 << 6)
+#define SFF8636_TX_PWR_1_HWARN (1 << 5)
+#define SFF8636_TX_PWR_1_LWARN (1 << 4)
+#define SFF8636_TX_PWR_2_HALARM (1 << 3)
+#define SFF8636_TX_PWR_2_LALARM (1 << 2)
+#define SFF8636_TX_PWR_2_HWARN (1 << 1)
+#define SFF8636_TX_PWR_2_LWARN (1 << 0)
+
+#define SFF8636_TX_PWR_34_AW_OFFSET 0x0E
+#define SFF8636_TX_PWR_3_HALARM (1 << 7)
+#define SFF8636_TX_PWR_3_LALARM (1 << 6)
+#define SFF8636_TX_PWR_3_HWARN (1 << 5)
+#define SFF8636_TX_PWR_3_LWARN (1 << 4)
+#define SFF8636_TX_PWR_4_HALARM (1 << 3)
+#define SFF8636_TX_PWR_4_LALARM (1 << 2)
+#define SFF8636_TX_PWR_4_HWARN (1 << 1)
+#define SFF8636_TX_PWR_4_LWARN (1 << 0)
+
+/* Module Monitoring Values - 22-33 */
+#define SFF8636_TEMP_CURR 0x16
+#define SFF8636_TEMP_MSB_OFFSET 0x16
+#define SFF8636_TEMP_LSB_OFFSET 0x17
+
+#define SFF8636_VCC_CURR 0x1A
+#define SFF8636_VCC_MSB_OFFSET 0x1A
+#define SFF8636_VCC_LSB_OFFSET 0x1B
+
+/* Channel Monitoring Values - 34-81 */
+#define SFF8636_RX_PWR_1_OFFSET 0x22
+#define SFF8636_RX_PWR_2_OFFSET 0x24
+#define SFF8636_RX_PWR_3_OFFSET 0x26
+#define SFF8636_RX_PWR_4_OFFSET 0x28
+
+#define SFF8636_TX_BIAS_1_OFFSET 0x2A
+#define SFF8636_TX_BIAS_2_OFFSET 0x2C
+#define SFF8636_TX_BIAS_3_OFFSET 0x2E
+#define SFF8636_TX_BIAS_4_OFFSET 0x30
+
+#define SFF8636_TX_PWR_1_OFFSET 0x32
+#define SFF8636_TX_PWR_2_OFFSET 0x34
+#define SFF8636_TX_PWR_3_OFFSET 0x36
+#define SFF8636_TX_PWR_4_OFFSET 0x38
+
+/* Control Bytes - 86 - 99 */
+#define SFF8636_TX_DISABLE_OFFSET 0x56
+#define SFF8636_TX_DISABLE_4 (1 << 3)
+#define SFF8636_TX_DISABLE_3 (1 << 2)
+#define SFF8636_TX_DISABLE_2 (1 << 1)
+#define SFF8636_TX_DISABLE_1 (1 << 0)
+
+#define SFF8636_RX_RATE_SELECT_OFFSET 0x57
+#define SFF8636_RX_RATE_SELECT_4_MASK (3 << 6)
+#define SFF8636_RX_RATE_SELECT_3_MASK (3 << 4)
+#define SFF8636_RX_RATE_SELECT_2_MASK (3 << 2)
+#define SFF8636_RX_RATE_SELECT_1_MASK (3 << 0)
+
+#define SFF8636_TX_RATE_SELECT_OFFSET 0x58
+#define SFF8636_TX_RATE_SELECT_4_MASK (3 << 6)
+#define SFF8636_TX_RATE_SELECT_3_MASK (3 << 4)
+#define SFF8636_TX_RATE_SELECT_2_MASK (3 << 2)
+#define SFF8636_TX_RATE_SELECT_1_MASK (3 << 0)
+
+#define SFF8636_RX_APP_SELECT_4_OFFSET 0x58
+#define SFF8636_RX_APP_SELECT_3_OFFSET 0x59
+#define SFF8636_RX_APP_SELECT_2_OFFSET 0x5A
+#define SFF8636_RX_APP_SELECT_1_OFFSET 0x5B
+
+#define SFF8636_PWR_MODE_OFFSET 0x5D
+#define SFF8636_HIGH_PWR_ENABLE (1 << 2)
+#define SFF8636_LOW_PWR_SET (1 << 1)
+#define SFF8636_PWR_OVERRIDE (1 << 0)
+
+#define SFF8636_TX_APP_SELECT_4_OFFSET 0x5E
+#define SFF8636_TX_APP_SELECT_3_OFFSET 0x5F
+#define SFF8636_TX_APP_SELECT_2_OFFSET 0x60
+#define SFF8636_TX_APP_SELECT_1_OFFSET 0x61
+
+#define SFF8636_LOS_MASK_OFFSET 0x64
+#define SFF8636_TX_LOS_4_MASK (1 << 7)
+#define SFF8636_TX_LOS_3_MASK (1 << 6)
+#define SFF8636_TX_LOS_2_MASK (1 << 5)
+#define SFF8636_TX_LOS_1_MASK (1 << 4)
+#define SFF8636_RX_LOS_4_MASK (1 << 3)
+#define SFF8636_RX_LOS_3_MASK (1 << 2)
+#define SFF8636_RX_LOS_2_MASK (1 << 1)
+#define SFF8636_RX_LOS_1_MASK (1 << 0)
+
+#define SFF8636_FAULT_MASK_OFFSET 0x65
+#define SFF8636_TX_FAULT_1_MASK (1 << 3)
+#define SFF8636_TX_FAULT_2_MASK (1 << 2)
+#define SFF8636_TX_FAULT_3_MASK (1 << 1)
+#define SFF8636_TX_FAULT_4_MASK (1 << 0)
+
+#define SFF8636_TEMP_MASK_OFFSET 0x67
+#define SFF8636_TEMP_HALARM_MASK (1 << 7)
+#define SFF8636_TEMP_LALARM_MASK (1 << 6)
+#define SFF8636_TEMP_HWARN_MASK (1 << 5)
+#define SFF8636_TEMP_LWARN_MASK (1 << 4)
+
+#define SFF8636_VCC_MASK_OFFSET 0x68
+#define SFF8636_VCC_HALARM_MASK (1 << 7)
+#define SFF8636_VCC_LALARM_MASK (1 << 6)
+#define SFF8636_VCC_HWARN_MASK (1 << 5)
+#define SFF8636_VCC_LWARN_MASK (1 << 4)
+
+/*------------------------------------------------------------------------------
+ *
+ * Upper Memory Page 00h
+ * Serial ID - Base ID, Extended ID and Vendor Specific ID fields
+ *
+ */
+/* Identifier - 128 */
+/* Identifier values same as Lower Memory Page 00h */
+#define SFF8636_UPPER_PAGE_0_ID_OFFSET 0x80
+
+/* Extended Identifier - 128 */
+#define SFF8636_EXT_ID_OFFSET 0x81
+#define SFF8636_EXT_ID_PWR_CLASS_MASK 0xC0
+#define SFF8636_EXT_ID_PWR_CLASS_1 (0 << 6)
+#define SFF8636_EXT_ID_PWR_CLASS_2 (1 << 6)
+#define SFF8636_EXT_ID_PWR_CLASS_3 (2 << 6)
+#define SFF8636_EXT_ID_PWR_CLASS_4 (3 << 6)
+#define SFF8636_EXT_ID_CLIE_MASK 0x10
+#define SFF8636_EXT_ID_CLIEI_CODE_PRESENT (1 << 4)
+#define SFF8636_EXT_ID_CDR_TX_MASK 0x08
+#define SFF8636_EXT_ID_CDR_TX_PRESENT (1 << 3)
+#define SFF8636_EXT_ID_CDR_RX_MASK 0x04
+#define SFF8636_EXT_ID_CDR_RX_PRESENT (1 << 2)
+#define SFF8636_EXT_ID_EPWR_CLASS_MASK 0x03
+#define SFF8636_EXT_ID_PWR_CLASS_LEGACY 0
+#define SFF8636_EXT_ID_PWR_CLASS_5 1
+#define SFF8636_EXT_ID_PWR_CLASS_6 2
+#define SFF8636_EXT_ID_PWR_CLASS_7 3
+
+/* Connector Values offset - 130 */
+/* Values are defined under SFF8024_CTOR */
+#define SFF8636_CTOR_OFFSET 0x82
+#define SFF8636_CTOR_UNKNOWN 0x00
+#define SFF8636_CTOR_SC 0x01
+#define SFF8636_CTOR_FC_STYLE_1 0x02
+#define SFF8636_CTOR_FC_STYLE_2 0x03
+#define SFF8636_CTOR_BNC_TNC 0x04
+#define SFF8636_CTOR_FC_COAX 0x05
+#define SFF8636_CTOR_FIBER_JACK 0x06
+#define SFF8636_CTOR_LC 0x07
+#define SFF8636_CTOR_MT_RJ 0x08
+#define SFF8636_CTOR_MU 0x09
+#define SFF8636_CTOR_SG 0x0A
+#define SFF8636_CTOR_OPT_PT 0x0B
+#define SFF8636_CTOR_MPO 0x0C
+/* 0D-1Fh --- Reserved */
+#define SFF8636_CTOR_HSDC_II 0x20
+#define SFF8636_CTOR_COPPER_PT 0x21
+#define SFF8636_CTOR_RJ45 0x22
+#define SFF8636_CTOR_NO_SEPARABLE 0x23
+#define SFF8636_CTOR_MXC_2X16 0x24
+
+/* Specification Compliance - 131-138 */
+/* Ethernet Compliance Codes - 131 */
+#define SFF8636_ETHERNET_COMP_OFFSET 0x83
+#define SFF8636_ETHERNET_RSRVD (1 << 7)
+#define SFF8636_ETHERNET_10G_LRM (1 << 6)
+#define SFF8636_ETHERNET_10G_LR (1 << 5)
+#define SFF8636_ETHERNET_10G_SR (1 << 4)
+#define SFF8636_ETHERNET_40G_CR4 (1 << 3)
+#define SFF8636_ETHERNET_40G_SR4 (1 << 2)
+#define SFF8636_ETHERNET_40G_LR4 (1 << 1)
+#define SFF8636_ETHERNET_40G_ACTIVE (1 << 0)
+
+/* SONET Compliance Codes - 132 */
+#define SFF8636_SONET_COMP_OFFSET 0x84
+#define SFF8636_SONET_40G_OTN (1 << 3)
+#define SFF8636_SONET_OC48_LR (1 << 2)
+#define SFF8636_SONET_OC48_IR (1 << 1)
+#define SFF8636_SONET_OC48_SR (1 << 0)
+
+/* SAS/SATA Complaince Codes - 133 */
+#define SFF8636_SAS_COMP_OFFSET 0x85
+#define SFF8636_SAS_12G (1 << 6)
+#define SFF8636_SAS_6G (1 << 5)
+#define SFF8636_SAS_3G (1 << 4)
+
+/* Gigabit Ethernet Compliance Codes - 134 */
+#define SFF8636_GIGE_COMP_OFFSET 0x86
+#define SFF8636_GIGE_1000_BASE_T (1 << 3)
+#define SFF8636_GIGE_1000_BASE_CX (1 << 2)
+#define SFF8636_GIGE_1000_BASE_LX (1 << 1)
+#define SFF8636_GIGE_1000_BASE_SX (1 << 0)
+
+/* Fibre Channel Link length/Transmitter Tech. - 135,136 */
+#define SFF8636_FC_LEN_OFFSET 0x87
+#define SFF8636_FC_LEN_VERY_LONG (1 << 7)
+#define SFF8636_FC_LEN_SHORT (1 << 6)
+#define SFF8636_FC_LEN_INT (1 << 5)
+#define SFF8636_FC_LEN_LONG (1 << 4)
+#define SFF8636_FC_LEN_MED (1 << 3)
+#define SFF8636_FC_TECH_LONG_LC (1 << 1)
+#define SFF8636_FC_TECH_ELEC_INTER (1 << 0)
+
+#define SFF8636_FC_TECH_OFFSET 0x88
+#define SFF8636_FC_TECH_ELEC_INTRA (1 << 7)
+#define SFF8636_FC_TECH_SHORT_WO_OFC (1 << 6)
+#define SFF8636_FC_TECH_SHORT_W_OFC (1 << 5)
+#define SFF8636_FC_TECH_LONG_LL (1 << 4)
+
+/* Fibre Channel Transmitter Media - 137 */
+#define SFF8636_FC_TRANS_MEDIA_OFFSET 0x89
+/* Twin Axial Pair */
+#define SFF8636_FC_TRANS_MEDIA_TW (1 << 7)
+/* Shielded Twisted Pair */
+#define SFF8636_FC_TRANS_MEDIA_TP (1 << 6)
+/* Miniature Coax */
+#define SFF8636_FC_TRANS_MEDIA_MI (1 << 5)
+/* Video Coax */
+#define SFF8636_FC_TRANS_MEDIA_TV (1 << 4)
+/* Multi-mode 62.5m */
+#define SFF8636_FC_TRANS_MEDIA_M6 (1 << 3)
+/* Multi-mode 50m */
+#define SFF8636_FC_TRANS_MEDIA_M5 (1 << 2)
+/* Multi-mode 50um */
+#define SFF8636_FC_TRANS_MEDIA_OM3 (1 << 1)
+/* Single Mode */
+#define SFF8636_FC_TRANS_MEDIA_SM (1 << 0)
+
+/* Fibre Channel Speed - 138 */
+#define SFF8636_FC_SPEED_OFFSET 0x8A
+#define SFF8636_FC_SPEED_1200_MBPS (1 << 7)
+#define SFF8636_FC_SPEED_800_MBPS (1 << 6)
+#define SFF8636_FC_SPEED_1600_MBPS (1 << 5)
+#define SFF8636_FC_SPEED_400_MBPS (1 << 4)
+#define SFF8636_FC_SPEED_200_MBPS (1 << 2)
+#define SFF8636_FC_SPEED_100_MBPS (1 << 0)
+
+/* Encoding - 139 */
+/* Values are defined under SFF8024_ENCODING */
+#define SFF8636_ENCODING_OFFSET 0x8B
+#define SFF8636_ENCODING_MANCHESTER 0x06
+#define SFF8636_ENCODING_64B66B 0x05
+#define SFF8636_ENCODING_SONET 0x04
+#define SFF8636_ENCODING_NRZ 0x03
+#define SFF8636_ENCODING_4B5B 0x02
+#define SFF8636_ENCODING_8B10B 0x01
+#define SFF8636_ENCODING_UNSPEC 0x00
+
+/* BR, Nominal - 140 */
+#define SFF8636_BR_NOMINAL_OFFSET 0x8C
+
+/* Extended RateSelect - 141 */
+#define SFF8636_EXT_RS_OFFSET 0x8D
+#define SFF8636_EXT_RS_V1 (1 << 0)
+
+/* Length (Standard SM Fiber)-km - 142 */
+#define SFF8636_SM_LEN_OFFSET 0x8E
+
+/* Length (OM3)-Unit 2m - 143 */
+#define SFF8636_OM3_LEN_OFFSET 0x8F
+
+/* Length (OM2)-Unit 1m - 144 */
+#define SFF8636_OM2_LEN_OFFSET 0x90
+
+/* Length (OM1)-Unit 1m - 145 */
+#define SFF8636_OM1_LEN_OFFSET 0x91
+
+/* Cable Assembly Length -Unit 1m - 146 */
+#define SFF8636_CBL_LEN_OFFSET 0x92
+
+/* Device Technology - 147 */
+#define SFF8636_DEVICE_TECH_OFFSET 0x93
+/* Transmitter Technology */
+#define SFF8636_TRANS_TECH_MASK 0xF0
+/* Copper cable, linear active equalizers */
+#define SFF8636_TRANS_COPPER_LNR_EQUAL (15 << 4)
+/* Copper cable, near end limiting active equalizers */
+#define SFF8636_TRANS_COPPER_NEAR_EQUAL (14 << 4)
+/* Copper cable, far end limiting active equalizers */
+#define SFF8636_TRANS_COPPER_FAR_EQUAL (13 << 4)
+/* Copper cable, near & far end limiting active equalizers */
+#define SFF8636_TRANS_COPPER_LNR_FAR_EQUAL (12 << 4)
+/* Copper cable, passive equalized */
+#define SFF8636_TRANS_COPPER_PAS_EQUAL (11 << 4)
+/* Copper cable, unequalized */
+#define SFF8636_TRANS_COPPER_PAS_UNEQUAL (10 << 4)
+/* 1490 nm DFB */
+#define SFF8636_TRANS_1490_DFB (9 << 4)
+/* Others */
+#define SFF8636_TRANS_OTHERS (8 << 4)
+/* 1550 nm EML */
+#define SFF8636_TRANS_1550_EML (7 << 4)
+/* 1310 nm EML */
+#define SFF8636_TRANS_1310_EML (6 << 4)
+/* 1550 nm DFB */
+#define SFF8636_TRANS_1550_DFB (5 << 4)
+/* 1310 nm DFB */
+#define SFF8636_TRANS_1310_DFB (4 << 4)
+/* 1310 nm FP */
+#define SFF8636_TRANS_1310_FP (3 << 4)
+/* 1550 nm VCSEL */
+#define SFF8636_TRANS_1550_VCSEL (2 << 4)
+/* 1310 nm VCSEL */
+#define SFF8636_TRANS_1310_VCSEL (1 << 4)
+/* 850 nm VCSEL */
+#define SFF8636_TRANS_850_VCSEL (0 << 4)
+
+ /* Active/No wavelength control */
+#define SFF8636_DEV_TECH_ACTIVE_WAVE_LEN (1 << 3)
+/* Cooled transmitter */
+#define SFF8636_DEV_TECH_COOL_TRANS (1 << 2)
+/* APD/Pin Detector */
+#define SFF8636_DEV_TECH_APD_DETECTOR (1 << 1)
+/* Transmitter tunable */
+#define SFF8636_DEV_TECH_TUNABLE (1 << 0)
+
+/* Vendor Name - 148-163 */
+#define SFF8636_VENDOR_NAME_START_OFFSET 0x94
+#define SFF8636_VENDOR_NAME_END_OFFSET 0xA3
+
+/* Extended Module Codes - 164 */
+#define SFF8636_EXT_MOD_CODE_OFFSET 0xA4
+#define SFF8636_EXT_MOD_INFINIBAND_EDR (1 << 4)
+#define SFF8636_EXT_MOD_INFINIBAND_FDR (1 << 3)
+#define SFF8636_EXT_MOD_INFINIBAND_QDR (1 << 2)
+#define SFF8636_EXT_MOD_INFINIBAND_DDR (1 << 1)
+#define SFF8636_EXT_MOD_INFINIBAND_SDR (1 << 0)
+
+/* Vendor OUI - 165-167 */
+#define SFF8636_VENDOR_OUI_OFFSET 0xA5
+#define SFF8636_VENDOR_OUI_LEN 3
+
+/* Vendor OUI - 165-167 */
+#define SFF8636_VENDOR_PN_START_OFFSET 0xA8
+#define SFF8636_VENDOR_PN_END_OFFSET 0xB7
+
+/* Vendor Revision - 184-185 */
+#define SFF8636_VENDOR_REV_START_OFFSET 0xB8
+#define SFF8636_VENDOR_REV_END_OFFSET 0xB9
+
+/* Wavelength - 186-187 */
+#define SFF8636_WAVELEN_HIGH_BYTE_OFFSET 0xBA
+#define SFF8636_WAVELEN_LOW_BYTE_OFFSET 0xBB
+
+/* Wavelength Tolerance- 188-189 */
+#define SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET 0xBC
+#define SFF8636_WAVE_TOL_LOW_BYTE_OFFSET 0xBD
+
+/* Max case temp - Other than 70 C - 190 */
+#define SFF8636_MAXCASE_TEMP_OFFSET 0xBE
+
+/* CC_BASE - 191 */
+#define SFF8636_CC_BASE_OFFSET 0xBF
+
+/* Option Values - 192-195 */
+#define SFF8636_OPTION_1_OFFSET 0xC0
+#define SFF8636_ETHERNET_UNSPECIFIED 0x00
+#define SFF8636_ETHERNET_100G_AOC 0x01
+#define SFF8636_ETHERNET_100G_SR4 0x02
+#define SFF8636_ETHERNET_100G_LR4 0x03
+#define SFF8636_ETHERNET_100G_ER4 0x04
+#define SFF8636_ETHERNET_100G_SR10 0x05
+#define SFF8636_ETHERNET_100G_CWDM4_FEC 0x06
+#define SFF8636_ETHERNET_100G_PSM4 0x07
+#define SFF8636_ETHERNET_100G_ACC 0x08
+#define SFF8636_ETHERNET_100G_CWDM4_NO_FEC 0x09
+#define SFF8636_ETHERNET_100G_RSVD1 0x0A
+#define SFF8636_ETHERNET_100G_CR4 0x0B
+#define SFF8636_ETHERNET_25G_CR_CA_S 0x0C
+#define SFF8636_ETHERNET_25G_CR_CA_N 0x0D
+#define SFF8636_ETHERNET_40G_ER4 0x10
+#define SFF8636_ETHERNET_4X10_SR 0x11
+#define SFF8636_ETHERNET_40G_PSM4 0x12
+#define SFF8636_ETHERNET_G959_P1I1_2D1 0x13
+#define SFF8636_ETHERNET_G959_P1S1_2D2 0x14
+#define SFF8636_ETHERNET_G959_P1L1_2D2 0x15
+#define SFF8636_ETHERNET_10GT_SFI 0x16
+#define SFF8636_ETHERNET_100G_CLR4 0x17
+#define SFF8636_ETHERNET_100G_AOC2 0x18
+#define SFF8636_ETHERNET_100G_ACC2 0x19
+
+#define SFF8636_ETHERNET_100GE_DWDM2 0x1A
+#define SFF8636_ETHERNET_100G_1550NM_WDM 0x1B
+#define SFF8636_ETHERNET_10G_BASET_SR 0x1C
+#define SFF8636_ETHERNET_5G_BASET 0x1D
+#define SFF8636_ETHERNET_2HALFG_BASET 0x1E
+#define SFF8636_ETHERNET_40G_SWDM4 0x1F
+#define SFF8636_ETHERNET_100G_SWDM4 0x20
+#define SFF8636_ETHERNET_100G_PAM4_BIDI 0x21
+#define SFF8636_ETHERNET_4WDM10_MSA 0x22
+#define SFF8636_ETHERNET_4WDM20_MSA 0x23
+#define SFF8636_ETHERNET_4WDM40_MSA 0x24
+#define SFF8636_ETHERNET_100G_DR 0x25
+#define SFF8636_ETHERNET_100G_FR_NOFEC 0x26
+#define SFF8636_ETHERNET_100G_LR_NOFEC 0x27
+/* 28h-2Fh reserved */
+#define SFF8636_ETHERNET_200G_ACC1 0x30
+#define SFF8636_ETHERNET_200G_AOC1 0x31
+#define SFF8636_ETHERNET_200G_ACC2 0x32
+#define SFF8636_ETHERNET_200G_A0C2 0x33
+/* 34h-3Fh reserved */
+#define SFF8636_ETHERNET_200G_CR4 0x40
+#define SFF8636_ETHERNET_200G_SR4 0x41
+#define SFF8636_ETHERNET_200G_DR4 0x42
+#define SFF8636_ETHERNET_200G_FR4 0x43
+#define SFF8636_ETHERNET_200G_PSM4 0x44
+#define SFF8636_ETHERNET_50G_LR 0x45
+#define SFF8636_ETHERNET_200G_LR4 0x46
+/* 47h-4Fh reserved */
+#define SFF8636_ETHERNET_64G_EA 0x50
+#define SFF8636_ETHERNET_64G_SW 0x51
+#define SFF8636_ETHERNET_64G_LW 0x52
+#define SFF8636_ETHERNET_128FC_EA 0x53
+#define SFF8636_ETHERNET_128FC_SW 0x54
+#define SFF8636_ETHERNET_128FC_LW 0x55
+/* 56h-5Fh reserved */
+
+#define SFF8636_OPTION_2_OFFSET 0xC1
+/* Tx input equalizers auto-adaptive */
+#define SFF8636_O2_TX_EQ_AUTO (1 << 3)
+/* Rx output amplitude */
+#define SFF8636_O2_RX_OUTPUT_AMP (1 << 0)
+#define SFF8636_OPTION_3_OFFSET 0xC2
+/* Tx CDR Loss of Lock */
+#define SFF8636_O3_TX_LOL (1 << 5)
+/* Rx CDR Loss of Lock */
+#define SFF8636_O3_RX_LOL (1 << 4)
+/* Rx Squelch Disable */
+#define SFF8636_O3_RX_SQL_DSBL (1 << 3)
+/* Rx Output Disable capable */
+#define SFF8636_O3_RX_OUTPUT_DSBL (1 << 2)
+/* Tx Squelch Disable */
+#define SFF8636_O3_TX_SQL_DSBL (1 << 1)
+/* Tx Squelch Impl */
+#define SFF8636_O3_TX_SQL_IMPL (1 << 0)
+#define SFF8636_OPTION_4_OFFSET 0xC3
+/* Memory Page 02 present */
+#define SFF8636_O4_PAGE_02_PRESENT (1 << 7)
+/* Memory Page 01 present */
+#define SFF8636_O4_PAGE_01_PRESENT (1 << 6)
+/* Rate Select implemented */
+#define SFF8636_O4_RATE_SELECT (1 << 5)
+/* Tx_DISABLE implemented */
+#define SFF8636_O4_TX_DISABLE (1 << 4)
+/* Tx_FAULT implemented */
+#define SFF8636_O4_TX_FAULT (1 << 3)
+/* Tx Squelch implemented */
+#define SFF8636_O4_TX_SQUELCH (1 << 2)
+/* Tx Loss of Signal */
+#define SFF8636_O4_TX_LOS (1 << 1)
+
+/* Vendor SN - 196-211 */
+#define SFF8636_VENDOR_SN_START_OFFSET 0xC4
+#define SFF8636_VENDOR_SN_END_OFFSET 0xD3
+
+/* Vendor Date - 212-219 */
+#define SFF8636_DATE_YEAR_OFFSET 0xD4
+#define SFF8636_DATE_YEAR_LEN 2
+#define SFF8636_DATE_MONTH_OFFSET 0xD6
+#define SFF8636_DATE_MONTH_LEN 2
+#define SFF8636_DATE_DAY_OFFSET 0xD8
+#define SFF8636_DATE_DAY_LEN 2
+#define SFF8636_DATE_VENDOR_LOT_OFFSET 0xDA
+#define SFF8636_DATE_VENDOR_LOT_LEN 2
+
+/* Diagnostic Monitoring Type - 220 */
+#define SFF8636_DIAG_TYPE_OFFSET 0xDC
+#define SFF8636_RX_PWR_TYPE_MASK 0x8
+#define SFF8636_RX_PWR_TYPE_AVG_PWR (1 << 3)
+#define SFF8636_RX_PWR_TYPE_OMA (0 << 3)
+#define SFF8636_TX_PWR_TYPE_MASK 0x4
+#define SFF8636_TX_PWR_TYPE_AVG_PWR (1 << 2)
+
+/* Enhanced Options - 221 */
+#define SFF8636_ENH_OPTIONS_OFFSET 0xDD
+#define SFF8636_RATE_SELECT_EXT_SUPPORT (1 << 3)
+#define SFF8636_RATE_SELECT_APP_TABLE_SUPPORT (1 << 2)
+
+/* Check code - 223 */
+#define SFF8636_CC_EXT_OFFSET 0xDF
+#define SFF8636_CC_EXT_LEN 1
+
+/*------------------------------------------------------------------------------
+ *
+ * Upper Memory Page 03h
+ * Contains module thresholds, channel thresholds and masks,
+ * and optional channel controls
+ *
+ * Offset - Page Num(3) * PageSize(0x80) + Page offset
+ */
+
+/* 3 * 128 + Lower page 00h(128) */
+#define SFF8636_PAGE03H_OFFSET (128 * 4)
+
+/* Module Thresholds (48 Bytes) 128-175 */
+/* MSB at low address, LSB at high address */
+#define SFF8636_TEMP_HALRM 0x80
+#define SFF8636_TEMP_LALRM 0x82
+#define SFF8636_TEMP_HWARN 0x84
+#define SFF8636_TEMP_LWARN 0x86
+
+#define SFF8636_VCC_HALRM 0x90
+#define SFF8636_VCC_LALRM 0x92
+#define SFF8636_VCC_HWARN 0x94
+#define SFF8636_VCC_LWARN 0x96
+
+#define SFF8636_RX_PWR_HALRM 0xB0
+#define SFF8636_RX_PWR_LALRM 0xB2
+#define SFF8636_RX_PWR_HWARN 0xB4
+#define SFF8636_RX_PWR_LWARN 0xB6
+
+#define SFF8636_TX_BIAS_HALRM 0xB8
+#define SFF8636_TX_BIAS_LALRM 0xBA
+#define SFF8636_TX_BIAS_HWARN 0xBC
+#define SFF8636_TX_BIAS_LWARN 0xBE
+
+#define SFF8636_TX_PWR_HALRM 0xC0
+#define SFF8636_TX_PWR_LALRM 0xC2
+#define SFF8636_TX_PWR_HWARN 0xC4
+#define SFF8636_TX_PWR_LWARN 0xC6
+
+#define ETH_MODULE_SFF_8636_MAX_LEN 640
+#define ETH_MODULE_SFF_8436_MAX_LEN 640
+
+#endif /* QSFP_H__ */
diff --git a/realtek.c b/realtek.c
new file mode 100644
index 0000000..ee0c611
--- /dev/null
+++ b/realtek.c
@@ -0,0 +1,689 @@
+/* Copyright 2001 Sun Microsystems (thockin@sun.com) */
+#include <stdio.h>
+#include <stdlib.h>
+#include "internal.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+enum chip_type {
+ RTL8139 = 1,
+ RTL8139_K,
+ RTL8139A,
+ RTL8139A_G,
+ RTL8139B,
+ RTL8130,
+ RTL8139C,
+ RTL8100,
+ RTL8100B_8139D,
+ RTL8139Cp,
+ RTL8101,
+
+ /* chips not handled by 8139too/8139cp module */
+ RTL_GIGA_MAC_VER_01,
+ RTL_GIGA_MAC_VER_02,
+ RTL_GIGA_MAC_VER_03,
+ RTL_GIGA_MAC_VER_04,
+ RTL_GIGA_MAC_VER_05,
+ RTL_GIGA_MAC_VER_06,
+ RTL_GIGA_MAC_VER_07,
+ RTL_GIGA_MAC_VER_08,
+ RTL_GIGA_MAC_VER_09,
+ RTL_GIGA_MAC_VER_10,
+ RTL_GIGA_MAC_VER_11,
+ RTL_GIGA_MAC_VER_12,
+ RTL_GIGA_MAC_VER_13,
+ RTL_GIGA_MAC_VER_14,
+ RTL_GIGA_MAC_VER_15,
+ RTL_GIGA_MAC_VER_16,
+ RTL_GIGA_MAC_VER_17,
+ RTL_GIGA_MAC_VER_18,
+ RTL_GIGA_MAC_VER_19,
+ RTL_GIGA_MAC_VER_20,
+ RTL_GIGA_MAC_VER_21,
+ RTL_GIGA_MAC_VER_22,
+ RTL_GIGA_MAC_VER_23,
+ RTL_GIGA_MAC_VER_24,
+ RTL_GIGA_MAC_VER_25,
+ RTL_GIGA_MAC_VER_26,
+ RTL_GIGA_MAC_VER_27,
+ RTL_GIGA_MAC_VER_28,
+ RTL_GIGA_MAC_VER_29,
+ RTL_GIGA_MAC_VER_30,
+ RTL_GIGA_MAC_VER_31,
+ RTL_GIGA_MAC_VER_32,
+ RTL_GIGA_MAC_VER_33,
+ RTL_GIGA_MAC_VER_34,
+ RTL_GIGA_MAC_VER_35,
+ RTL_GIGA_MAC_VER_36,
+ RTL_GIGA_MAC_VER_37,
+ RTL_GIGA_MAC_VER_38,
+ RTL_GIGA_MAC_VER_39,
+ RTL_GIGA_MAC_VER_40,
+ RTL_GIGA_MAC_VER_41,
+ RTL_GIGA_MAC_VER_42,
+ RTL_GIGA_MAC_VER_43,
+ RTL_GIGA_MAC_VER_44,
+};
+
+static const char * const chip_names[] = {
+ [RTL8139] = "8139",
+ [RTL8139_K] = "8139-K",
+ [RTL8139A] = "8139A",
+ [RTL8139A_G] = "8139A-G",
+ [RTL8139B] = "8139B",
+ [RTL8130] = "8130",
+ [RTL8139C] = "8139C",
+ [RTL8100] = "8100",
+ [RTL8100B_8139D] = "8100B/8139D",
+ [RTL8139Cp] = "8139C+",
+ [RTL8101] = "8101",
+
+ /* chips not handled by 8139too/8139cp module */
+ [RTL_GIGA_MAC_VER_01] = "8169",
+ [RTL_GIGA_MAC_VER_02] = "8169s",
+ [RTL_GIGA_MAC_VER_03] = "8110s",
+ [RTL_GIGA_MAC_VER_04] = "8169sb/8110sb",
+ [RTL_GIGA_MAC_VER_05] = "8169sc/8110sc",
+ [RTL_GIGA_MAC_VER_06] = "8169sc/8110sc",
+ [RTL_GIGA_MAC_VER_07] = "8102e",
+ [RTL_GIGA_MAC_VER_08] = "8102e",
+ [RTL_GIGA_MAC_VER_09] = "8102e",
+ [RTL_GIGA_MAC_VER_10] = "8101e",
+ [RTL_GIGA_MAC_VER_11] = "8168b/8111b",
+ [RTL_GIGA_MAC_VER_12] = "8168b/8111b",
+ [RTL_GIGA_MAC_VER_13] = "8101e",
+ [RTL_GIGA_MAC_VER_14] = "8100e",
+ [RTL_GIGA_MAC_VER_15] = "8100e",
+ [RTL_GIGA_MAC_VER_16] = "8101e",
+ [RTL_GIGA_MAC_VER_17] = "8168b/8111b",
+ [RTL_GIGA_MAC_VER_18] = "8168cp/8111cp",
+ [RTL_GIGA_MAC_VER_19] = "8168c/8111c",
+ [RTL_GIGA_MAC_VER_20] = "8168c/8111c",
+ [RTL_GIGA_MAC_VER_21] = "8168c/8111c",
+ [RTL_GIGA_MAC_VER_22] = "8168c/8111c",
+ [RTL_GIGA_MAC_VER_23] = "8168cp/8111cp",
+ [RTL_GIGA_MAC_VER_24] = "8168cp/8111cp",
+ [RTL_GIGA_MAC_VER_25] = "8168d/8111d",
+ [RTL_GIGA_MAC_VER_26] = "8168d/8111d",
+ [RTL_GIGA_MAC_VER_27] = "8168dp/8111dp",
+ [RTL_GIGA_MAC_VER_28] = "8168dp/8111dp",
+ [RTL_GIGA_MAC_VER_29] = "8105e",
+ [RTL_GIGA_MAC_VER_30] = "8105e",
+ [RTL_GIGA_MAC_VER_31] = "8168dp/8111dp",
+ [RTL_GIGA_MAC_VER_32] = "8168e/8111e",
+ [RTL_GIGA_MAC_VER_33] = "8168e/8111e",
+ [RTL_GIGA_MAC_VER_34] = "8168evl/8111evl",
+ [RTL_GIGA_MAC_VER_35] = "8168f/8111f",
+ [RTL_GIGA_MAC_VER_36] = "8168f/8111f",
+ [RTL_GIGA_MAC_VER_37] = "8402",
+ [RTL_GIGA_MAC_VER_38] = "8411",
+ [RTL_GIGA_MAC_VER_39] = "8106e",
+ [RTL_GIGA_MAC_VER_40] = "8168g/8111g",
+ [RTL_GIGA_MAC_VER_41] = "8168g/8111g",
+ [RTL_GIGA_MAC_VER_42] = "8168g/8111g",
+ [RTL_GIGA_MAC_VER_43] = "8106e",
+ [RTL_GIGA_MAC_VER_44] = "8411",
+};
+
+static struct chip_info {
+ u32 id_mask;
+ u32 id_val;
+ int mac_version;
+} rtl_info_tbl[] = {
+ { 0xfcc00000, 0x40000000, RTL8139 },
+ { 0xfcc00000, 0x60000000, RTL8139_K },
+ { 0xfcc00000, 0x70000000, RTL8139A },
+ { 0xfcc00000, 0x70800000, RTL8139A_G },
+ { 0xfcc00000, 0x78000000, RTL8139B },
+ { 0xfcc00000, 0x7c000000, RTL8130 },
+ { 0xfcc00000, 0x74000000, RTL8139C },
+ { 0xfcc00000, 0x78800000, RTL8100 },
+ { 0xfcc00000, 0x74400000, RTL8100B_8139D },
+ { 0xfcc00000, 0x74800000, RTL8139Cp },
+ { 0xfcc00000, 0x74c00000, RTL8101 },
+
+ /* chips not handled by 8139too/8139cp module */
+ /* 8168G family. */
+ { 0x7cf00000, 0x5c800000, RTL_GIGA_MAC_VER_44 },
+ { 0x7cf00000, 0x50900000, RTL_GIGA_MAC_VER_42 },
+ { 0x7cf00000, 0x4c100000, RTL_GIGA_MAC_VER_41 },
+ { 0x7cf00000, 0x4c000000, RTL_GIGA_MAC_VER_40 },
+
+ /* 8168F family. */
+ { 0x7c800000, 0x48800000, RTL_GIGA_MAC_VER_38 },
+ { 0x7cf00000, 0x48100000, RTL_GIGA_MAC_VER_36 },
+ { 0x7cf00000, 0x48000000, RTL_GIGA_MAC_VER_35 },
+
+ /* 8168E family. */
+ { 0x7c800000, 0x2c800000, RTL_GIGA_MAC_VER_34 },
+ { 0x7cf00000, 0x2c200000, RTL_GIGA_MAC_VER_33 },
+ { 0x7cf00000, 0x2c100000, RTL_GIGA_MAC_VER_32 },
+ { 0x7c800000, 0x2c000000, RTL_GIGA_MAC_VER_33 },
+
+ /* 8168D family. */
+ { 0x7cf00000, 0x28300000, RTL_GIGA_MAC_VER_26 },
+ { 0x7cf00000, 0x28100000, RTL_GIGA_MAC_VER_25 },
+ { 0x7c800000, 0x28000000, RTL_GIGA_MAC_VER_26 },
+
+ /* 8168DP family. */
+ { 0x7cf00000, 0x28800000, RTL_GIGA_MAC_VER_27 },
+ { 0x7cf00000, 0x28a00000, RTL_GIGA_MAC_VER_28 },
+ { 0x7cf00000, 0x28b00000, RTL_GIGA_MAC_VER_31 },
+
+ /* 8168C family. */
+ { 0x7cf00000, 0x3cb00000, RTL_GIGA_MAC_VER_24 },
+ { 0x7cf00000, 0x3c900000, RTL_GIGA_MAC_VER_23 },
+ { 0x7cf00000, 0x3c800000, RTL_GIGA_MAC_VER_18 },
+ { 0x7c800000, 0x3c800000, RTL_GIGA_MAC_VER_24 },
+ { 0x7cf00000, 0x3c000000, RTL_GIGA_MAC_VER_19 },
+ { 0x7cf00000, 0x3c200000, RTL_GIGA_MAC_VER_20 },
+ { 0x7cf00000, 0x3c300000, RTL_GIGA_MAC_VER_21 },
+ { 0x7cf00000, 0x3c400000, RTL_GIGA_MAC_VER_22 },
+ { 0x7c800000, 0x3c000000, RTL_GIGA_MAC_VER_22 },
+
+ /* 8168B family. */
+ { 0x7cf00000, 0x38000000, RTL_GIGA_MAC_VER_12 },
+ { 0x7cf00000, 0x38500000, RTL_GIGA_MAC_VER_17 },
+ { 0x7c800000, 0x38000000, RTL_GIGA_MAC_VER_17 },
+ { 0x7c800000, 0x30000000, RTL_GIGA_MAC_VER_11 },
+
+ /* 8101 family. */
+ { 0x7cf00000, 0x44900000, RTL_GIGA_MAC_VER_39 },
+ { 0x7c800000, 0x44800000, RTL_GIGA_MAC_VER_39 },
+ { 0x7c800000, 0x44000000, RTL_GIGA_MAC_VER_37 },
+ { 0x7cf00000, 0x40b00000, RTL_GIGA_MAC_VER_30 },
+ { 0x7cf00000, 0x40a00000, RTL_GIGA_MAC_VER_30 },
+ { 0x7cf00000, 0x40900000, RTL_GIGA_MAC_VER_29 },
+ { 0x7c800000, 0x40800000, RTL_GIGA_MAC_VER_30 },
+ { 0x7cf00000, 0x34a00000, RTL_GIGA_MAC_VER_09 },
+ { 0x7cf00000, 0x24a00000, RTL_GIGA_MAC_VER_09 },
+ { 0x7cf00000, 0x34900000, RTL_GIGA_MAC_VER_08 },
+ { 0x7cf00000, 0x24900000, RTL_GIGA_MAC_VER_08 },
+ { 0x7cf00000, 0x34800000, RTL_GIGA_MAC_VER_07 },
+ { 0x7cf00000, 0x24800000, RTL_GIGA_MAC_VER_07 },
+ { 0x7cf00000, 0x34000000, RTL_GIGA_MAC_VER_13 },
+ { 0x7cf00000, 0x34300000, RTL_GIGA_MAC_VER_10 },
+ { 0x7cf00000, 0x34200000, RTL_GIGA_MAC_VER_16 },
+ { 0x7c800000, 0x34800000, RTL_GIGA_MAC_VER_09 },
+ { 0x7c800000, 0x24800000, RTL_GIGA_MAC_VER_09 },
+ { 0x7c800000, 0x34000000, RTL_GIGA_MAC_VER_16 },
+ /* FIXME: where did these entries come from ? -- FR */
+ { 0xfc800000, 0x38800000, RTL_GIGA_MAC_VER_15 },
+ { 0xfc800000, 0x30800000, RTL_GIGA_MAC_VER_14 },
+
+ /* 8110 family. */
+ { 0xfc800000, 0x98000000, RTL_GIGA_MAC_VER_06 },
+ { 0xfc800000, 0x18000000, RTL_GIGA_MAC_VER_05 },
+ { 0xfc800000, 0x10000000, RTL_GIGA_MAC_VER_04 },
+ { 0xfc800000, 0x04000000, RTL_GIGA_MAC_VER_03 },
+ { 0xfc800000, 0x00800000, RTL_GIGA_MAC_VER_02 },
+ { 0xfc800000, 0x00000000, RTL_GIGA_MAC_VER_01 },
+
+ { }
+};
+
+static void
+print_intr_bits(u16 mask)
+{
+ fprintf(stdout,
+ " %s%s%s%s%s%s%s%s%s%s%s\n",
+ mask & (1 << 15) ? "SERR " : "",
+ mask & (1 << 14) ? "TimeOut " : "",
+ mask & (1 << 8) ? "SWInt " : "",
+ mask & (1 << 7) ? "TxNoBuf " : "",
+ mask & (1 << 6) ? "RxFIFO " : "",
+ mask & (1 << 5) ? "LinkChg " : "",
+ mask & (1 << 4) ? "RxNoBuf " : "",
+ mask & (1 << 3) ? "TxErr " : "",
+ mask & (1 << 2) ? "TxOK " : "",
+ mask & (1 << 1) ? "RxErr " : "",
+ mask & (1 << 0) ? "RxOK " : "");
+}
+
+int
+realtek_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *data = (u32 *) regs->data;
+ u8 *data8 = (u8 *) regs->data;
+ u32 v;
+ struct chip_info *ci;
+ unsigned int board_type;
+
+ v = data[0x40 >> 2]; /* TxConfig */
+
+ ci = &rtl_info_tbl[0];
+ while (ci->mac_version) {
+ if ((v & ci->id_mask) == ci->id_val)
+ break;
+ ci++;
+ }
+ board_type = ci->mac_version;
+ if (!board_type) {
+ fprintf(stderr, "Unknown RealTek chip (TxConfig: 0x%08x)\n", v);
+ return 91;
+ }
+
+ fprintf(stdout,
+ "RealTek RTL%s registers:\n"
+ "--------------------------------------------------------\n",
+ chip_names[board_type]);
+
+ fprintf(stdout,
+ "0x00: MAC Address %02x:%02x:%02x:%02x:%02x:%02x\n",
+ data8[0x00],
+ data8[0x01],
+ data8[0x02],
+ data8[0x03],
+ data8[0x04],
+ data8[0x05]);
+
+ fprintf(stdout,
+ "0x08: Multicast Address Filter 0x%08x 0x%08x\n",
+ data[0x08 >> 2],
+ data[0x0c >> 2]);
+
+ if (board_type == RTL8139Cp || board_type >= RTL_GIGA_MAC_VER_01) {
+ fprintf(stdout,
+ "0x10: Dump Tally Counter Command 0x%08x 0x%08x\n",
+ data[0x10 >> 2],
+ data[0x14 >> 2]);
+
+ fprintf(stdout,
+ "0x20: Tx Normal Priority Ring Addr 0x%08x 0x%08x\n",
+ data[0x20 >> 2],
+ data[0x24 >> 2]);
+
+ fprintf(stdout,
+ "0x28: Tx High Priority Ring Addr 0x%08x 0x%08x\n",
+ data[0x28 >> 2],
+ data[0x2C >> 2]);
+ } else {
+ fprintf(stdout,
+ "0x10: Transmit Status Desc 0 0x%08x\n"
+ "0x14: Transmit Status Desc 1 0x%08x\n"
+ "0x18: Transmit Status Desc 2 0x%08x\n"
+ "0x1C: Transmit Status Desc 3 0x%08x\n",
+ data[0x10 >> 2],
+ data[0x14 >> 2],
+ data[0x18 >> 2],
+ data[0x1C >> 2]);
+ fprintf(stdout,
+ "0x20: Transmit Start Addr 0 0x%08x\n"
+ "0x24: Transmit Start Addr 1 0x%08x\n"
+ "0x28: Transmit Start Addr 2 0x%08x\n"
+ "0x2C: Transmit Start Addr 3 0x%08x\n",
+ data[0x20 >> 2],
+ data[0x24 >> 2],
+ data[0x28 >> 2],
+ data[0x2C >> 2]);
+ }
+
+ if (board_type < RTL_GIGA_MAC_VER_11 ||
+ board_type > RTL_GIGA_MAC_VER_17) {
+ if (board_type >= RTL_GIGA_MAC_VER_01) {
+ fprintf(stdout,
+ "0x30: Flash memory read/write 0x%08x\n",
+ data[0x30 >> 2]);
+ } else {
+ fprintf(stdout,
+ "0x30: Rx buffer addr (C mode) 0x%08x\n",
+ data[0x30 >> 2]);
+ }
+ }
+
+ v = data8[0x36];
+ fprintf(stdout,
+ "0x34: Early Rx Byte Count %8u\n"
+ "0x36: Early Rx Status 0x%02x\n",
+ data[0x34 >> 2] & 0xffff,
+ v);
+
+ if (v & 0xf) {
+ fprintf(stdout,
+ " %s%s%s%s\n",
+ v & (1 << 3) ? "ERxGood " : "",
+ v & (1 << 2) ? "ERxBad " : "",
+ v & (1 << 1) ? "ERxOverWrite " : "",
+ v & (1 << 0) ? "ERxOK " : "");
+ }
+
+ v = data8[0x37];
+ fprintf(stdout,
+ "0x37: Command 0x%02x\n"
+ " Rx %s, Tx %s%s\n",
+ data8[0x37],
+ v & (1 << 3) ? "on" : "off",
+ v & (1 << 2) ? "on" : "off",
+ v & (1 << 4) ? ", RESET" : "");
+
+ if (board_type < RTL_GIGA_MAC_VER_01) {
+ fprintf(stdout,
+ "0x38: Current Address of Packet Read (C mode) 0x%04x\n"
+ "0x3A: Current Rx buffer address (C mode) 0x%04x\n",
+ data[0x38 >> 2] & 0xffff,
+ data[0x38 >> 2] >> 16);
+ }
+
+ fprintf(stdout,
+ "0x3C: Interrupt Mask 0x%04x\n",
+ data[0x3c >> 2] & 0xffff);
+ print_intr_bits(data[0x3c >> 2] & 0xffff);
+ fprintf(stdout,
+ "0x3E: Interrupt Status 0x%04x\n",
+ data[0x3c >> 2] >> 16);
+ print_intr_bits(data[0x3c >> 2] >> 16);
+
+ fprintf(stdout,
+ "0x40: Tx Configuration 0x%08x\n"
+ "0x44: Rx Configuration 0x%08x\n"
+ "0x48: Timer count 0x%08x\n"
+ "0x4C: Missed packet counter 0x%06x\n",
+ data[0x40 >> 2],
+ data[0x44 >> 2],
+ data[0x48 >> 2],
+ data[0x4C >> 2] & 0xffffff);
+
+ fprintf(stdout,
+ "0x50: EEPROM Command 0x%02x\n"
+ "0x51: Config 0 0x%02x\n"
+ "0x52: Config 1 0x%02x\n",
+ data8[0x50],
+ data8[0x51],
+ data8[0x52]);
+
+ if (board_type >= RTL_GIGA_MAC_VER_01) {
+ fprintf(stdout,
+ "0x53: Config 2 0x%02x\n"
+ "0x54: Config 3 0x%02x\n"
+ "0x55: Config 4 0x%02x\n"
+ "0x56: Config 5 0x%02x\n",
+ data8[0x53],
+ data8[0x54],
+ data8[0x55],
+ data8[0x56]);
+ fprintf(stdout,
+ "0x58: Timer interrupt 0x%08x\n",
+ data[0x58 >> 2]);
+ }
+ else {
+ if (board_type >= RTL8139A) {
+ fprintf(stdout,
+ "0x54: Timer interrupt 0x%08x\n",
+ data[0x54 >> 2]);
+ }
+ fprintf(stdout,
+ "0x58: Media status 0x%02x\n",
+ data8[0x58]);
+ if (board_type >= RTL8139A) {
+ fprintf(stdout,
+ "0x59: Config 3 0x%02x\n",
+ data8[0x59]);
+ }
+ if (board_type >= RTL8139B) {
+ fprintf(stdout,
+ "0x5A: Config 4 0x%02x\n",
+ data8[0x5A]);
+ }
+ }
+
+ fprintf(stdout,
+ "0x5C: Multiple Interrupt Select 0x%04x\n",
+ data[0x5c >> 2] & 0xffff);
+
+ if (board_type >= RTL_GIGA_MAC_VER_01) {
+ fprintf(stdout,
+ "0x60: PHY access 0x%08x\n",
+ data[0x60 >> 2]);
+
+ if (board_type < RTL_GIGA_MAC_VER_11 ||
+ board_type > RTL_GIGA_MAC_VER_17) {
+ fprintf(stdout,
+ "0x64: TBI control and status 0x%08x\n",
+ data[0x64 >> 2]);
+ fprintf(stdout,
+ "0x68: TBI Autonegotiation advertisement (ANAR) 0x%04x\n"
+ "0x6A: TBI Link partner ability (LPAR) 0x%04x\n",
+ data[0x68 >> 2] & 0xffff,
+ data[0x68 >> 2] >> 16);
+ }
+
+ fprintf(stdout,
+ "0x6C: PHY status 0x%02x\n",
+ data8[0x6C]);
+
+ fprintf(stdout,
+ "0x84: PM wakeup frame 0 0x%08x 0x%08x\n"
+ "0x8C: PM wakeup frame 1 0x%08x 0x%08x\n",
+ data[0x84 >> 2],
+ data[0x88 >> 2],
+ data[0x8C >> 2],
+ data[0x90 >> 2]);
+
+ fprintf(stdout,
+ "0x94: PM wakeup frame 2 (low) 0x%08x 0x%08x\n"
+ "0x9C: PM wakeup frame 2 (high) 0x%08x 0x%08x\n",
+ data[0x94 >> 2],
+ data[0x98 >> 2],
+ data[0x9C >> 2],
+ data[0xA0 >> 2]);
+
+ fprintf(stdout,
+ "0xA4: PM wakeup frame 3 (low) 0x%08x 0x%08x\n"
+ "0xAC: PM wakeup frame 3 (high) 0x%08x 0x%08x\n",
+ data[0xA4 >> 2],
+ data[0xA8 >> 2],
+ data[0xAC >> 2],
+ data[0xB0 >> 2]);
+
+ fprintf(stdout,
+ "0xB4: PM wakeup frame 4 (low) 0x%08x 0x%08x\n"
+ "0xBC: PM wakeup frame 4 (high) 0x%08x 0x%08x\n",
+ data[0xB4 >> 2],
+ data[0xB8 >> 2],
+ data[0xBC >> 2],
+ data[0xC0 >> 2]);
+
+ fprintf(stdout,
+ "0xC4: Wakeup frame 0 CRC 0x%04x\n"
+ "0xC6: Wakeup frame 1 CRC 0x%04x\n"
+ "0xC8: Wakeup frame 2 CRC 0x%04x\n"
+ "0xCA: Wakeup frame 3 CRC 0x%04x\n"
+ "0xCC: Wakeup frame 4 CRC 0x%04x\n",
+ data[0xC4 >> 2] & 0xffff,
+ data[0xC4 >> 2] >> 16,
+ data[0xC8 >> 2] & 0xffff,
+ data[0xC8 >> 2] >> 16,
+ data[0xCC >> 2] & 0xffff);
+ fprintf(stdout,
+ "0xDA: RX packet maximum size 0x%04x\n",
+ data[0xD8 >> 2] >> 16);
+ }
+ else {
+ fprintf(stdout,
+ "0x5E: PCI revision id 0x%02x\n",
+ data8[0x5e]);
+ fprintf(stdout,
+ "0x60: Transmit Status of All Desc (C mode) 0x%04x\n"
+ "0x62: MII Basic Mode Control Register 0x%04x\n",
+ data[0x60 >> 2] & 0xffff,
+ data[0x60 >> 2] >> 16);
+ fprintf(stdout,
+ "0x64: MII Basic Mode Status Register 0x%04x\n"
+ "0x66: MII Autonegotiation Advertising 0x%04x\n",
+ data[0x64 >> 2] & 0xffff,
+ data[0x64 >> 2] >> 16);
+ fprintf(stdout,
+ "0x68: MII Link Partner Ability 0x%04x\n"
+ "0x6A: MII Expansion 0x%04x\n",
+ data[0x68 >> 2] & 0xffff,
+ data[0x68 >> 2] >> 16);
+ fprintf(stdout,
+ "0x6C: MII Disconnect counter 0x%04x\n"
+ "0x6E: MII False carrier sense counter 0x%04x\n",
+ data[0x6C >> 2] & 0xffff,
+ data[0x6C >> 2] >> 16);
+ fprintf(stdout,
+ "0x70: MII Nway test 0x%04x\n"
+ "0x72: MII RX_ER counter 0x%04x\n",
+ data[0x70 >> 2] & 0xffff,
+ data[0x70 >> 2] >> 16);
+ fprintf(stdout,
+ "0x74: MII CS configuration 0x%04x\n",
+ data[0x74 >> 2] & 0xffff);
+ if (board_type >= RTL8139_K) {
+ fprintf(stdout,
+ "0x78: PHY parameter 1 0x%08x\n"
+ "0x7C: Twister parameter 0x%08x\n",
+ data[0x78 >> 2],
+ data[0x7C >> 2]);
+ if (board_type >= RTL8139A) {
+ fprintf(stdout,
+ "0x80: PHY parameter 2 0x%02x\n",
+ data8[0x80]);
+ }
+ }
+ if (board_type == RTL8139Cp) {
+ fprintf(stdout,
+ "0x82: Low addr of a Tx Desc w/ Tx DMA OK 0x%04x\n",
+ data[0x80 >> 2] >> 16);
+ } else if (board_type == RTL8130) {
+ fprintf(stdout,
+ "0x82: MII register 0x%02x\n",
+ data8[0x82]);
+ }
+ if (board_type >= RTL8139A) {
+ fprintf(stdout,
+ "0x84: PM CRC for wakeup frame 0 0x%02x\n"
+ "0x85: PM CRC for wakeup frame 1 0x%02x\n"
+ "0x86: PM CRC for wakeup frame 2 0x%02x\n"
+ "0x87: PM CRC for wakeup frame 3 0x%02x\n"
+ "0x88: PM CRC for wakeup frame 4 0x%02x\n"
+ "0x89: PM CRC for wakeup frame 5 0x%02x\n"
+ "0x8A: PM CRC for wakeup frame 6 0x%02x\n"
+ "0x8B: PM CRC for wakeup frame 7 0x%02x\n",
+ data8[0x84],
+ data8[0x85],
+ data8[0x86],
+ data8[0x87],
+ data8[0x88],
+ data8[0x89],
+ data8[0x8A],
+ data8[0x8B]);
+ fprintf(stdout,
+ "0x8C: PM wakeup frame 0 0x%08x 0x%08x\n"
+ "0x94: PM wakeup frame 1 0x%08x 0x%08x\n"
+ "0x9C: PM wakeup frame 2 0x%08x 0x%08x\n"
+ "0xA4: PM wakeup frame 3 0x%08x 0x%08x\n"
+ "0xAC: PM wakeup frame 4 0x%08x 0x%08x\n"
+ "0xB4: PM wakeup frame 5 0x%08x 0x%08x\n"
+ "0xBC: PM wakeup frame 6 0x%08x 0x%08x\n"
+ "0xC4: PM wakeup frame 7 0x%08x 0x%08x\n",
+ data[0x8C >> 2],
+ data[0x90 >> 2],
+ data[0x94 >> 2],
+ data[0x98 >> 2],
+ data[0x9C >> 2],
+ data[0xA0 >> 2],
+ data[0xA4 >> 2],
+ data[0xA8 >> 2],
+ data[0xAC >> 2],
+ data[0xB0 >> 2],
+ data[0xB4 >> 2],
+ data[0xB8 >> 2],
+ data[0xBC >> 2],
+ data[0xC0 >> 2],
+ data[0xC4 >> 2],
+ data[0xC8 >> 2]);
+ fprintf(stdout,
+ "0xCC: PM LSB CRC for wakeup frame 0 0x%02x\n"
+ "0xCD: PM LSB CRC for wakeup frame 1 0x%02x\n"
+ "0xCE: PM LSB CRC for wakeup frame 2 0x%02x\n"
+ "0xCF: PM LSB CRC for wakeup frame 3 0x%02x\n"
+ "0xD0: PM LSB CRC for wakeup frame 4 0x%02x\n"
+ "0xD1: PM LSB CRC for wakeup frame 5 0x%02x\n"
+ "0xD2: PM LSB CRC for wakeup frame 6 0x%02x\n"
+ "0xD3: PM LSB CRC for wakeup frame 7 0x%02x\n",
+ data8[0xCC],
+ data8[0xCD],
+ data8[0xCE],
+ data8[0xCF],
+ data8[0xD0],
+ data8[0xD1],
+ data8[0xD2],
+ data8[0xD3]);
+ }
+ if (board_type >= RTL8139B) {
+ if (board_type != RTL8100 && board_type != RTL8100B_8139D &&
+ board_type != RTL8101)
+ fprintf(stdout,
+ "0xD4: Flash memory read/write 0x%08x\n",
+ data[0xD4 >> 2]);
+ if (board_type != RTL8130)
+ fprintf(stdout,
+ "0xD8: Config 5 0x%02x\n",
+ data8[0xD8]);
+ }
+ }
+
+ if (board_type == RTL8139Cp || board_type >= RTL_GIGA_MAC_VER_01) {
+ v = data[0xE0 >> 2] & 0xffff;
+ fprintf(stdout,
+ "0xE0: C+ Command 0x%04x\n",
+ v);
+ if (v & (1 << 9))
+ fprintf(stdout, " Big-endian mode\n");
+ if (v & (1 << 8))
+ fprintf(stdout, " Home LAN enable\n");
+ if (v & (1 << 6))
+ fprintf(stdout, " VLAN de-tagging\n");
+ if (v & (1 << 5))
+ fprintf(stdout, " RX checksumming\n");
+ if (v & (1 << 4))
+ fprintf(stdout, " PCI 64-bit DAC\n");
+ if (v & (1 << 3))
+ fprintf(stdout, " PCI Multiple RW\n");
+
+ v = data[0xe0 >> 2] >> 16;
+ fprintf(stdout,
+ "0xE2: Interrupt Mitigation 0x%04x\n"
+ " TxTimer: %u\n"
+ " TxPackets: %u\n"
+ " RxTimer: %u\n"
+ " RxPackets: %u\n",
+ v,
+ v >> 12,
+ (v >> 8) & 0xf,
+ (v >> 4) & 0xf,
+ v & 0xf);
+
+ fprintf(stdout,
+ "0xE4: Rx Ring Addr 0x%08x 0x%08x\n",
+ data[0xE4 >> 2],
+ data[0xE8 >> 2]);
+
+ fprintf(stdout,
+ "0xEC: Early Tx threshold 0x%02x\n",
+ data8[0xEC]);
+
+ if (board_type == RTL8139Cp) {
+ fprintf(stdout,
+ "0xFC: External MII register 0x%08x\n",
+ data[0xFC >> 2]);
+ } else if (board_type >= RTL_GIGA_MAC_VER_01 &&
+ (board_type < RTL_GIGA_MAC_VER_11 ||
+ board_type > RTL_GIGA_MAC_VER_17)) {
+ fprintf(stdout,
+ "0xF0: Func Event 0x%08x\n"
+ "0xF4: Func Event Mask 0x%08x\n"
+ "0xF8: Func Preset State 0x%08x\n"
+ "0xFC: Func Force Event 0x%08x\n",
+ data[0xF0 >> 2],
+ data[0xF4 >> 2],
+ data[0xF8 >> 2],
+ data[0xFC >> 2]);
+ }
+ }
+
+ return 0;
+}
diff --git a/rxclass.c b/rxclass.c
new file mode 100644
index 0000000..f17e3a5
--- /dev/null
+++ b/rxclass.c
@@ -0,0 +1,1459 @@
+/*
+ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/sockios.h>
+#include <arpa/inet.h>
+#include "internal.h"
+
+static void invert_flow_mask(struct ethtool_rx_flow_spec *fsp)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(fsp->m_u); i++)
+ fsp->m_u.hdata[i] ^= 0xFF;
+}
+
+static void rxclass_print_ipv4_rule(__be32 sip, __be32 sipm, __be32 dip,
+ __be32 dipm, u8 tos, u8 tosm)
+{
+ char sip_str[INET_ADDRSTRLEN];
+ char sipm_str[INET_ADDRSTRLEN];
+ char dip_str[INET_ADDRSTRLEN];
+ char dipm_str[INET_ADDRSTRLEN];
+
+ fprintf(stdout,
+ "\tSrc IP addr: %s mask: %s\n"
+ "\tDest IP addr: %s mask: %s\n"
+ "\tTOS: 0x%x mask: 0x%x\n",
+ inet_ntop(AF_INET, &sip, sip_str, INET_ADDRSTRLEN),
+ inet_ntop(AF_INET, &sipm, sipm_str, INET_ADDRSTRLEN),
+ inet_ntop(AF_INET, &dip, dip_str, INET_ADDRSTRLEN),
+ inet_ntop(AF_INET, &dipm, dipm_str, INET_ADDRSTRLEN),
+ tos, tosm);
+}
+
+static void rxclass_print_ipv6_rule(__be32 *sip, __be32 *sipm, __be32 *dip,
+ __be32 *dipm, u8 tclass, u8 tclassm)
+{
+ char sip_str[INET6_ADDRSTRLEN];
+ char sipm_str[INET6_ADDRSTRLEN];
+ char dip_str[INET6_ADDRSTRLEN];
+ char dipm_str[INET6_ADDRSTRLEN];
+
+ fprintf(stdout,
+ "\tSrc IP addr: %s mask: %s\n"
+ "\tDest IP addr: %s mask: %s\n"
+ "\tTraffic Class: 0x%x mask: 0x%x\n",
+ inet_ntop(AF_INET6, sip, sip_str, INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, sipm, sipm_str, INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, dip, dip_str, INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, dipm, dipm_str, INET6_ADDRSTRLEN),
+ tclass, tclassm);
+}
+
+static void rxclass_print_nfc_spec_ext(struct ethtool_rx_flow_spec *fsp)
+{
+ if (fsp->flow_type & FLOW_EXT) {
+ u64 data, datam;
+ __u16 etype, etypem, tci, tcim;
+ etype = ntohs(fsp->h_ext.vlan_etype);
+ etypem = ntohs(~fsp->m_ext.vlan_etype);
+ tci = ntohs(fsp->h_ext.vlan_tci);
+ tcim = ntohs(~fsp->m_ext.vlan_tci);
+ data = (u64)ntohl(fsp->h_ext.data[0]) << 32;
+ data |= (u64)ntohl(fsp->h_ext.data[1]);
+ datam = (u64)ntohl(~fsp->m_ext.data[0]) << 32;
+ datam |= (u64)ntohl(~fsp->m_ext.data[1]);
+
+ fprintf(stdout,
+ "\tVLAN EtherType: 0x%x mask: 0x%x\n"
+ "\tVLAN: 0x%x mask: 0x%x\n"
+ "\tUser-defined: 0x%llx mask: 0x%llx\n",
+ etype, etypem, tci, tcim, data, datam);
+ }
+
+ if (fsp->flow_type & FLOW_MAC_EXT) {
+ unsigned char *dmac, *dmacm;
+
+ dmac = fsp->h_ext.h_dest;
+ dmacm = fsp->m_ext.h_dest;
+
+ fprintf(stdout,
+ "\tDest MAC addr: %02X:%02X:%02X:%02X:%02X:%02X"
+ " mask: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ dmac[0], dmac[1], dmac[2], dmac[3], dmac[4],
+ dmac[5], dmacm[0], dmacm[1], dmacm[2], dmacm[3],
+ dmacm[4], dmacm[5]);
+ }
+}
+
+static void rxclass_print_nfc_rule(struct ethtool_rx_flow_spec *fsp,
+ __u32 rss_context)
+{
+ unsigned char *smac, *smacm, *dmac, *dmacm;
+ __u32 flow_type;
+
+ fprintf(stdout, "Filter: %d\n", fsp->location);
+
+ flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
+
+ invert_flow_mask(fsp);
+
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ if (flow_type == TCP_V4_FLOW)
+ fprintf(stdout, "\tRule Type: TCP over IPv4\n");
+ else if (flow_type == UDP_V4_FLOW)
+ fprintf(stdout, "\tRule Type: UDP over IPv4\n");
+ else
+ fprintf(stdout, "\tRule Type: SCTP over IPv4\n");
+ rxclass_print_ipv4_rule(fsp->h_u.tcp_ip4_spec.ip4src,
+ fsp->m_u.tcp_ip4_spec.ip4src,
+ fsp->h_u.tcp_ip4_spec.ip4dst,
+ fsp->m_u.tcp_ip4_spec.ip4dst,
+ fsp->h_u.tcp_ip4_spec.tos,
+ fsp->m_u.tcp_ip4_spec.tos);
+ fprintf(stdout,
+ "\tSrc port: %d mask: 0x%x\n"
+ "\tDest port: %d mask: 0x%x\n",
+ ntohs(fsp->h_u.tcp_ip4_spec.psrc),
+ ntohs(fsp->m_u.tcp_ip4_spec.psrc),
+ ntohs(fsp->h_u.tcp_ip4_spec.pdst),
+ ntohs(fsp->m_u.tcp_ip4_spec.pdst));
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ if (flow_type == AH_V4_FLOW)
+ fprintf(stdout, "\tRule Type: IPSEC AH over IPv4\n");
+ else
+ fprintf(stdout, "\tRule Type: IPSEC ESP over IPv4\n");
+ rxclass_print_ipv4_rule(fsp->h_u.ah_ip4_spec.ip4src,
+ fsp->m_u.ah_ip4_spec.ip4src,
+ fsp->h_u.ah_ip4_spec.ip4dst,
+ fsp->m_u.ah_ip4_spec.ip4dst,
+ fsp->h_u.ah_ip4_spec.tos,
+ fsp->m_u.ah_ip4_spec.tos);
+ fprintf(stdout,
+ "\tSPI: %d mask: 0x%x\n",
+ ntohl(fsp->h_u.esp_ip4_spec.spi),
+ ntohl(fsp->m_u.esp_ip4_spec.spi));
+ break;
+ case IPV4_USER_FLOW:
+ fprintf(stdout, "\tRule Type: Raw IPv4\n");
+ rxclass_print_ipv4_rule(fsp->h_u.usr_ip4_spec.ip4src,
+ fsp->m_u.usr_ip4_spec.ip4src,
+ fsp->h_u.usr_ip4_spec.ip4dst,
+ fsp->m_u.usr_ip4_spec.ip4dst,
+ fsp->h_u.usr_ip4_spec.tos,
+ fsp->m_u.usr_ip4_spec.tos);
+ fprintf(stdout,
+ "\tProtocol: %d mask: 0x%x\n"
+ "\tL4 bytes: 0x%x mask: 0x%x\n",
+ fsp->h_u.usr_ip4_spec.proto,
+ fsp->m_u.usr_ip4_spec.proto,
+ ntohl(fsp->h_u.usr_ip4_spec.l4_4_bytes),
+ ntohl(fsp->m_u.usr_ip4_spec.l4_4_bytes));
+ break;
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ case SCTP_V6_FLOW:
+ if (flow_type == TCP_V6_FLOW)
+ fprintf(stdout, "\tRule Type: TCP over IPv6\n");
+ else if (flow_type == UDP_V6_FLOW)
+ fprintf(stdout, "\tRule Type: UDP over IPv6\n");
+ else
+ fprintf(stdout, "\tRule Type: SCTP over IPv6\n");
+ rxclass_print_ipv6_rule(fsp->h_u.tcp_ip6_spec.ip6src,
+ fsp->m_u.tcp_ip6_spec.ip6src,
+ fsp->h_u.tcp_ip6_spec.ip6dst,
+ fsp->m_u.tcp_ip6_spec.ip6dst,
+ fsp->h_u.tcp_ip6_spec.tclass,
+ fsp->m_u.tcp_ip6_spec.tclass);
+ fprintf(stdout,
+ "\tSrc port: %d mask: 0x%x\n"
+ "\tDest port: %d mask: 0x%x\n",
+ ntohs(fsp->h_u.tcp_ip6_spec.psrc),
+ ntohs(fsp->m_u.tcp_ip6_spec.psrc),
+ ntohs(fsp->h_u.tcp_ip6_spec.pdst),
+ ntohs(fsp->m_u.tcp_ip6_spec.pdst));
+ break;
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ if (flow_type == AH_V6_FLOW)
+ fprintf(stdout, "\tRule Type: IPSEC AH over IPv6\n");
+ else
+ fprintf(stdout, "\tRule Type: IPSEC ESP over IPv6\n");
+ rxclass_print_ipv6_rule(fsp->h_u.ah_ip6_spec.ip6src,
+ fsp->m_u.ah_ip6_spec.ip6src,
+ fsp->h_u.ah_ip6_spec.ip6dst,
+ fsp->m_u.ah_ip6_spec.ip6dst,
+ fsp->h_u.ah_ip6_spec.tclass,
+ fsp->m_u.ah_ip6_spec.tclass);
+ fprintf(stdout,
+ "\tSPI: %d mask: 0x%x\n",
+ ntohl(fsp->h_u.esp_ip6_spec.spi),
+ ntohl(fsp->m_u.esp_ip6_spec.spi));
+ break;
+ case IPV6_USER_FLOW:
+ fprintf(stdout, "\tRule Type: Raw IPv6\n");
+ rxclass_print_ipv6_rule(fsp->h_u.usr_ip6_spec.ip6src,
+ fsp->m_u.usr_ip6_spec.ip6src,
+ fsp->h_u.usr_ip6_spec.ip6dst,
+ fsp->m_u.usr_ip6_spec.ip6dst,
+ fsp->h_u.usr_ip6_spec.tclass,
+ fsp->m_u.usr_ip6_spec.tclass);
+ fprintf(stdout,
+ "\tProtocol: %d mask: 0x%x\n"
+ "\tL4 bytes: 0x%x mask: 0x%x\n",
+ fsp->h_u.usr_ip6_spec.l4_proto,
+ fsp->m_u.usr_ip6_spec.l4_proto,
+ ntohl(fsp->h_u.usr_ip6_spec.l4_4_bytes),
+ ntohl(fsp->m_u.usr_ip6_spec.l4_4_bytes));
+ break;
+ case ETHER_FLOW:
+ dmac = fsp->h_u.ether_spec.h_dest;
+ dmacm = fsp->m_u.ether_spec.h_dest;
+ smac = fsp->h_u.ether_spec.h_source;
+ smacm = fsp->m_u.ether_spec.h_source;
+
+ fprintf(stdout,
+ "\tFlow Type: Raw Ethernet\n"
+ "\tSrc MAC addr: %02X:%02X:%02X:%02X:%02X:%02X"
+ " mask: %02X:%02X:%02X:%02X:%02X:%02X\n"
+ "\tDest MAC addr: %02X:%02X:%02X:%02X:%02X:%02X"
+ " mask: %02X:%02X:%02X:%02X:%02X:%02X\n"
+ "\tEthertype: 0x%X mask: 0x%X\n",
+ smac[0], smac[1], smac[2], smac[3], smac[4], smac[5],
+ smacm[0], smacm[1], smacm[2], smacm[3], smacm[4],
+ smacm[5], dmac[0], dmac[1], dmac[2], dmac[3], dmac[4],
+ dmac[5], dmacm[0], dmacm[1], dmacm[2], dmacm[3],
+ dmacm[4], dmacm[5],
+ ntohs(fsp->h_u.ether_spec.h_proto),
+ ntohs(fsp->m_u.ether_spec.h_proto));
+ break;
+ default:
+ fprintf(stdout,
+ "\tUnknown Flow type: %d\n", flow_type);
+ break;
+ }
+
+ rxclass_print_nfc_spec_ext(fsp);
+
+ if (fsp->flow_type & FLOW_RSS)
+ fprintf(stdout, "\tRSS Context ID: %u\n", rss_context);
+
+ if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
+ fprintf(stdout, "\tAction: Drop\n");
+ } else if (fsp->ring_cookie == RX_CLS_FLOW_WAKE) {
+ fprintf(stdout, "\tAction: Wake-on-LAN\n");
+ } else {
+ u64 vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie);
+ u64 queue = ethtool_get_flow_spec_ring(fsp->ring_cookie);
+
+ /* A value of zero indicates that this rule targeted the main
+ * function. A positive value indicates which virtual function
+ * was targeted, so we'll subtract 1 in order to show the
+ * correct VF index
+ */
+ if (vf)
+ fprintf(stdout, "\tAction: Direct to VF %llu queue %llu\n",
+ vf - 1, queue);
+ else
+ fprintf(stdout, "\tAction: Direct to queue %llu\n",
+ queue);
+ }
+
+ fprintf(stdout, "\n");
+}
+
+static void rxclass_print_rule(struct ethtool_rx_flow_spec *fsp,
+ __u32 rss_context)
+{
+ /* print the rule in this location */
+ switch (fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ case SCTP_V6_FLOW:
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ case IPV6_USER_FLOW:
+ case ETHER_FLOW:
+ rxclass_print_nfc_rule(fsp, rss_context);
+ break;
+ case IPV4_USER_FLOW:
+ if (fsp->h_u.usr_ip4_spec.ip_ver == ETH_RX_NFC_IP4)
+ rxclass_print_nfc_rule(fsp, rss_context);
+ else /* IPv6 uses IPV6_USER_FLOW */
+ fprintf(stderr, "IPV4_USER_FLOW with wrong ip_ver\n");
+ break;
+ default:
+ fprintf(stderr, "rxclass: Unknown flow type\n");
+ break;
+ }
+}
+
+static int rxclass_get_dev_info(struct cmd_context *ctx, __u32 *count,
+ int *driver_select)
+{
+ struct ethtool_rxnfc nfccmd;
+ int err;
+
+ nfccmd.cmd = ETHTOOL_GRXCLSRLCNT;
+ nfccmd.data = 0;
+ err = send_ioctl(ctx, &nfccmd);
+ *count = nfccmd.rule_cnt;
+ if (driver_select)
+ *driver_select = !!(nfccmd.data & RX_CLS_LOC_SPECIAL);
+ if (err < 0)
+ perror("rxclass: Cannot get RX class rule count");
+
+ return err;
+}
+
+int rxclass_rule_get(struct cmd_context *ctx, __u32 loc)
+{
+ struct ethtool_rxnfc nfccmd;
+ int err;
+
+ /* fetch rule from netdev */
+ nfccmd.cmd = ETHTOOL_GRXCLSRULE;
+ memset(&nfccmd.fs, 0, sizeof(struct ethtool_rx_flow_spec));
+ nfccmd.fs.location = loc;
+ err = send_ioctl(ctx, &nfccmd);
+ if (err < 0) {
+ perror("rxclass: Cannot get RX class rule");
+ return err;
+ }
+
+ /* display rule */
+ rxclass_print_rule(&nfccmd.fs, (__u32)nfccmd.rss_context);
+ return err;
+}
+
+int rxclass_rule_getall(struct cmd_context *ctx)
+{
+ struct ethtool_rxnfc *nfccmd;
+ __u32 *rule_locs;
+ unsigned int i;
+ __u32 count;
+ int err;
+
+ /* determine rule count */
+ err = rxclass_get_dev_info(ctx, &count, NULL);
+ if (err < 0)
+ return err;
+
+ fprintf(stdout, "Total %d rules\n\n", count);
+
+ /* alloc memory for request of location list */
+ nfccmd = calloc(1, sizeof(*nfccmd) + (count * sizeof(__u32)));
+ if (!nfccmd) {
+ perror("rxclass: Cannot allocate memory for"
+ " RX class rule locations");
+ return -ENOMEM;
+ }
+
+ /* request location list */
+ nfccmd->cmd = ETHTOOL_GRXCLSRLALL;
+ nfccmd->rule_cnt = count;
+ err = send_ioctl(ctx, nfccmd);
+ if (err < 0) {
+ perror("rxclass: Cannot get RX class rules");
+ free(nfccmd);
+ return err;
+ }
+
+ /* write locations to bitmap */
+ rule_locs = nfccmd->rule_locs;
+ for (i = 0; i < count; i++) {
+ err = rxclass_rule_get(ctx, rule_locs[i]);
+ if (err < 0)
+ break;
+ }
+
+ /* free memory and set flag to avoid reinit */
+ free(nfccmd);
+
+ return err;
+}
+
+/*
+ * This is a simple rule manager implementation for ordering rx flow
+ * classification rules based on newest rules being first in the list.
+ * The assumption is that this rule manager is the only one adding rules to
+ * the device's hardware classifier.
+ */
+
+struct rmgr_ctrl {
+ /* flag for device/driver that can select locations itself */
+ int driver_select;
+ /* slot contains a bitmap indicating which filters are valid */
+ unsigned long *slot;
+ __u32 n_rules;
+ __u32 size;
+};
+
+static int rmgr_ins(struct rmgr_ctrl *rmgr, __u32 loc)
+{
+ /* verify location is in rule manager range */
+ if (loc >= rmgr->size) {
+ fprintf(stderr, "rmgr: Location out of range\n");
+ return -1;
+ }
+
+ /* set bit for the rule */
+ set_bit(loc, rmgr->slot);
+
+ return 0;
+}
+
+static int rmgr_find_empty_slot(struct rmgr_ctrl *rmgr,
+ struct ethtool_rx_flow_spec *fsp)
+{
+ __u32 loc;
+ __u32 slot_num;
+
+ /* leave this to the driver if possible */
+ if (rmgr->driver_select)
+ return 0;
+
+ /* start at the end of the list since it is lowest priority */
+ loc = rmgr->size - 1;
+
+ /* locate the first slot a rule can be placed in */
+ slot_num = loc / BITS_PER_LONG;
+
+ /*
+ * Avoid testing individual bits by inverting the word and checking
+ * to see if any bits are left set, if so there are empty spots. By
+ * moving 1 + loc % BITS_PER_LONG we align ourselves to the last bit
+ * in the previous word.
+ *
+ * If loc rolls over it should be greater than or equal to rmgr->size
+ * and as such we know we have reached the end of the list.
+ */
+ if (!~(rmgr->slot[slot_num] | (~1UL << loc % BITS_PER_LONG))) {
+ loc -= 1 + (loc % BITS_PER_LONG);
+ slot_num--;
+ }
+
+ /*
+ * Now that we are aligned with the last bit in each long we can just
+ * go though and eliminate all the longs with no free bits
+ */
+ while (loc < rmgr->size && !~(rmgr->slot[slot_num])) {
+ loc -= BITS_PER_LONG;
+ slot_num--;
+ }
+
+ /*
+ * If we still are inside the range, test individual bits as one is
+ * likely available for our use.
+ */
+ while (loc < rmgr->size && test_bit(loc, rmgr->slot))
+ loc--;
+
+ /* location found, insert rule */
+ if (loc < rmgr->size) {
+ fsp->location = loc;
+ return rmgr_ins(rmgr, loc);
+ }
+
+ /* No space to add this rule */
+ fprintf(stderr, "rmgr: Cannot find appropriate slot to insert rule\n");
+
+ return -1;
+}
+
+static int rmgr_init(struct cmd_context *ctx, struct rmgr_ctrl *rmgr)
+{
+ struct ethtool_rxnfc *nfccmd;
+ __u32 *rule_locs;
+ unsigned int i;
+ int err;
+
+ /* clear rule manager settings */
+ memset(rmgr, 0, sizeof(*rmgr));
+
+ /* request device/driver information */
+ err = rxclass_get_dev_info(ctx, &rmgr->n_rules, &rmgr->driver_select);
+ if (err < 0)
+ return err;
+
+ /* do not get the table if the device/driver can select locations */
+ if (rmgr->driver_select)
+ return 0;
+
+ /* alloc memory for request of location list */
+ nfccmd = calloc(1, sizeof(*nfccmd) + (rmgr->n_rules * sizeof(__u32)));
+ if (!nfccmd) {
+ perror("rmgr: Cannot allocate memory for"
+ " RX class rule locations");
+ return -1;
+ }
+
+ /* request location list */
+ nfccmd->cmd = ETHTOOL_GRXCLSRLALL;
+ nfccmd->rule_cnt = rmgr->n_rules;
+ err = send_ioctl(ctx, nfccmd);
+ if (err < 0) {
+ perror("rmgr: Cannot get RX class rules");
+ free(nfccmd);
+ return err;
+ }
+
+ /* make certain the table size is valid */
+ rmgr->size = nfccmd->data;
+ if (rmgr->size == 0 || rmgr->size < rmgr->n_rules) {
+ perror("rmgr: Invalid RX class rules table size");
+ return -1;
+ }
+
+ /* initialize bitmap for storage of valid locations */
+ rmgr->slot = calloc(1, BITS_TO_LONGS(rmgr->size) * sizeof(long));
+ if (!rmgr->slot) {
+ perror("rmgr: Cannot allocate memory for RX class rules");
+ return -1;
+ }
+
+ /* write locations to bitmap */
+ rule_locs = nfccmd->rule_locs;
+ for (i = 0; i < rmgr->n_rules; i++) {
+ err = rmgr_ins(rmgr, rule_locs[i]);
+ if (err < 0)
+ break;
+ }
+
+ free(nfccmd);
+
+ return err;
+}
+
+static void rmgr_cleanup(struct rmgr_ctrl *rmgr)
+{
+ free(rmgr->slot);
+ rmgr->slot = NULL;
+ rmgr->size = 0;
+}
+
+static int rmgr_set_location(struct cmd_context *ctx,
+ struct ethtool_rx_flow_spec *fsp)
+{
+ struct rmgr_ctrl rmgr;
+ int err;
+
+ /* init table of available rules */
+ err = rmgr_init(ctx, &rmgr);
+ if (err < 0)
+ goto out;
+
+ /* verify rule location */
+ err = rmgr_find_empty_slot(&rmgr, fsp);
+
+out:
+ /* cleanup table and free resources */
+ rmgr_cleanup(&rmgr);
+
+ return err;
+}
+
+int rxclass_rule_ins(struct cmd_context *ctx,
+ struct ethtool_rx_flow_spec *fsp, __u32 rss_context)
+{
+ struct ethtool_rxnfc nfccmd;
+ __u32 loc = fsp->location;
+ int err;
+
+ /*
+ * if location is unspecified and driver cannot select locations, pull
+ * rules from device and allocate a free rule for our use
+ */
+ if (loc & RX_CLS_LOC_SPECIAL) {
+ err = rmgr_set_location(ctx, fsp);
+ if (err < 0)
+ return err;
+ }
+
+ /* notify netdev of new rule */
+ nfccmd.cmd = ETHTOOL_SRXCLSRLINS;
+ nfccmd.rss_context = rss_context;
+ nfccmd.fs = *fsp;
+ err = send_ioctl(ctx, &nfccmd);
+ if (err < 0)
+ perror("rmgr: Cannot insert RX class rule");
+ else if (loc & RX_CLS_LOC_SPECIAL)
+ printf("Added rule with ID %d\n", nfccmd.fs.location);
+
+ return err;
+}
+
+int rxclass_rule_del(struct cmd_context *ctx, __u32 loc)
+{
+ struct ethtool_rxnfc nfccmd;
+ int err;
+
+ /* notify netdev of rule removal */
+ nfccmd.cmd = ETHTOOL_SRXCLSRLDEL;
+ nfccmd.fs.location = loc;
+ err = send_ioctl(ctx, &nfccmd);
+ if (err < 0)
+ perror("rmgr: Cannot delete RX class rule");
+
+ return err;
+}
+
+typedef enum {
+ OPT_NONE = 0,
+ OPT_S32,
+ OPT_U8,
+ OPT_U16,
+ OPT_U32,
+ OPT_U64,
+ OPT_RING_VF,
+ OPT_RING_QUEUE,
+ OPT_BE16,
+ OPT_BE32,
+ OPT_BE64,
+ OPT_IP4,
+ OPT_IP6,
+ OPT_MAC,
+} rule_opt_type_t;
+
+#define NFC_FLAG_RING 0x0001
+#define NFC_FLAG_LOC 0x0002
+#define NFC_FLAG_SADDR 0x0004
+#define NFC_FLAG_DADDR 0x0008
+#define NFC_FLAG_SPORT 0x0010
+#define NFC_FLAG_DPORT 0x0020
+#define NFC_FLAG_SPI 0x0030
+#define NFC_FLAG_TOS 0x0040
+#define NFC_FLAG_PROTO 0x0080
+#define NTUPLE_FLAG_VLAN 0x0100
+#define NTUPLE_FLAG_UDEF 0x0200
+#define NTUPLE_FLAG_VETH 0x0400
+#define NFC_FLAG_MAC_ADDR 0x0800
+#define NFC_FLAG_RING_VF 0x1000
+#define NFC_FLAG_RING_QUEUE 0x2000
+
+struct rule_opts {
+ const char *name;
+ rule_opt_type_t type;
+ u32 flag;
+ int offset;
+ int moffset;
+};
+
+static const struct rule_opts rule_nfc_tcp_ip4[] = {
+ { "src-ip", OPT_IP4, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.ip4src),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.ip4src) },
+ { "dst-ip", OPT_IP4, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.ip4dst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.ip4dst) },
+ { "tos", OPT_U8, NFC_FLAG_TOS,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.tos),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.tos) },
+ { "src-port", OPT_BE16, NFC_FLAG_SPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.psrc),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.psrc) },
+ { "dst-port", OPT_BE16, NFC_FLAG_DPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.pdst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.pdst) },
+ { "action", OPT_U64, NFC_FLAG_RING,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "vf", OPT_RING_VF, NFC_FLAG_RING_VF,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "queue", OPT_RING_QUEUE, NFC_FLAG_RING_QUEUE,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "loc", OPT_U32, NFC_FLAG_LOC,
+ offsetof(struct ethtool_rx_flow_spec, location), -1 },
+ { "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+ { "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+ { "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+ { "dst-mac", OPT_MAC, NFC_FLAG_MAC_ADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.h_dest),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
+};
+
+static const struct rule_opts rule_nfc_esp_ip4[] = {
+ { "src-ip", OPT_IP4, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip4_spec.ip4src),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip4_spec.ip4src) },
+ { "dst-ip", OPT_IP4, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip4_spec.ip4dst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip4_spec.ip4dst) },
+ { "tos", OPT_U8, NFC_FLAG_TOS,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip4_spec.tos),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip4_spec.tos) },
+ { "spi", OPT_BE32, NFC_FLAG_SPI,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip4_spec.spi),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip4_spec.spi) },
+ { "action", OPT_U64, NFC_FLAG_RING,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "vf", OPT_RING_VF, NFC_FLAG_RING_VF,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "queue", OPT_RING_QUEUE, NFC_FLAG_RING_QUEUE,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "loc", OPT_U32, NFC_FLAG_LOC,
+ offsetof(struct ethtool_rx_flow_spec, location), -1 },
+ { "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+ { "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+ { "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+ { "dst-mac", OPT_MAC, NFC_FLAG_MAC_ADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.h_dest),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
+};
+
+static const struct rule_opts rule_nfc_usr_ip4[] = {
+ { "src-ip", OPT_IP4, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.ip4src),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.ip4src) },
+ { "dst-ip", OPT_IP4, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.ip4dst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.ip4dst) },
+ { "tos", OPT_U8, NFC_FLAG_TOS,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.tos),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.tos) },
+ { "l4proto", OPT_U8, NFC_FLAG_PROTO,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.proto),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.proto) },
+ { "l4data", OPT_BE32, NFC_FLAG_SPI,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.l4_4_bytes),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.l4_4_bytes) },
+ { "spi", OPT_BE32, NFC_FLAG_SPI,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.l4_4_bytes),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.l4_4_bytes) },
+ { "src-port", OPT_BE16, NFC_FLAG_SPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.l4_4_bytes),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.l4_4_bytes) },
+ { "dst-port", OPT_BE16, NFC_FLAG_DPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.l4_4_bytes) + 2,
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.l4_4_bytes) + 2 },
+ { "action", OPT_U64, NFC_FLAG_RING,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "vf", OPT_RING_VF, NFC_FLAG_RING_VF,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "queue", OPT_RING_QUEUE, NFC_FLAG_RING_QUEUE,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "loc", OPT_U32, NFC_FLAG_LOC,
+ offsetof(struct ethtool_rx_flow_spec, location), -1 },
+ { "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+ { "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+ { "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+ { "dst-mac", OPT_MAC, NFC_FLAG_MAC_ADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.h_dest),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
+};
+
+static const struct rule_opts rule_nfc_tcp_ip6[] = {
+ { "src-ip", OPT_IP6, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.ip6src),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.ip6src) },
+ { "dst-ip", OPT_IP6, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.ip6dst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.ip6dst) },
+ { "tclass", OPT_U8, NFC_FLAG_TOS,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.tclass),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.tclass) },
+ { "src-port", OPT_BE16, NFC_FLAG_SPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.psrc),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.psrc) },
+ { "dst-port", OPT_BE16, NFC_FLAG_DPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.pdst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.pdst) },
+ { "action", OPT_U64, NFC_FLAG_RING,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "vf", OPT_RING_VF, NFC_FLAG_RING_VF,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "queue", OPT_RING_QUEUE, NFC_FLAG_RING_QUEUE,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "loc", OPT_U32, NFC_FLAG_LOC,
+ offsetof(struct ethtool_rx_flow_spec, location), -1 },
+ { "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+ { "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+ { "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+ { "dst-mac", OPT_MAC, NFC_FLAG_MAC_ADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.h_dest),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
+};
+
+static const struct rule_opts rule_nfc_esp_ip6[] = {
+ { "src-ip", OPT_IP6, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip6_spec.ip6src),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip6_spec.ip6src) },
+ { "dst-ip", OPT_IP6, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip6_spec.ip6dst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip6_spec.ip6dst) },
+ { "tclass", OPT_U8, NFC_FLAG_TOS,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip6_spec.tclass),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip6_spec.tclass) },
+ { "spi", OPT_BE32, NFC_FLAG_SPI,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip6_spec.spi),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip6_spec.spi) },
+ { "action", OPT_U64, NFC_FLAG_RING,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "vf", OPT_RING_VF, NFC_FLAG_RING_VF,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "queue", OPT_RING_QUEUE, NFC_FLAG_RING_QUEUE,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "loc", OPT_U32, NFC_FLAG_LOC,
+ offsetof(struct ethtool_rx_flow_spec, location), -1 },
+ { "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+ { "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+ { "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+ { "dst-mac", OPT_MAC, NFC_FLAG_MAC_ADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.h_dest),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
+};
+
+static const struct rule_opts rule_nfc_usr_ip6[] = {
+ { "src-ip", OPT_IP6, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.ip6src),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.ip6src) },
+ { "dst-ip", OPT_IP6, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.ip6dst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.ip6dst) },
+ { "tclass", OPT_U8, NFC_FLAG_TOS,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.tclass),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.tclass) },
+ { "l4proto", OPT_U8, NFC_FLAG_PROTO,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_proto),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_proto) },
+ { "l4data", OPT_BE32, NFC_FLAG_SPI,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_4_bytes),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_4_bytes) },
+ { "spi", OPT_BE32, NFC_FLAG_SPI,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_4_bytes),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_4_bytes) },
+ { "src-port", OPT_BE16, NFC_FLAG_SPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_4_bytes),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_4_bytes) },
+ { "dst-port", OPT_BE16, NFC_FLAG_DPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_4_bytes) + 2,
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_4_bytes) + 2 },
+ { "action", OPT_U64, NFC_FLAG_RING,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "vf", OPT_RING_VF, NFC_FLAG_RING_VF,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "queue", OPT_RING_QUEUE, NFC_FLAG_RING_QUEUE,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "loc", OPT_U32, NFC_FLAG_LOC,
+ offsetof(struct ethtool_rx_flow_spec, location), -1 },
+ { "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+ { "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+ { "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+ { "dst-mac", OPT_MAC, NFC_FLAG_MAC_ADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.h_dest),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
+};
+
+static const struct rule_opts rule_nfc_ether[] = {
+ { "src", OPT_MAC, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_source),
+ offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_source) },
+ { "dst", OPT_MAC, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_dest),
+ offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_dest) },
+ { "proto", OPT_BE16, NFC_FLAG_PROTO,
+ offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_proto),
+ offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_proto) },
+ { "action", OPT_U64, NFC_FLAG_RING,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "vf", OPT_RING_VF, NFC_FLAG_RING_VF,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "queue", OPT_RING_QUEUE, NFC_FLAG_RING_QUEUE,
+ offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+ { "loc", OPT_U32, NFC_FLAG_LOC,
+ offsetof(struct ethtool_rx_flow_spec, location), -1 },
+ { "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+ { "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+ { "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+ offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+ offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+};
+
+static int rxclass_get_long(char *str, long long *val, int size)
+{
+ long long max = ~0ULL >> (65 - size);
+ char *endp;
+
+ errno = 0;
+
+ *val = strtoll(str, &endp, 0);
+
+ if (*endp || errno || (*val > max) || (*val < ~max))
+ return -1;
+
+ return 0;
+}
+
+static int rxclass_get_ulong(char *str, unsigned long long *val, int size)
+{
+ unsigned long long max = ~0ULL >> (64 - size);
+ char *endp;
+
+ errno = 0;
+
+ *val = strtoull(str, &endp, 0);
+
+ if (*endp || errno || (*val > max))
+ return -1;
+
+ return 0;
+}
+
+static int rxclass_get_ipv4(char *str, __be32 *val)
+{
+ if (!inet_pton(AF_INET, str, val))
+ return -1;
+
+ return 0;
+}
+
+static int rxclass_get_ipv6(char *str, __be32 *val)
+{
+ if (!inet_pton(AF_INET6, str, val))
+ return -1;
+
+ return 0;
+}
+
+static int rxclass_get_ether(char *str, unsigned char *val)
+{
+ unsigned int buf[ETH_ALEN];
+ int count;
+
+ if (!strchr(str, ':'))
+ return -1;
+
+ count = sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x",
+ &buf[0], &buf[1], &buf[2],
+ &buf[3], &buf[4], &buf[5]);
+
+ if (count != ETH_ALEN)
+ return -1;
+
+ do {
+ count--;
+ val[count] = buf[count];
+ } while (count);
+
+ return 0;
+}
+
+static int rxclass_get_val(char *str, unsigned char *p, u32 *flags,
+ const struct rule_opts *opt)
+{
+ unsigned long long mask = ~0ULL;
+ int err = 0;
+
+ if (*flags & opt->flag)
+ return -1;
+
+ *flags |= opt->flag;
+
+ switch (opt->type) {
+ case OPT_S32: {
+ long long val;
+ err = rxclass_get_long(str, &val, 32);
+ if (err)
+ return -1;
+ *(int *)&p[opt->offset] = (int)val;
+ if (opt->moffset >= 0)
+ *(int *)&p[opt->moffset] = (int)mask;
+ break;
+ }
+ case OPT_U8: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 8);
+ if (err)
+ return -1;
+ *(u8 *)&p[opt->offset] = (u8)val;
+ if (opt->moffset >= 0)
+ *(u8 *)&p[opt->moffset] = (u8)mask;
+ break;
+ }
+ case OPT_U16: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 16);
+ if (err)
+ return -1;
+ *(u16 *)&p[opt->offset] = (u16)val;
+ if (opt->moffset >= 0)
+ *(u16 *)&p[opt->moffset] = (u16)mask;
+ break;
+ }
+ case OPT_U32: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 32);
+ if (err)
+ return -1;
+ *(u32 *)&p[opt->offset] = (u32)val;
+ if (opt->moffset >= 0)
+ *(u32 *)&p[opt->moffset] = (u32)mask;
+ break;
+ }
+ case OPT_U64: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 64);
+ if (err)
+ return -1;
+ *(u64 *)&p[opt->offset] = (u64)val;
+ if (opt->moffset >= 0)
+ *(u64 *)&p[opt->moffset] = (u64)mask;
+ break;
+ }
+ case OPT_RING_VF: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 8);
+ if (err)
+ return -1;
+
+ /* The ring_cookie uses 0 to indicate the rule targets the
+ * main function, so add 1 to the value in order to target the
+ * correct virtual function.
+ */
+ val++;
+
+ *(u64 *)&p[opt->offset] &= ~ETHTOOL_RX_FLOW_SPEC_RING_VF;
+ *(u64 *)&p[opt->offset] |= (u64)val << ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF;
+ break;
+ }
+ case OPT_RING_QUEUE: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 32);
+ if (err)
+ return -1;
+ *(u64 *)&p[opt->offset] &= ~ETHTOOL_RX_FLOW_SPEC_RING;
+ *(u64 *)&p[opt->offset] |= (u64)val;
+ break;
+ }
+ case OPT_BE16: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 16);
+ if (err)
+ return -1;
+ *(__be16 *)&p[opt->offset] = htons((u16)val);
+ if (opt->moffset >= 0)
+ *(__be16 *)&p[opt->moffset] = (__be16)mask;
+ break;
+ }
+ case OPT_BE32: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 32);
+ if (err)
+ return -1;
+ *(__be32 *)&p[opt->offset] = htonl((u32)val);
+ if (opt->moffset >= 0)
+ *(__be32 *)&p[opt->moffset] = (__be32)mask;
+ break;
+ }
+ case OPT_BE64: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 64);
+ if (err)
+ return -1;
+ *(__be64 *)&p[opt->offset] = htonll((u64)val);
+ if (opt->moffset >= 0)
+ *(__be64 *)&p[opt->moffset] = (__be64)mask;
+ break;
+ }
+ case OPT_IP4: {
+ __be32 val;
+ err = rxclass_get_ipv4(str, &val);
+ if (err)
+ return -1;
+ *(__be32 *)&p[opt->offset] = val;
+ if (opt->moffset >= 0)
+ *(__be32 *)&p[opt->moffset] = (__be32)mask;
+ break;
+ }
+ case OPT_IP6: {
+ __be32 val[4];
+ err = rxclass_get_ipv6(str, val);
+ if (err)
+ return -1;
+ memcpy(&p[opt->offset], val, sizeof(val));
+ if (opt->moffset >= 0)
+ memset(&p[opt->moffset], mask, sizeof(val));
+ break;
+ }
+ case OPT_MAC: {
+ unsigned char val[ETH_ALEN];
+ err = rxclass_get_ether(str, val);
+ if (err)
+ return -1;
+ memcpy(&p[opt->offset], val, ETH_ALEN);
+ if (opt->moffset >= 0)
+ memcpy(&p[opt->moffset], &mask, ETH_ALEN);
+ break;
+ }
+ case OPT_NONE:
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int rxclass_get_mask(char *str, unsigned char *p,
+ const struct rule_opts *opt)
+{
+ int err = 0;
+
+ if (opt->moffset < 0)
+ return -1;
+
+ switch (opt->type) {
+ case OPT_S32: {
+ long long val;
+ err = rxclass_get_long(str, &val, 32);
+ if (err)
+ return -1;
+ *(int *)&p[opt->moffset] = ~(int)val;
+ break;
+ }
+ case OPT_U8: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 8);
+ if (err)
+ return -1;
+ *(u8 *)&p[opt->moffset] = ~(u8)val;
+ break;
+ }
+ case OPT_U16: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 16);
+ if (err)
+ return -1;
+ *(u16 *)&p[opt->moffset] = ~(u16)val;
+ break;
+ }
+ case OPT_U32: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 32);
+ if (err)
+ return -1;
+ *(u32 *)&p[opt->moffset] = ~(u32)val;
+ break;
+ }
+ case OPT_U64: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 64);
+ if (err)
+ return -1;
+ *(u64 *)&p[opt->moffset] = ~(u64)val;
+ break;
+ }
+ case OPT_BE16: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 16);
+ if (err)
+ return -1;
+ *(__be16 *)&p[opt->moffset] = ~htons((u16)val);
+ break;
+ }
+ case OPT_BE32: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 32);
+ if (err)
+ return -1;
+ *(__be32 *)&p[opt->moffset] = ~htonl((u32)val);
+ break;
+ }
+ case OPT_BE64: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 64);
+ if (err)
+ return -1;
+ *(__be64 *)&p[opt->moffset] = ~htonll((u64)val);
+ break;
+ }
+ case OPT_IP4: {
+ __be32 val;
+ err = rxclass_get_ipv4(str, &val);
+ if (err)
+ return -1;
+ *(__be32 *)&p[opt->moffset] = ~val;
+ break;
+ }
+ case OPT_IP6: {
+ __be32 val[4], *field;
+ int i;
+ err = rxclass_get_ipv6(str, val);
+ if (err)
+ return -1;
+ field = (__be32 *)&p[opt->moffset];
+ for (i = 0; i < 4; i++)
+ field[i] = ~val[i];
+ break;
+ }
+ case OPT_MAC: {
+ unsigned char val[ETH_ALEN];
+ int i;
+ err = rxclass_get_ether(str, val);
+ if (err)
+ return -1;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ val[i] = ~val[i];
+
+ memcpy(&p[opt->moffset], val, ETH_ALEN);
+ break;
+ }
+ case OPT_NONE:
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int rxclass_parse_ruleopts(struct cmd_context *ctx,
+ struct ethtool_rx_flow_spec *fsp, __u32 *rss_context)
+{
+ const struct rule_opts *options;
+ unsigned char *p = (unsigned char *)fsp;
+ int i = 0, n_opts, err;
+ u32 flags = 0;
+ int flow_type;
+ int argc = ctx->argc;
+ char **argp = ctx->argp;
+
+ if (argc < 1)
+ goto syntax_err;
+
+ if (!strcmp(argp[0], "tcp4"))
+ flow_type = TCP_V4_FLOW;
+ else if (!strcmp(argp[0], "udp4"))
+ flow_type = UDP_V4_FLOW;
+ else if (!strcmp(argp[0], "sctp4"))
+ flow_type = SCTP_V4_FLOW;
+ else if (!strcmp(argp[0], "ah4"))
+ flow_type = AH_V4_FLOW;
+ else if (!strcmp(argp[0], "esp4"))
+ flow_type = ESP_V4_FLOW;
+ else if (!strcmp(argp[0], "ip4"))
+ flow_type = IPV4_USER_FLOW;
+ else if (!strcmp(argp[0], "tcp6"))
+ flow_type = TCP_V6_FLOW;
+ else if (!strcmp(argp[0], "udp6"))
+ flow_type = UDP_V6_FLOW;
+ else if (!strcmp(argp[0], "sctp6"))
+ flow_type = SCTP_V6_FLOW;
+ else if (!strcmp(argp[0], "ah6"))
+ flow_type = AH_V6_FLOW;
+ else if (!strcmp(argp[0], "esp6"))
+ flow_type = ESP_V6_FLOW;
+ else if (!strcmp(argp[0], "ip6"))
+ flow_type = IPV6_USER_FLOW;
+ else if (!strcmp(argp[0], "ether"))
+ flow_type = ETHER_FLOW;
+ else
+ goto syntax_err;
+
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ options = rule_nfc_tcp_ip4;
+ n_opts = ARRAY_SIZE(rule_nfc_tcp_ip4);
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ options = rule_nfc_esp_ip4;
+ n_opts = ARRAY_SIZE(rule_nfc_esp_ip4);
+ break;
+ case IPV4_USER_FLOW:
+ options = rule_nfc_usr_ip4;
+ n_opts = ARRAY_SIZE(rule_nfc_usr_ip4);
+ break;
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ case SCTP_V6_FLOW:
+ options = rule_nfc_tcp_ip6;
+ n_opts = ARRAY_SIZE(rule_nfc_tcp_ip6);
+ break;
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ options = rule_nfc_esp_ip6;
+ n_opts = ARRAY_SIZE(rule_nfc_esp_ip6);
+ break;
+ case IPV6_USER_FLOW:
+ options = rule_nfc_usr_ip6;
+ n_opts = ARRAY_SIZE(rule_nfc_usr_ip6);
+ break;
+ case ETHER_FLOW:
+ options = rule_nfc_ether;
+ n_opts = ARRAY_SIZE(rule_nfc_ether);
+ break;
+ default:
+ fprintf(stderr, "Add rule, invalid rule type[%s]\n", argp[0]);
+ return -1;
+ }
+
+ memset(p, 0, sizeof(*fsp));
+ fsp->flow_type = flow_type;
+ fsp->location = RX_CLS_LOC_ANY;
+
+ for (i = 1; i < argc;) {
+ const struct rule_opts *opt;
+ int idx;
+
+ /* special handling for 'context %d' as it doesn't go in
+ * the struct ethtool_rx_flow_spec
+ */
+ if (!strcmp(argp[i], "context")) {
+ unsigned long long val;
+
+ i++;
+ if (i >= argc) {
+ fprintf(stderr, "'context' missing value\n");
+ return -1;
+ }
+
+ if (rxclass_get_ulong(argp[i], &val, 32)) {
+ fprintf(stderr, "Invalid context value[%s]\n",
+ argp[i]);
+ return -1;
+ }
+
+ /* Can't use the ALLOC special value as the context ID
+ * of a filter to insert
+ */
+ if ((__u32)val == ETH_RXFH_CONTEXT_ALLOC) {
+ fprintf(stderr, "Bad context value %x\n",
+ (__u32)val);
+ return -1;
+ }
+
+ *rss_context = (__u32)val;
+ fsp->flow_type |= FLOW_RSS;
+ i++;
+ continue;
+ }
+
+ for (opt = options, idx = 0; idx < n_opts; idx++, opt++) {
+ char mask_name[16];
+
+ if (strcmp(argp[i], opt->name))
+ continue;
+
+ i++;
+ if (i >= argc)
+ break;
+
+ err = rxclass_get_val(argp[i], p, &flags, opt);
+ if (err) {
+ fprintf(stderr, "Invalid %s value[%s]\n",
+ opt->name, argp[i]);
+ return -1;
+ }
+
+ i++;
+ if (i >= argc)
+ break;
+
+ sprintf(mask_name, "%s-mask", opt->name);
+ if (strcmp(argp[i], "m") && strcmp(argp[i], mask_name))
+ break;
+
+ i++;
+ if (i >= argc)
+ goto syntax_err;
+
+ err = rxclass_get_mask(argp[i], p, opt);
+ if (err) {
+ fprintf(stderr, "Invalid %s mask[%s]\n",
+ opt->name, argp[i]);
+ return -1;
+ }
+
+ i++;
+
+ break;
+ }
+ if (idx == n_opts) {
+ fprintf(stdout, "Add rule, unrecognized option[%s]\n",
+ argp[i]);
+ return -1;
+ }
+ }
+
+ if ((flags & NFC_FLAG_RING) && (flags & NFC_FLAG_RING_QUEUE)) {
+ fprintf(stderr, "action and queue are not compatible\n");
+ return -1;
+ }
+
+ if ((flags & NFC_FLAG_RING) && (flags & NFC_FLAG_RING_VF)) {
+ fprintf(stderr, "action and vf are not compatible\n");
+ return -1;
+ }
+
+ if (flow_type == IPV4_USER_FLOW)
+ fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+ if (flags & (NTUPLE_FLAG_VLAN | NTUPLE_FLAG_UDEF | NTUPLE_FLAG_VETH))
+ fsp->flow_type |= FLOW_EXT;
+ if (flags & NFC_FLAG_MAC_ADDR)
+ fsp->flow_type |= FLOW_MAC_EXT;
+
+ return 0;
+
+syntax_err:
+ fprintf(stderr, "Add rule, invalid syntax\n");
+ return -1;
+}
diff --git a/scripts/ethtool-import-uapi b/scripts/ethtool-import-uapi
new file mode 100755
index 0000000..a04a9c9
--- /dev/null
+++ b/scripts/ethtool-import-uapi
@@ -0,0 +1,67 @@
+#!/bin/bash -e
+#
+# ethtool-import-uapi [commit]
+#
+# Imports sanitized copies of kernel uapi headers from <commit> (can be
+# a commit id, a tag or a branch name). If the argument is omitted,
+# commit currently checked out in the kernel repository is used.
+
+sn="${0##*/}"
+export ARCH="x86_64"
+mkopt="-j$(nproc)" || mkopt=''
+
+if [ ! -d "$LINUX_GIT" ]; then
+ echo "${sn}: please set LINUX_GIT to the location of kernel git" >&2
+ exit 1
+fi
+
+pushd "$LINUX_GIT"
+if [ -n "$1" ]; then
+ git checkout "$1"
+fi
+desc=$(git describe --exact-match 2>/dev/null \
+ || git show -s --abbrev=12 --pretty='commit %h')
+kobj=$(mktemp -d)
+make $mkopt O="$kobj" allmodconfig
+make $mkopt O="$kobj" prepare
+make $mkopt O="$kobj" INSTALL_HDR_PATH="${kobj}/hdr" headers_install
+popd
+
+pushd uapi
+find . -type f -name '*.h' -exec cp -v "${kobj}/hdr/include/{}" {} \;
+
+go_on=true
+while $go_on; do
+ go_on=false
+ while read f; do
+ if [ "${f#asm/}" != "$f" ]; then
+ # skip architecture dependent asm/ headers
+ continue
+ fi
+ if [ -f "$f" ]; then
+ # already present
+ continue
+ fi
+ if [ ! -f "${kobj}/hdr/include/${f}" ]; then
+ # not a kernel header
+ continue
+ fi
+ echo "+ add $f"
+ go_on=true
+ mkdir -p "${f%/*}"
+ cp "${kobj}/hdr/include/${f}" "${f}"
+ done < <(
+ find . -type f -name '*.[ch]' -exec sed -nre '\_^[[:blank:]]*#include[[:blank:]]<.+>_ { s_^[[:blank:]]*#include[[:blank:]]<([^>]*)>.*$_\1_ ; p }' {} \; \
+ | LC_ALL=C sort -u
+ )
+done
+popd
+rm -rf "$kobj"
+
+git add uapi
+git commit -s -F - <<EOT
+update UAPI header copies
+
+Update to kernel ${desc}.
+
+EOT
diff --git a/sfc.c b/sfc.c
new file mode 100644
index 0000000..340800e
--- /dev/null
+++ b/sfc.c
@@ -0,0 +1,3928 @@
+/****************************************************************************
+ * Support for Solarflare Solarstorm network controllers and boards
+ * Copyright 2010-2012 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "internal.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+/* Falcon architecture register definitions
+ * (from linux/drivers/net/ethernet/sfc/farch_regs.h)
+ */
+
+/* ADR_REGION_REG: Address region register */
+#define FR_AZ_ADR_REGION 0x00000000
+#define FRF_AZ_ADR_REGION3_LBN 96
+#define FRF_AZ_ADR_REGION3_WIDTH 18
+#define FRF_AZ_ADR_REGION2_LBN 64
+#define FRF_AZ_ADR_REGION2_WIDTH 18
+#define FRF_AZ_ADR_REGION1_LBN 32
+#define FRF_AZ_ADR_REGION1_WIDTH 18
+#define FRF_AZ_ADR_REGION0_LBN 0
+#define FRF_AZ_ADR_REGION0_WIDTH 18
+
+/* INT_EN_REG_KER: Kernel driver Interrupt enable register */
+#define FR_AZ_INT_EN_KER 0x00000010
+#define FRF_AZ_KER_INT_LEVE_SEL_LBN 8
+#define FRF_AZ_KER_INT_LEVE_SEL_WIDTH 6
+#define FRF_AZ_KER_INT_CHAR_LBN 4
+#define FRF_AZ_KER_INT_CHAR_WIDTH 1
+#define FRF_AZ_KER_INT_KER_LBN 3
+#define FRF_AZ_KER_INT_KER_WIDTH 1
+#define FRF_AZ_DRV_INT_EN_KER_LBN 0
+#define FRF_AZ_DRV_INT_EN_KER_WIDTH 1
+
+/* INT_EN_REG_CHAR: Char Driver interrupt enable register */
+#define FR_BZ_INT_EN_CHAR 0x00000020
+#define FRF_BZ_CHAR_INT_LEVE_SEL_LBN 8
+#define FRF_BZ_CHAR_INT_LEVE_SEL_WIDTH 6
+#define FRF_BZ_CHAR_INT_CHAR_LBN 4
+#define FRF_BZ_CHAR_INT_CHAR_WIDTH 1
+#define FRF_BZ_CHAR_INT_KER_LBN 3
+#define FRF_BZ_CHAR_INT_KER_WIDTH 1
+#define FRF_BZ_DRV_INT_EN_CHAR_LBN 0
+#define FRF_BZ_DRV_INT_EN_CHAR_WIDTH 1
+
+/* INT_ADR_REG_KER: Interrupt host address for Kernel driver */
+#define FR_AZ_INT_ADR_KER 0x00000030
+#define FRF_AZ_NORM_INT_VEC_DIS_KER_LBN 64
+#define FRF_AZ_NORM_INT_VEC_DIS_KER_WIDTH 1
+#define FRF_AZ_INT_ADR_KER_LBN 0
+#define FRF_AZ_INT_ADR_KER_WIDTH 64
+
+/* INT_ADR_REG_CHAR: Interrupt host address for Char driver */
+#define FR_BZ_INT_ADR_CHAR 0x00000040
+#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_LBN 64
+#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_WIDTH 1
+#define FRF_BZ_INT_ADR_CHAR_LBN 0
+#define FRF_BZ_INT_ADR_CHAR_WIDTH 64
+
+/* INT_ACK_KER: Kernel interrupt acknowledge register */
+#define FR_AA_INT_ACK_KER 0x00000050
+#define FRF_AA_INT_ACK_KER_FIELD_LBN 0
+#define FRF_AA_INT_ACK_KER_FIELD_WIDTH 32
+
+/* INT_ISR0_REG: Function 0 Interrupt Acknowledge Status register */
+#define FR_BZ_INT_ISR0 0x00000090
+#define FRF_BZ_INT_ISR_REG_LBN 0
+#define FRF_BZ_INT_ISR_REG_WIDTH 64
+
+/* HW_INIT_REG: Hardware initialization register */
+#define FR_AZ_HW_INIT 0x000000c0
+#define FRF_BB_BDMRD_CPLF_FULL_LBN 124
+#define FRF_BB_BDMRD_CPLF_FULL_WIDTH 1
+#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_LBN 121
+#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_WIDTH 3
+#define FRF_CZ_TX_MRG_TAGS_LBN 120
+#define FRF_CZ_TX_MRG_TAGS_WIDTH 1
+#define FRF_AB_TRGT_MASK_ALL_LBN 100
+#define FRF_AB_TRGT_MASK_ALL_WIDTH 1
+#define FRF_AZ_DOORBELL_DROP_LBN 92
+#define FRF_AZ_DOORBELL_DROP_WIDTH 8
+#define FRF_AB_TX_RREQ_MASK_EN_LBN 76
+#define FRF_AB_TX_RREQ_MASK_EN_WIDTH 1
+#define FRF_AB_PE_EIDLE_DIS_LBN 75
+#define FRF_AB_PE_EIDLE_DIS_WIDTH 1
+#define FRF_AA_FC_BLOCKING_EN_LBN 45
+#define FRF_AA_FC_BLOCKING_EN_WIDTH 1
+#define FRF_BZ_B2B_REQ_EN_LBN 45
+#define FRF_BZ_B2B_REQ_EN_WIDTH 1
+#define FRF_AA_B2B_REQ_EN_LBN 44
+#define FRF_AA_B2B_REQ_EN_WIDTH 1
+#define FRF_BB_FC_BLOCKING_EN_LBN 44
+#define FRF_BB_FC_BLOCKING_EN_WIDTH 1
+#define FRF_AZ_POST_WR_MASK_LBN 40
+#define FRF_AZ_POST_WR_MASK_WIDTH 4
+#define FRF_AZ_TLP_TC_LBN 34
+#define FRF_AZ_TLP_TC_WIDTH 3
+#define FRF_AZ_TLP_ATTR_LBN 32
+#define FRF_AZ_TLP_ATTR_WIDTH 2
+#define FRF_AB_INTB_VEC_LBN 24
+#define FRF_AB_INTB_VEC_WIDTH 5
+#define FRF_AB_INTA_VEC_LBN 16
+#define FRF_AB_INTA_VEC_WIDTH 5
+#define FRF_AZ_WD_TIMER_LBN 8
+#define FRF_AZ_WD_TIMER_WIDTH 8
+#define FRF_AZ_US_DISABLE_LBN 5
+#define FRF_AZ_US_DISABLE_WIDTH 1
+#define FRF_AZ_TLP_EP_LBN 4
+#define FRF_AZ_TLP_EP_WIDTH 1
+#define FRF_AZ_ATTR_SEL_LBN 3
+#define FRF_AZ_ATTR_SEL_WIDTH 1
+#define FRF_AZ_TD_SEL_LBN 1
+#define FRF_AZ_TD_SEL_WIDTH 1
+#define FRF_AZ_TLP_TD_LBN 0
+#define FRF_AZ_TLP_TD_WIDTH 1
+
+/* EE_SPI_HCMD_REG: SPI host command register */
+#define FR_AB_EE_SPI_HCMD 0x00000100
+#define FRF_AB_EE_SPI_HCMD_CMD_EN_LBN 31
+#define FRF_AB_EE_SPI_HCMD_CMD_EN_WIDTH 1
+#define FRF_AB_EE_WR_TIMER_ACTIVE_LBN 28
+#define FRF_AB_EE_WR_TIMER_ACTIVE_WIDTH 1
+#define FRF_AB_EE_SPI_HCMD_SF_SEL_LBN 24
+#define FRF_AB_EE_SPI_HCMD_SF_SEL_WIDTH 1
+#define FRF_AB_EE_SPI_HCMD_DABCNT_LBN 16
+#define FRF_AB_EE_SPI_HCMD_DABCNT_WIDTH 5
+#define FRF_AB_EE_SPI_HCMD_READ_LBN 15
+#define FRF_AB_EE_SPI_HCMD_READ_WIDTH 1
+#define FRF_AB_EE_SPI_HCMD_DUBCNT_LBN 12
+#define FRF_AB_EE_SPI_HCMD_DUBCNT_WIDTH 2
+#define FRF_AB_EE_SPI_HCMD_ADBCNT_LBN 8
+#define FRF_AB_EE_SPI_HCMD_ADBCNT_WIDTH 2
+#define FRF_AB_EE_SPI_HCMD_ENC_LBN 0
+#define FRF_AB_EE_SPI_HCMD_ENC_WIDTH 8
+
+/* USR_EV_CFG: User Level Event Configuration register */
+#define FR_CZ_USR_EV_CFG 0x00000100
+#define FRF_CZ_USREV_DIS_LBN 16
+#define FRF_CZ_USREV_DIS_WIDTH 1
+#define FRF_CZ_DFLT_EVQ_LBN 0
+#define FRF_CZ_DFLT_EVQ_WIDTH 10
+
+/* EE_SPI_HADR_REG: SPI host address register */
+#define FR_AB_EE_SPI_HADR 0x00000110
+#define FRF_AB_EE_SPI_HADR_DUBYTE_LBN 24
+#define FRF_AB_EE_SPI_HADR_DUBYTE_WIDTH 8
+#define FRF_AB_EE_SPI_HADR_ADR_LBN 0
+#define FRF_AB_EE_SPI_HADR_ADR_WIDTH 24
+
+/* EE_SPI_HDATA_REG: SPI host data register */
+#define FR_AB_EE_SPI_HDATA 0x00000120
+#define FRF_AB_EE_SPI_HDATA3_LBN 96
+#define FRF_AB_EE_SPI_HDATA3_WIDTH 32
+#define FRF_AB_EE_SPI_HDATA2_LBN 64
+#define FRF_AB_EE_SPI_HDATA2_WIDTH 32
+#define FRF_AB_EE_SPI_HDATA1_LBN 32
+#define FRF_AB_EE_SPI_HDATA1_WIDTH 32
+#define FRF_AB_EE_SPI_HDATA0_LBN 0
+#define FRF_AB_EE_SPI_HDATA0_WIDTH 32
+
+/* EE_BASE_PAGE_REG: Expansion ROM base mirror register */
+#define FR_AB_EE_BASE_PAGE 0x00000130
+#define FRF_AB_EE_EXPROM_MASK_LBN 16
+#define FRF_AB_EE_EXPROM_MASK_WIDTH 13
+#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_LBN 0
+#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_WIDTH 13
+
+/* EE_VPD_CFG0_REG: SPI/VPD configuration register 0 */
+#define FR_AB_EE_VPD_CFG0 0x00000140
+#define FRF_AB_EE_SF_FASTRD_EN_LBN 127
+#define FRF_AB_EE_SF_FASTRD_EN_WIDTH 1
+#define FRF_AB_EE_SF_CLOCK_DIV_LBN 120
+#define FRF_AB_EE_SF_CLOCK_DIV_WIDTH 7
+#define FRF_AB_EE_VPD_WIP_POLL_LBN 119
+#define FRF_AB_EE_VPD_WIP_POLL_WIDTH 1
+#define FRF_AB_EE_EE_CLOCK_DIV_LBN 112
+#define FRF_AB_EE_EE_CLOCK_DIV_WIDTH 7
+#define FRF_AB_EE_EE_WR_TMR_VALUE_LBN 96
+#define FRF_AB_EE_EE_WR_TMR_VALUE_WIDTH 16
+#define FRF_AB_EE_VPDW_LENGTH_LBN 80
+#define FRF_AB_EE_VPDW_LENGTH_WIDTH 15
+#define FRF_AB_EE_VPDW_BASE_LBN 64
+#define FRF_AB_EE_VPDW_BASE_WIDTH 15
+#define FRF_AB_EE_VPD_WR_CMD_EN_LBN 56
+#define FRF_AB_EE_VPD_WR_CMD_EN_WIDTH 8
+#define FRF_AB_EE_VPD_BASE_LBN 32
+#define FRF_AB_EE_VPD_BASE_WIDTH 24
+#define FRF_AB_EE_VPD_LENGTH_LBN 16
+#define FRF_AB_EE_VPD_LENGTH_WIDTH 15
+#define FRF_AB_EE_VPD_AD_SIZE_LBN 8
+#define FRF_AB_EE_VPD_AD_SIZE_WIDTH 5
+#define FRF_AB_EE_VPD_ACCESS_ON_LBN 5
+#define FRF_AB_EE_VPD_ACCESS_ON_WIDTH 1
+#define FRF_AB_EE_VPD_ACCESS_BLOCK_LBN 4
+#define FRF_AB_EE_VPD_ACCESS_BLOCK_WIDTH 1
+#define FRF_AB_EE_VPD_DEV_SF_SEL_LBN 2
+#define FRF_AB_EE_VPD_DEV_SF_SEL_WIDTH 1
+#define FRF_AB_EE_VPD_EN_AD9_MODE_LBN 1
+#define FRF_AB_EE_VPD_EN_AD9_MODE_WIDTH 1
+#define FRF_AB_EE_VPD_EN_LBN 0
+#define FRF_AB_EE_VPD_EN_WIDTH 1
+
+/* EE_VPD_SW_CNTL_REG: VPD access SW control register */
+#define FR_AB_EE_VPD_SW_CNTL 0x00000150
+#define FRF_AB_EE_VPD_CYCLE_PENDING_LBN 31
+#define FRF_AB_EE_VPD_CYCLE_PENDING_WIDTH 1
+#define FRF_AB_EE_VPD_CYC_WRITE_LBN 28
+#define FRF_AB_EE_VPD_CYC_WRITE_WIDTH 1
+#define FRF_AB_EE_VPD_CYC_ADR_LBN 0
+#define FRF_AB_EE_VPD_CYC_ADR_WIDTH 15
+
+/* EE_VPD_SW_DATA_REG: VPD access SW data register */
+#define FR_AB_EE_VPD_SW_DATA 0x00000160
+#define FRF_AB_EE_VPD_CYC_DAT_LBN 0
+#define FRF_AB_EE_VPD_CYC_DAT_WIDTH 32
+
+/* PBMX_DBG_IADDR_REG: Capture Module address register */
+#define FR_CZ_PBMX_DBG_IADDR 0x000001f0
+#define FRF_CZ_PBMX_DBG_IADDR_LBN 0
+#define FRF_CZ_PBMX_DBG_IADDR_WIDTH 32
+
+/* PCIE_CORE_INDIRECT_REG: Indirect Access to PCIE Core registers */
+#define FR_BB_PCIE_CORE_INDIRECT 0x000001f0
+#define FRF_BB_PCIE_CORE_TARGET_DATA_LBN 32
+#define FRF_BB_PCIE_CORE_TARGET_DATA_WIDTH 32
+#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_LBN 15
+#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_WIDTH 1
+#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_LBN 0
+#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_WIDTH 12
+
+/* PBMX_DBG_IDATA_REG: Capture Module data register */
+#define FR_CZ_PBMX_DBG_IDATA 0x000001f8
+#define FRF_CZ_PBMX_DBG_IDATA_LBN 0
+#define FRF_CZ_PBMX_DBG_IDATA_WIDTH 64
+
+/* NIC_STAT_REG: NIC status register */
+#define FR_AB_NIC_STAT 0x00000200
+#define FRF_BB_AER_DIS_LBN 34
+#define FRF_BB_AER_DIS_WIDTH 1
+#define FRF_BB_EE_STRAP_EN_LBN 31
+#define FRF_BB_EE_STRAP_EN_WIDTH 1
+#define FRF_BB_EE_STRAP_LBN 24
+#define FRF_BB_EE_STRAP_WIDTH 4
+#define FRF_BB_REVISION_ID_LBN 17
+#define FRF_BB_REVISION_ID_WIDTH 7
+#define FRF_AB_ONCHIP_SRAM_LBN 16
+#define FRF_AB_ONCHIP_SRAM_WIDTH 1
+#define FRF_AB_SF_PRST_LBN 9
+#define FRF_AB_SF_PRST_WIDTH 1
+#define FRF_AB_EE_PRST_LBN 8
+#define FRF_AB_EE_PRST_WIDTH 1
+#define FRF_AB_ATE_MODE_LBN 3
+#define FRF_AB_ATE_MODE_WIDTH 1
+#define FRF_AB_STRAP_PINS_LBN 0
+#define FRF_AB_STRAP_PINS_WIDTH 3
+
+/* GPIO_CTL_REG: GPIO control register */
+#define FR_AB_GPIO_CTL 0x00000210
+#define FRF_AB_GPIO_OUT3_LBN 112
+#define FRF_AB_GPIO_OUT3_WIDTH 16
+#define FRF_AB_GPIO_IN3_LBN 104
+#define FRF_AB_GPIO_IN3_WIDTH 8
+#define FRF_AB_GPIO_PWRUP_VALUE3_LBN 96
+#define FRF_AB_GPIO_PWRUP_VALUE3_WIDTH 8
+#define FRF_AB_GPIO_OUT2_LBN 80
+#define FRF_AB_GPIO_OUT2_WIDTH 16
+#define FRF_AB_GPIO_IN2_LBN 72
+#define FRF_AB_GPIO_IN2_WIDTH 8
+#define FRF_AB_GPIO_PWRUP_VALUE2_LBN 64
+#define FRF_AB_GPIO_PWRUP_VALUE2_WIDTH 8
+#define FRF_AB_GPIO15_OEN_LBN 63
+#define FRF_AB_GPIO15_OEN_WIDTH 1
+#define FRF_AB_GPIO14_OEN_LBN 62
+#define FRF_AB_GPIO14_OEN_WIDTH 1
+#define FRF_AB_GPIO13_OEN_LBN 61
+#define FRF_AB_GPIO13_OEN_WIDTH 1
+#define FRF_AB_GPIO12_OEN_LBN 60
+#define FRF_AB_GPIO12_OEN_WIDTH 1
+#define FRF_AB_GPIO11_OEN_LBN 59
+#define FRF_AB_GPIO11_OEN_WIDTH 1
+#define FRF_AB_GPIO10_OEN_LBN 58
+#define FRF_AB_GPIO10_OEN_WIDTH 1
+#define FRF_AB_GPIO9_OEN_LBN 57
+#define FRF_AB_GPIO9_OEN_WIDTH 1
+#define FRF_AB_GPIO8_OEN_LBN 56
+#define FRF_AB_GPIO8_OEN_WIDTH 1
+#define FRF_AB_GPIO15_OUT_LBN 55
+#define FRF_AB_GPIO15_OUT_WIDTH 1
+#define FRF_AB_GPIO14_OUT_LBN 54
+#define FRF_AB_GPIO14_OUT_WIDTH 1
+#define FRF_AB_GPIO13_OUT_LBN 53
+#define FRF_AB_GPIO13_OUT_WIDTH 1
+#define FRF_AB_GPIO12_OUT_LBN 52
+#define FRF_AB_GPIO12_OUT_WIDTH 1
+#define FRF_AB_GPIO11_OUT_LBN 51
+#define FRF_AB_GPIO11_OUT_WIDTH 1
+#define FRF_AB_GPIO10_OUT_LBN 50
+#define FRF_AB_GPIO10_OUT_WIDTH 1
+#define FRF_AB_GPIO9_OUT_LBN 49
+#define FRF_AB_GPIO9_OUT_WIDTH 1
+#define FRF_AB_GPIO8_OUT_LBN 48
+#define FRF_AB_GPIO8_OUT_WIDTH 1
+#define FRF_AB_GPIO15_IN_LBN 47
+#define FRF_AB_GPIO15_IN_WIDTH 1
+#define FRF_AB_GPIO14_IN_LBN 46
+#define FRF_AB_GPIO14_IN_WIDTH 1
+#define FRF_AB_GPIO13_IN_LBN 45
+#define FRF_AB_GPIO13_IN_WIDTH 1
+#define FRF_AB_GPIO12_IN_LBN 44
+#define FRF_AB_GPIO12_IN_WIDTH 1
+#define FRF_AB_GPIO11_IN_LBN 43
+#define FRF_AB_GPIO11_IN_WIDTH 1
+#define FRF_AB_GPIO10_IN_LBN 42
+#define FRF_AB_GPIO10_IN_WIDTH 1
+#define FRF_AB_GPIO9_IN_LBN 41
+#define FRF_AB_GPIO9_IN_WIDTH 1
+#define FRF_AB_GPIO8_IN_LBN 40
+#define FRF_AB_GPIO8_IN_WIDTH 1
+#define FRF_AB_GPIO15_PWRUP_VALUE_LBN 39
+#define FRF_AB_GPIO15_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO14_PWRUP_VALUE_LBN 38
+#define FRF_AB_GPIO14_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO13_PWRUP_VALUE_LBN 37
+#define FRF_AB_GPIO13_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO12_PWRUP_VALUE_LBN 36
+#define FRF_AB_GPIO12_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO11_PWRUP_VALUE_LBN 35
+#define FRF_AB_GPIO11_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO10_PWRUP_VALUE_LBN 34
+#define FRF_AB_GPIO10_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO9_PWRUP_VALUE_LBN 33
+#define FRF_AB_GPIO9_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO8_PWRUP_VALUE_LBN 32
+#define FRF_AB_GPIO8_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_CLK156_OUT_EN_LBN 31
+#define FRF_AB_CLK156_OUT_EN_WIDTH 1
+#define FRF_AB_USE_NIC_CLK_LBN 30
+#define FRF_AB_USE_NIC_CLK_WIDTH 1
+#define FRF_AB_GPIO5_OEN_LBN 29
+#define FRF_AB_GPIO5_OEN_WIDTH 1
+#define FRF_AB_GPIO4_OEN_LBN 28
+#define FRF_AB_GPIO4_OEN_WIDTH 1
+#define FRF_AB_GPIO3_OEN_LBN 27
+#define FRF_AB_GPIO3_OEN_WIDTH 1
+#define FRF_AB_GPIO2_OEN_LBN 26
+#define FRF_AB_GPIO2_OEN_WIDTH 1
+#define FRF_AB_GPIO1_OEN_LBN 25
+#define FRF_AB_GPIO1_OEN_WIDTH 1
+#define FRF_AB_GPIO0_OEN_LBN 24
+#define FRF_AB_GPIO0_OEN_WIDTH 1
+#define FRF_AB_GPIO7_OUT_LBN 23
+#define FRF_AB_GPIO7_OUT_WIDTH 1
+#define FRF_AB_GPIO6_OUT_LBN 22
+#define FRF_AB_GPIO6_OUT_WIDTH 1
+#define FRF_AB_GPIO5_OUT_LBN 21
+#define FRF_AB_GPIO5_OUT_WIDTH 1
+#define FRF_AB_GPIO4_OUT_LBN 20
+#define FRF_AB_GPIO4_OUT_WIDTH 1
+#define FRF_AB_GPIO3_OUT_LBN 19
+#define FRF_AB_GPIO3_OUT_WIDTH 1
+#define FRF_AB_GPIO2_OUT_LBN 18
+#define FRF_AB_GPIO2_OUT_WIDTH 1
+#define FRF_AB_GPIO1_OUT_LBN 17
+#define FRF_AB_GPIO1_OUT_WIDTH 1
+#define FRF_AB_GPIO0_OUT_LBN 16
+#define FRF_AB_GPIO0_OUT_WIDTH 1
+#define FRF_AB_GPIO7_IN_LBN 15
+#define FRF_AB_GPIO7_IN_WIDTH 1
+#define FRF_AB_GPIO6_IN_LBN 14
+#define FRF_AB_GPIO6_IN_WIDTH 1
+#define FRF_AB_GPIO5_IN_LBN 13
+#define FRF_AB_GPIO5_IN_WIDTH 1
+#define FRF_AB_GPIO4_IN_LBN 12
+#define FRF_AB_GPIO4_IN_WIDTH 1
+#define FRF_AB_GPIO3_IN_LBN 11
+#define FRF_AB_GPIO3_IN_WIDTH 1
+#define FRF_AB_GPIO2_IN_LBN 10
+#define FRF_AB_GPIO2_IN_WIDTH 1
+#define FRF_AB_GPIO1_IN_LBN 9
+#define FRF_AB_GPIO1_IN_WIDTH 1
+#define FRF_AB_GPIO0_IN_LBN 8
+#define FRF_AB_GPIO0_IN_WIDTH 1
+#define FRF_AB_GPIO7_PWRUP_VALUE_LBN 7
+#define FRF_AB_GPIO7_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO6_PWRUP_VALUE_LBN 6
+#define FRF_AB_GPIO6_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO5_PWRUP_VALUE_LBN 5
+#define FRF_AB_GPIO5_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO4_PWRUP_VALUE_LBN 4
+#define FRF_AB_GPIO4_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO3_PWRUP_VALUE_LBN 3
+#define FRF_AB_GPIO3_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO2_PWRUP_VALUE_LBN 2
+#define FRF_AB_GPIO2_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO1_PWRUP_VALUE_LBN 1
+#define FRF_AB_GPIO1_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO0_PWRUP_VALUE_LBN 0
+#define FRF_AB_GPIO0_PWRUP_VALUE_WIDTH 1
+
+/* GLB_CTL_REG: Global control register */
+#define FR_AB_GLB_CTL 0x00000220
+#define FRF_AB_EXT_PHY_RST_CTL_LBN 63
+#define FRF_AB_EXT_PHY_RST_CTL_WIDTH 1
+#define FRF_AB_XAUI_SD_RST_CTL_LBN 62
+#define FRF_AB_XAUI_SD_RST_CTL_WIDTH 1
+#define FRF_AB_PCIE_SD_RST_CTL_LBN 61
+#define FRF_AB_PCIE_SD_RST_CTL_WIDTH 1
+#define FRF_AA_PCIX_RST_CTL_LBN 60
+#define FRF_AA_PCIX_RST_CTL_WIDTH 1
+#define FRF_BB_BIU_RST_CTL_LBN 60
+#define FRF_BB_BIU_RST_CTL_WIDTH 1
+#define FRF_AB_PCIE_STKY_RST_CTL_LBN 59
+#define FRF_AB_PCIE_STKY_RST_CTL_WIDTH 1
+#define FRF_AB_PCIE_NSTKY_RST_CTL_LBN 58
+#define FRF_AB_PCIE_NSTKY_RST_CTL_WIDTH 1
+#define FRF_AB_PCIE_CORE_RST_CTL_LBN 57
+#define FRF_AB_PCIE_CORE_RST_CTL_WIDTH 1
+#define FRF_AB_XGRX_RST_CTL_LBN 56
+#define FRF_AB_XGRX_RST_CTL_WIDTH 1
+#define FRF_AB_XGTX_RST_CTL_LBN 55
+#define FRF_AB_XGTX_RST_CTL_WIDTH 1
+#define FRF_AB_EM_RST_CTL_LBN 54
+#define FRF_AB_EM_RST_CTL_WIDTH 1
+#define FRF_AB_EV_RST_CTL_LBN 53
+#define FRF_AB_EV_RST_CTL_WIDTH 1
+#define FRF_AB_SR_RST_CTL_LBN 52
+#define FRF_AB_SR_RST_CTL_WIDTH 1
+#define FRF_AB_RX_RST_CTL_LBN 51
+#define FRF_AB_RX_RST_CTL_WIDTH 1
+#define FRF_AB_TX_RST_CTL_LBN 50
+#define FRF_AB_TX_RST_CTL_WIDTH 1
+#define FRF_AB_EE_RST_CTL_LBN 49
+#define FRF_AB_EE_RST_CTL_WIDTH 1
+#define FRF_AB_CS_RST_CTL_LBN 48
+#define FRF_AB_CS_RST_CTL_WIDTH 1
+#define FRF_AB_HOT_RST_CTL_LBN 40
+#define FRF_AB_HOT_RST_CTL_WIDTH 2
+#define FRF_AB_RST_EXT_PHY_LBN 31
+#define FRF_AB_RST_EXT_PHY_WIDTH 1
+#define FRF_AB_RST_XAUI_SD_LBN 30
+#define FRF_AB_RST_XAUI_SD_WIDTH 1
+#define FRF_AB_RST_PCIE_SD_LBN 29
+#define FRF_AB_RST_PCIE_SD_WIDTH 1
+#define FRF_AA_RST_PCIX_LBN 28
+#define FRF_AA_RST_PCIX_WIDTH 1
+#define FRF_BB_RST_BIU_LBN 28
+#define FRF_BB_RST_BIU_WIDTH 1
+#define FRF_AB_RST_PCIE_STKY_LBN 27
+#define FRF_AB_RST_PCIE_STKY_WIDTH 1
+#define FRF_AB_RST_PCIE_NSTKY_LBN 26
+#define FRF_AB_RST_PCIE_NSTKY_WIDTH 1
+#define FRF_AB_RST_PCIE_CORE_LBN 25
+#define FRF_AB_RST_PCIE_CORE_WIDTH 1
+#define FRF_AB_RST_XGRX_LBN 24
+#define FRF_AB_RST_XGRX_WIDTH 1
+#define FRF_AB_RST_XGTX_LBN 23
+#define FRF_AB_RST_XGTX_WIDTH 1
+#define FRF_AB_RST_EM_LBN 22
+#define FRF_AB_RST_EM_WIDTH 1
+#define FRF_AB_RST_EV_LBN 21
+#define FRF_AB_RST_EV_WIDTH 1
+#define FRF_AB_RST_SR_LBN 20
+#define FRF_AB_RST_SR_WIDTH 1
+#define FRF_AB_RST_RX_LBN 19
+#define FRF_AB_RST_RX_WIDTH 1
+#define FRF_AB_RST_TX_LBN 18
+#define FRF_AB_RST_TX_WIDTH 1
+#define FRF_AB_RST_SF_LBN 17
+#define FRF_AB_RST_SF_WIDTH 1
+#define FRF_AB_RST_CS_LBN 16
+#define FRF_AB_RST_CS_WIDTH 1
+#define FRF_AB_INT_RST_DUR_LBN 4
+#define FRF_AB_INT_RST_DUR_WIDTH 3
+#define FRF_AB_EXT_PHY_RST_DUR_LBN 1
+#define FRF_AB_EXT_PHY_RST_DUR_WIDTH 3
+#define FFE_AB_EXT_PHY_RST_DUR_10240US 7
+#define FFE_AB_EXT_PHY_RST_DUR_5120US 6
+#define FFE_AB_EXT_PHY_RST_DUR_2560US 5
+#define FFE_AB_EXT_PHY_RST_DUR_1280US 4
+#define FFE_AB_EXT_PHY_RST_DUR_640US 3
+#define FFE_AB_EXT_PHY_RST_DUR_320US 2
+#define FFE_AB_EXT_PHY_RST_DUR_160US 1
+#define FFE_AB_EXT_PHY_RST_DUR_80US 0
+#define FRF_AB_SWRST_LBN 0
+#define FRF_AB_SWRST_WIDTH 1
+
+/* FATAL_INTR_REG_KER: Fatal interrupt register for Kernel */
+#define FR_AZ_FATAL_INTR_KER 0x00000230
+#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_LBN 44
+#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_WIDTH 1
+#define FRF_AB_PCI_BUSERR_INT_KER_EN_LBN 43
+#define FRF_AB_PCI_BUSERR_INT_KER_EN_WIDTH 1
+#define FRF_CZ_MBU_PERR_INT_KER_EN_LBN 43
+#define FRF_CZ_MBU_PERR_INT_KER_EN_WIDTH 1
+#define FRF_AZ_SRAM_OOB_INT_KER_EN_LBN 42
+#define FRF_AZ_SRAM_OOB_INT_KER_EN_WIDTH 1
+#define FRF_AZ_BUFID_OOB_INT_KER_EN_LBN 41
+#define FRF_AZ_BUFID_OOB_INT_KER_EN_WIDTH 1
+#define FRF_AZ_MEM_PERR_INT_KER_EN_LBN 40
+#define FRF_AZ_MEM_PERR_INT_KER_EN_WIDTH 1
+#define FRF_AZ_RBUF_OWN_INT_KER_EN_LBN 39
+#define FRF_AZ_RBUF_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_TBUF_OWN_INT_KER_EN_LBN 38
+#define FRF_AZ_TBUF_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_LBN 37
+#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_LBN 36
+#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_EVQ_OWN_INT_KER_EN_LBN 35
+#define FRF_AZ_EVQ_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_EVF_OFLO_INT_KER_EN_LBN 34
+#define FRF_AZ_EVF_OFLO_INT_KER_EN_WIDTH 1
+#define FRF_AZ_ILL_ADR_INT_KER_EN_LBN 33
+#define FRF_AZ_ILL_ADR_INT_KER_EN_WIDTH 1
+#define FRF_AZ_SRM_PERR_INT_KER_EN_LBN 32
+#define FRF_AZ_SRM_PERR_INT_KER_EN_WIDTH 1
+#define FRF_CZ_SRAM_PERR_INT_P_KER_LBN 12
+#define FRF_CZ_SRAM_PERR_INT_P_KER_WIDTH 1
+#define FRF_AB_PCI_BUSERR_INT_KER_LBN 11
+#define FRF_AB_PCI_BUSERR_INT_KER_WIDTH 1
+#define FRF_CZ_MBU_PERR_INT_KER_LBN 11
+#define FRF_CZ_MBU_PERR_INT_KER_WIDTH 1
+#define FRF_AZ_SRAM_OOB_INT_KER_LBN 10
+#define FRF_AZ_SRAM_OOB_INT_KER_WIDTH 1
+#define FRF_AZ_BUFID_DC_OOB_INT_KER_LBN 9
+#define FRF_AZ_BUFID_DC_OOB_INT_KER_WIDTH 1
+#define FRF_AZ_MEM_PERR_INT_KER_LBN 8
+#define FRF_AZ_MEM_PERR_INT_KER_WIDTH 1
+#define FRF_AZ_RBUF_OWN_INT_KER_LBN 7
+#define FRF_AZ_RBUF_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_TBUF_OWN_INT_KER_LBN 6
+#define FRF_AZ_TBUF_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_RDESCQ_OWN_INT_KER_LBN 5
+#define FRF_AZ_RDESCQ_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_TDESCQ_OWN_INT_KER_LBN 4
+#define FRF_AZ_TDESCQ_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_EVQ_OWN_INT_KER_LBN 3
+#define FRF_AZ_EVQ_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_EVF_OFLO_INT_KER_LBN 2
+#define FRF_AZ_EVF_OFLO_INT_KER_WIDTH 1
+#define FRF_AZ_ILL_ADR_INT_KER_LBN 1
+#define FRF_AZ_ILL_ADR_INT_KER_WIDTH 1
+#define FRF_AZ_SRM_PERR_INT_KER_LBN 0
+#define FRF_AZ_SRM_PERR_INT_KER_WIDTH 1
+
+/* FATAL_INTR_REG_CHAR: Fatal interrupt register for Char */
+#define FR_BZ_FATAL_INTR_CHAR 0x00000240
+#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_LBN 44
+#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_WIDTH 1
+#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_LBN 43
+#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_WIDTH 1
+#define FRF_CZ_MBU_PERR_INT_CHAR_EN_LBN 43
+#define FRF_CZ_MBU_PERR_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_LBN 42
+#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_LBN 41
+#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_MEM_PERR_INT_CHAR_EN_LBN 40
+#define FRF_BZ_MEM_PERR_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_LBN 39
+#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_LBN 38
+#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_LBN 37
+#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_LBN 36
+#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_LBN 35
+#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_LBN 34
+#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_ILL_ADR_INT_CHAR_EN_LBN 33
+#define FRF_BZ_ILL_ADR_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_SRM_PERR_INT_CHAR_EN_LBN 32
+#define FRF_BZ_SRM_PERR_INT_CHAR_EN_WIDTH 1
+#define FRF_CZ_SRAM_PERR_INT_P_CHAR_LBN 12
+#define FRF_CZ_SRAM_PERR_INT_P_CHAR_WIDTH 1
+#define FRF_BB_PCI_BUSERR_INT_CHAR_LBN 11
+#define FRF_BB_PCI_BUSERR_INT_CHAR_WIDTH 1
+#define FRF_CZ_MBU_PERR_INT_CHAR_LBN 11
+#define FRF_CZ_MBU_PERR_INT_CHAR_WIDTH 1
+#define FRF_BZ_SRAM_OOB_INT_CHAR_LBN 10
+#define FRF_BZ_SRAM_OOB_INT_CHAR_WIDTH 1
+#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_LBN 9
+#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_WIDTH 1
+#define FRF_BZ_MEM_PERR_INT_CHAR_LBN 8
+#define FRF_BZ_MEM_PERR_INT_CHAR_WIDTH 1
+#define FRF_BZ_RBUF_OWN_INT_CHAR_LBN 7
+#define FRF_BZ_RBUF_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_TBUF_OWN_INT_CHAR_LBN 6
+#define FRF_BZ_TBUF_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_RDESCQ_OWN_INT_CHAR_LBN 5
+#define FRF_BZ_RDESCQ_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_TDESCQ_OWN_INT_CHAR_LBN 4
+#define FRF_BZ_TDESCQ_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_EVQ_OWN_INT_CHAR_LBN 3
+#define FRF_BZ_EVQ_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_EVF_OFLO_INT_CHAR_LBN 2
+#define FRF_BZ_EVF_OFLO_INT_CHAR_WIDTH 1
+#define FRF_BZ_ILL_ADR_INT_CHAR_LBN 1
+#define FRF_BZ_ILL_ADR_INT_CHAR_WIDTH 1
+#define FRF_BZ_SRM_PERR_INT_CHAR_LBN 0
+#define FRF_BZ_SRM_PERR_INT_CHAR_WIDTH 1
+
+/* DP_CTRL_REG: Datapath control register */
+#define FR_BZ_DP_CTRL 0x00000250
+#define FRF_BZ_FLS_EVQ_ID_LBN 0
+#define FRF_BZ_FLS_EVQ_ID_WIDTH 12
+
+/* MEM_STAT_REG: Memory status register */
+#define FR_AZ_MEM_STAT 0x00000260
+#define FRF_AB_MEM_PERR_VEC_LBN 53
+#define FRF_AB_MEM_PERR_VEC_WIDTH 38
+#define FRF_AB_MBIST_CORR_LBN 38
+#define FRF_AB_MBIST_CORR_WIDTH 15
+#define FRF_AB_MBIST_ERR_LBN 0
+#define FRF_AB_MBIST_ERR_WIDTH 40
+#define FRF_CZ_MEM_PERR_VEC_LBN 0
+#define FRF_CZ_MEM_PERR_VEC_WIDTH 35
+
+/* CS_DEBUG_REG: Debug register */
+#define FR_AZ_CS_DEBUG 0x00000270
+#define FRF_AB_GLB_DEBUG2_SEL_LBN 50
+#define FRF_AB_GLB_DEBUG2_SEL_WIDTH 3
+#define FRF_AB_DEBUG_BLK_SEL2_LBN 47
+#define FRF_AB_DEBUG_BLK_SEL2_WIDTH 3
+#define FRF_AB_DEBUG_BLK_SEL1_LBN 44
+#define FRF_AB_DEBUG_BLK_SEL1_WIDTH 3
+#define FRF_AB_DEBUG_BLK_SEL0_LBN 41
+#define FRF_AB_DEBUG_BLK_SEL0_WIDTH 3
+#define FRF_CZ_CS_PORT_NUM_LBN 40
+#define FRF_CZ_CS_PORT_NUM_WIDTH 2
+#define FRF_AB_MISC_DEBUG_ADDR_LBN 36
+#define FRF_AB_MISC_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_SERDES_DEBUG_ADDR_LBN 31
+#define FRF_AB_SERDES_DEBUG_ADDR_WIDTH 5
+#define FRF_CZ_CS_PORT_FPE_LBN 1
+#define FRF_CZ_CS_PORT_FPE_WIDTH 35
+#define FRF_AB_EM_DEBUG_ADDR_LBN 26
+#define FRF_AB_EM_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_SR_DEBUG_ADDR_LBN 21
+#define FRF_AB_SR_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_EV_DEBUG_ADDR_LBN 16
+#define FRF_AB_EV_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_RX_DEBUG_ADDR_LBN 11
+#define FRF_AB_RX_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_TX_DEBUG_ADDR_LBN 6
+#define FRF_AB_TX_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_CS_BIU_DEBUG_ADDR_LBN 1
+#define FRF_AB_CS_BIU_DEBUG_ADDR_WIDTH 5
+#define FRF_AZ_CS_DEBUG_EN_LBN 0
+#define FRF_AZ_CS_DEBUG_EN_WIDTH 1
+
+/* DRIVER_REG: Driver scratch register [0-7] */
+#define FR_AZ_DRIVER 0x00000280
+#define FR_AZ_DRIVER_STEP 16
+#define FR_AZ_DRIVER_ROWS 8
+#define FRF_AZ_DRIVER_DW0_LBN 0
+#define FRF_AZ_DRIVER_DW0_WIDTH 32
+
+/* ALTERA_BUILD_REG: Altera build register */
+#define FR_AZ_ALTERA_BUILD 0x00000300
+#define FRF_AZ_ALTERA_BUILD_VER_LBN 0
+#define FRF_AZ_ALTERA_BUILD_VER_WIDTH 32
+
+/* CSR_SPARE_REG: Spare register */
+#define FR_AZ_CSR_SPARE 0x00000310
+#define FRF_AB_MEM_PERR_EN_LBN 64
+#define FRF_AB_MEM_PERR_EN_WIDTH 38
+#define FRF_CZ_MEM_PERR_EN_LBN 64
+#define FRF_CZ_MEM_PERR_EN_WIDTH 35
+#define FRF_AB_MEM_PERR_EN_TX_DATA_LBN 72
+#define FRF_AB_MEM_PERR_EN_TX_DATA_WIDTH 2
+#define FRF_AZ_CSR_SPARE_BITS_LBN 0
+#define FRF_AZ_CSR_SPARE_BITS_WIDTH 32
+
+/* PCIE_SD_CTL0123_REG: PCIE SerDes control register 0 to 3 */
+#define FR_AB_PCIE_SD_CTL0123 0x00000320
+#define FRF_AB_PCIE_TESTSIG_H_LBN 96
+#define FRF_AB_PCIE_TESTSIG_H_WIDTH 19
+#define FRF_AB_PCIE_TESTSIG_L_LBN 64
+#define FRF_AB_PCIE_TESTSIG_L_WIDTH 19
+#define FRF_AB_PCIE_OFFSET_LBN 56
+#define FRF_AB_PCIE_OFFSET_WIDTH 8
+#define FRF_AB_PCIE_OFFSETEN_H_LBN 55
+#define FRF_AB_PCIE_OFFSETEN_H_WIDTH 1
+#define FRF_AB_PCIE_OFFSETEN_L_LBN 54
+#define FRF_AB_PCIE_OFFSETEN_L_WIDTH 1
+#define FRF_AB_PCIE_HIVMODE_H_LBN 53
+#define FRF_AB_PCIE_HIVMODE_H_WIDTH 1
+#define FRF_AB_PCIE_HIVMODE_L_LBN 52
+#define FRF_AB_PCIE_HIVMODE_L_WIDTH 1
+#define FRF_AB_PCIE_PARRESET_H_LBN 51
+#define FRF_AB_PCIE_PARRESET_H_WIDTH 1
+#define FRF_AB_PCIE_PARRESET_L_LBN 50
+#define FRF_AB_PCIE_PARRESET_L_WIDTH 1
+#define FRF_AB_PCIE_LPBKWDRV_H_LBN 49
+#define FRF_AB_PCIE_LPBKWDRV_H_WIDTH 1
+#define FRF_AB_PCIE_LPBKWDRV_L_LBN 48
+#define FRF_AB_PCIE_LPBKWDRV_L_WIDTH 1
+#define FRF_AB_PCIE_LPBK_LBN 40
+#define FRF_AB_PCIE_LPBK_WIDTH 8
+#define FRF_AB_PCIE_PARLPBK_LBN 32
+#define FRF_AB_PCIE_PARLPBK_WIDTH 8
+#define FRF_AB_PCIE_RXTERMADJ_H_LBN 30
+#define FRF_AB_PCIE_RXTERMADJ_H_WIDTH 2
+#define FRF_AB_PCIE_RXTERMADJ_L_LBN 28
+#define FRF_AB_PCIE_RXTERMADJ_L_WIDTH 2
+#define FFE_AB_PCIE_RXTERMADJ_MIN15PCNT 3
+#define FFE_AB_PCIE_RXTERMADJ_PL10PCNT 2
+#define FFE_AB_PCIE_RXTERMADJ_MIN17PCNT 1
+#define FFE_AB_PCIE_RXTERMADJ_NOMNL 0
+#define FRF_AB_PCIE_TXTERMADJ_H_LBN 26
+#define FRF_AB_PCIE_TXTERMADJ_H_WIDTH 2
+#define FRF_AB_PCIE_TXTERMADJ_L_LBN 24
+#define FRF_AB_PCIE_TXTERMADJ_L_WIDTH 2
+#define FFE_AB_PCIE_TXTERMADJ_MIN15PCNT 3
+#define FFE_AB_PCIE_TXTERMADJ_PL10PCNT 2
+#define FFE_AB_PCIE_TXTERMADJ_MIN17PCNT 1
+#define FFE_AB_PCIE_TXTERMADJ_NOMNL 0
+#define FRF_AB_PCIE_RXEQCTL_H_LBN 18
+#define FRF_AB_PCIE_RXEQCTL_H_WIDTH 2
+#define FRF_AB_PCIE_RXEQCTL_L_LBN 16
+#define FRF_AB_PCIE_RXEQCTL_L_WIDTH 2
+#define FFE_AB_PCIE_RXEQCTL_OFF_ALT 3
+#define FFE_AB_PCIE_RXEQCTL_OFF 2
+#define FFE_AB_PCIE_RXEQCTL_MIN 1
+#define FFE_AB_PCIE_RXEQCTL_MAX 0
+#define FRF_AB_PCIE_HIDRV_LBN 8
+#define FRF_AB_PCIE_HIDRV_WIDTH 8
+#define FRF_AB_PCIE_LODRV_LBN 0
+#define FRF_AB_PCIE_LODRV_WIDTH 8
+
+/* PCIE_SD_CTL45_REG: PCIE SerDes control register 4 and 5 */
+#define FR_AB_PCIE_SD_CTL45 0x00000330
+#define FRF_AB_PCIE_DTX7_LBN 60
+#define FRF_AB_PCIE_DTX7_WIDTH 4
+#define FRF_AB_PCIE_DTX6_LBN 56
+#define FRF_AB_PCIE_DTX6_WIDTH 4
+#define FRF_AB_PCIE_DTX5_LBN 52
+#define FRF_AB_PCIE_DTX5_WIDTH 4
+#define FRF_AB_PCIE_DTX4_LBN 48
+#define FRF_AB_PCIE_DTX4_WIDTH 4
+#define FRF_AB_PCIE_DTX3_LBN 44
+#define FRF_AB_PCIE_DTX3_WIDTH 4
+#define FRF_AB_PCIE_DTX2_LBN 40
+#define FRF_AB_PCIE_DTX2_WIDTH 4
+#define FRF_AB_PCIE_DTX1_LBN 36
+#define FRF_AB_PCIE_DTX1_WIDTH 4
+#define FRF_AB_PCIE_DTX0_LBN 32
+#define FRF_AB_PCIE_DTX0_WIDTH 4
+#define FRF_AB_PCIE_DEQ7_LBN 28
+#define FRF_AB_PCIE_DEQ7_WIDTH 4
+#define FRF_AB_PCIE_DEQ6_LBN 24
+#define FRF_AB_PCIE_DEQ6_WIDTH 4
+#define FRF_AB_PCIE_DEQ5_LBN 20
+#define FRF_AB_PCIE_DEQ5_WIDTH 4
+#define FRF_AB_PCIE_DEQ4_LBN 16
+#define FRF_AB_PCIE_DEQ4_WIDTH 4
+#define FRF_AB_PCIE_DEQ3_LBN 12
+#define FRF_AB_PCIE_DEQ3_WIDTH 4
+#define FRF_AB_PCIE_DEQ2_LBN 8
+#define FRF_AB_PCIE_DEQ2_WIDTH 4
+#define FRF_AB_PCIE_DEQ1_LBN 4
+#define FRF_AB_PCIE_DEQ1_WIDTH 4
+#define FRF_AB_PCIE_DEQ0_LBN 0
+#define FRF_AB_PCIE_DEQ0_WIDTH 4
+
+/* PCIE_PCS_CTL_STAT_REG: PCIE PCS control and status register */
+#define FR_AB_PCIE_PCS_CTL_STAT 0x00000340
+#define FRF_AB_PCIE_PRBSERRCOUNT0_H_LBN 52
+#define FRF_AB_PCIE_PRBSERRCOUNT0_H_WIDTH 4
+#define FRF_AB_PCIE_PRBSERRCOUNT0_L_LBN 48
+#define FRF_AB_PCIE_PRBSERRCOUNT0_L_WIDTH 4
+#define FRF_AB_PCIE_PRBSERR_LBN 40
+#define FRF_AB_PCIE_PRBSERR_WIDTH 8
+#define FRF_AB_PCIE_PRBSERRH0_LBN 32
+#define FRF_AB_PCIE_PRBSERRH0_WIDTH 8
+#define FRF_AB_PCIE_FASTINIT_H_LBN 15
+#define FRF_AB_PCIE_FASTINIT_H_WIDTH 1
+#define FRF_AB_PCIE_FASTINIT_L_LBN 14
+#define FRF_AB_PCIE_FASTINIT_L_WIDTH 1
+#define FRF_AB_PCIE_CTCDISABLE_H_LBN 13
+#define FRF_AB_PCIE_CTCDISABLE_H_WIDTH 1
+#define FRF_AB_PCIE_CTCDISABLE_L_LBN 12
+#define FRF_AB_PCIE_CTCDISABLE_L_WIDTH 1
+#define FRF_AB_PCIE_PRBSSYNC_H_LBN 11
+#define FRF_AB_PCIE_PRBSSYNC_H_WIDTH 1
+#define FRF_AB_PCIE_PRBSSYNC_L_LBN 10
+#define FRF_AB_PCIE_PRBSSYNC_L_WIDTH 1
+#define FRF_AB_PCIE_PRBSERRACK_H_LBN 9
+#define FRF_AB_PCIE_PRBSERRACK_H_WIDTH 1
+#define FRF_AB_PCIE_PRBSERRACK_L_LBN 8
+#define FRF_AB_PCIE_PRBSERRACK_L_WIDTH 1
+#define FRF_AB_PCIE_PRBSSEL_LBN 0
+#define FRF_AB_PCIE_PRBSSEL_WIDTH 8
+
+/* DEBUG_DATA_OUT_REG: Live Debug and Debug 2 out ports */
+#define FR_BB_DEBUG_DATA_OUT 0x00000350
+#define FRF_BB_DEBUG2_PORT_LBN 25
+#define FRF_BB_DEBUG2_PORT_WIDTH 15
+#define FRF_BB_DEBUG1_PORT_LBN 0
+#define FRF_BB_DEBUG1_PORT_WIDTH 25
+
+/* EVQ_RPTR_REGP0: Event queue read pointer register */
+#define FR_BZ_EVQ_RPTR_P0 0x00000400
+#define FR_BZ_EVQ_RPTR_P0_STEP 8192
+#define FR_BZ_EVQ_RPTR_P0_ROWS 1024
+/* EVQ_RPTR_REG_KER: Event queue read pointer register */
+#define FR_AA_EVQ_RPTR_KER 0x00011b00
+#define FR_AA_EVQ_RPTR_KER_STEP 4
+#define FR_AA_EVQ_RPTR_KER_ROWS 4
+/* EVQ_RPTR_REG: Event queue read pointer register */
+#define FR_BZ_EVQ_RPTR 0x00fa0000
+#define FR_BZ_EVQ_RPTR_STEP 16
+#define FR_BB_EVQ_RPTR_ROWS 4096
+#define FR_CZ_EVQ_RPTR_ROWS 1024
+/* EVQ_RPTR_REGP123: Event queue read pointer register */
+#define FR_BB_EVQ_RPTR_P123 0x01000400
+#define FR_BB_EVQ_RPTR_P123_STEP 8192
+#define FR_BB_EVQ_RPTR_P123_ROWS 3072
+#define FRF_AZ_EVQ_RPTR_VLD_LBN 15
+#define FRF_AZ_EVQ_RPTR_VLD_WIDTH 1
+#define FRF_AZ_EVQ_RPTR_LBN 0
+#define FRF_AZ_EVQ_RPTR_WIDTH 15
+
+/* TIMER_COMMAND_REGP0: Timer Command Registers */
+#define FR_BZ_TIMER_COMMAND_P0 0x00000420
+#define FR_BZ_TIMER_COMMAND_P0_STEP 8192
+#define FR_BZ_TIMER_COMMAND_P0_ROWS 1024
+/* TIMER_COMMAND_REG_KER: Timer Command Registers */
+#define FR_AA_TIMER_COMMAND_KER 0x00000420
+#define FR_AA_TIMER_COMMAND_KER_STEP 8192
+#define FR_AA_TIMER_COMMAND_KER_ROWS 4
+/* TIMER_COMMAND_REGP123: Timer Command Registers */
+#define FR_BB_TIMER_COMMAND_P123 0x01000420
+#define FR_BB_TIMER_COMMAND_P123_STEP 8192
+#define FR_BB_TIMER_COMMAND_P123_ROWS 3072
+#define FRF_CZ_TC_TIMER_MODE_LBN 14
+#define FRF_CZ_TC_TIMER_MODE_WIDTH 2
+#define FRF_AB_TC_TIMER_MODE_LBN 12
+#define FRF_AB_TC_TIMER_MODE_WIDTH 2
+#define FRF_CZ_TC_TIMER_VAL_LBN 0
+#define FRF_CZ_TC_TIMER_VAL_WIDTH 14
+#define FRF_AB_TC_TIMER_VAL_LBN 0
+#define FRF_AB_TC_TIMER_VAL_WIDTH 12
+
+/* DRV_EV_REG: Driver generated event register */
+#define FR_AZ_DRV_EV 0x00000440
+#define FRF_AZ_DRV_EV_QID_LBN 64
+#define FRF_AZ_DRV_EV_QID_WIDTH 12
+#define FRF_AZ_DRV_EV_DATA_LBN 0
+#define FRF_AZ_DRV_EV_DATA_WIDTH 64
+
+/* EVQ_CTL_REG: Event queue control register */
+#define FR_AZ_EVQ_CTL 0x00000450
+#define FRF_CZ_RX_EVQ_WAKEUP_MASK_LBN 15
+#define FRF_CZ_RX_EVQ_WAKEUP_MASK_WIDTH 10
+#define FRF_BB_RX_EVQ_WAKEUP_MASK_LBN 15
+#define FRF_BB_RX_EVQ_WAKEUP_MASK_WIDTH 6
+#define FRF_AZ_EVQ_OWNERR_CTL_LBN 14
+#define FRF_AZ_EVQ_OWNERR_CTL_WIDTH 1
+#define FRF_AZ_EVQ_FIFO_AF_TH_LBN 7
+#define FRF_AZ_EVQ_FIFO_AF_TH_WIDTH 7
+#define FRF_AZ_EVQ_FIFO_NOTAF_TH_LBN 0
+#define FRF_AZ_EVQ_FIFO_NOTAF_TH_WIDTH 7
+
+/* EVQ_CNT1_REG: Event counter 1 register */
+#define FR_AZ_EVQ_CNT1 0x00000460
+#define FRF_AZ_EVQ_CNT_PRE_FIFO_LBN 120
+#define FRF_AZ_EVQ_CNT_PRE_FIFO_WIDTH 7
+#define FRF_AZ_EVQ_CNT_TOBIU_LBN 100
+#define FRF_AZ_EVQ_CNT_TOBIU_WIDTH 20
+#define FRF_AZ_EVQ_TX_REQ_CNT_LBN 80
+#define FRF_AZ_EVQ_TX_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_RX_REQ_CNT_LBN 60
+#define FRF_AZ_EVQ_RX_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_EM_REQ_CNT_LBN 40
+#define FRF_AZ_EVQ_EM_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_CSR_REQ_CNT_LBN 20
+#define FRF_AZ_EVQ_CSR_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_ERR_REQ_CNT_LBN 0
+#define FRF_AZ_EVQ_ERR_REQ_CNT_WIDTH 20
+
+/* EVQ_CNT2_REG: Event counter 2 register */
+#define FR_AZ_EVQ_CNT2 0x00000470
+#define FRF_AZ_EVQ_UPD_REQ_CNT_LBN 104
+#define FRF_AZ_EVQ_UPD_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_CLR_REQ_CNT_LBN 84
+#define FRF_AZ_EVQ_CLR_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_RDY_CNT_LBN 80
+#define FRF_AZ_EVQ_RDY_CNT_WIDTH 4
+#define FRF_AZ_EVQ_WU_REQ_CNT_LBN 60
+#define FRF_AZ_EVQ_WU_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_WET_REQ_CNT_LBN 40
+#define FRF_AZ_EVQ_WET_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_INIT_REQ_CNT_LBN 20
+#define FRF_AZ_EVQ_INIT_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_TM_REQ_CNT_LBN 0
+#define FRF_AZ_EVQ_TM_REQ_CNT_WIDTH 20
+
+/* USR_EV_REG: Event mailbox register */
+#define FR_CZ_USR_EV 0x00000540
+#define FR_CZ_USR_EV_STEP 8192
+#define FR_CZ_USR_EV_ROWS 1024
+#define FRF_CZ_USR_EV_DATA_LBN 0
+#define FRF_CZ_USR_EV_DATA_WIDTH 32
+
+/* BUF_TBL_CFG_REG: Buffer table configuration register */
+#define FR_AZ_BUF_TBL_CFG 0x00000600
+#define FRF_AZ_BUF_TBL_MODE_LBN 3
+#define FRF_AZ_BUF_TBL_MODE_WIDTH 1
+
+/* SRM_RX_DC_CFG_REG: SRAM receive descriptor cache configuration register */
+#define FR_AZ_SRM_RX_DC_CFG 0x00000610
+#define FRF_AZ_SRM_CLK_TMP_EN_LBN 21
+#define FRF_AZ_SRM_CLK_TMP_EN_WIDTH 1
+#define FRF_AZ_SRM_RX_DC_BASE_ADR_LBN 0
+#define FRF_AZ_SRM_RX_DC_BASE_ADR_WIDTH 21
+
+/* SRM_TX_DC_CFG_REG: SRAM transmit descriptor cache configuration register */
+#define FR_AZ_SRM_TX_DC_CFG 0x00000620
+#define FRF_AZ_SRM_TX_DC_BASE_ADR_LBN 0
+#define FRF_AZ_SRM_TX_DC_BASE_ADR_WIDTH 21
+
+/* SRM_CFG_REG: SRAM configuration register */
+#define FR_AZ_SRM_CFG 0x00000630
+#define FRF_AZ_SRM_OOB_ADR_INTEN_LBN 5
+#define FRF_AZ_SRM_OOB_ADR_INTEN_WIDTH 1
+#define FRF_AZ_SRM_OOB_BUF_INTEN_LBN 4
+#define FRF_AZ_SRM_OOB_BUF_INTEN_WIDTH 1
+#define FRF_AZ_SRM_INIT_EN_LBN 3
+#define FRF_AZ_SRM_INIT_EN_WIDTH 1
+#define FRF_AZ_SRM_NUM_BANK_LBN 2
+#define FRF_AZ_SRM_NUM_BANK_WIDTH 1
+#define FRF_AZ_SRM_BANK_SIZE_LBN 0
+#define FRF_AZ_SRM_BANK_SIZE_WIDTH 2
+
+/* BUF_TBL_UPD_REG: Buffer table update register */
+#define FR_AZ_BUF_TBL_UPD 0x00000650
+#define FRF_AZ_BUF_UPD_CMD_LBN 63
+#define FRF_AZ_BUF_UPD_CMD_WIDTH 1
+#define FRF_AZ_BUF_CLR_CMD_LBN 62
+#define FRF_AZ_BUF_CLR_CMD_WIDTH 1
+#define FRF_AZ_BUF_CLR_END_ID_LBN 32
+#define FRF_AZ_BUF_CLR_END_ID_WIDTH 20
+#define FRF_AZ_BUF_CLR_START_ID_LBN 0
+#define FRF_AZ_BUF_CLR_START_ID_WIDTH 20
+
+/* SRM_UPD_EVQ_REG: Buffer table update register */
+#define FR_AZ_SRM_UPD_EVQ 0x00000660
+#define FRF_AZ_SRM_UPD_EVQ_ID_LBN 0
+#define FRF_AZ_SRM_UPD_EVQ_ID_WIDTH 12
+
+/* SRAM_PARITY_REG: SRAM parity register. */
+#define FR_AZ_SRAM_PARITY 0x00000670
+#define FRF_CZ_BYPASS_ECC_LBN 3
+#define FRF_CZ_BYPASS_ECC_WIDTH 1
+#define FRF_CZ_SEC_INT_LBN 2
+#define FRF_CZ_SEC_INT_WIDTH 1
+#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_LBN 1
+#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_WIDTH 1
+#define FRF_AB_FORCE_SRAM_PERR_LBN 0
+#define FRF_AB_FORCE_SRAM_PERR_WIDTH 1
+#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_LBN 0
+#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_WIDTH 1
+
+/* RX_CFG_REG: Receive configuration register */
+#define FR_AZ_RX_CFG 0x00000800
+#define FRF_CZ_RX_MIN_KBUF_SIZE_LBN 72
+#define FRF_CZ_RX_MIN_KBUF_SIZE_WIDTH 14
+#define FRF_CZ_RX_HDR_SPLIT_EN_LBN 71
+#define FRF_CZ_RX_HDR_SPLIT_EN_WIDTH 1
+#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_LBN 62
+#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_WIDTH 9
+#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_LBN 53
+#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_WIDTH 9
+#define FRF_CZ_RX_PRE_RFF_IPG_LBN 49
+#define FRF_CZ_RX_PRE_RFF_IPG_WIDTH 4
+#define FRF_BZ_RX_TCP_SUP_LBN 48
+#define FRF_BZ_RX_TCP_SUP_WIDTH 1
+#define FRF_BZ_RX_INGR_EN_LBN 47
+#define FRF_BZ_RX_INGR_EN_WIDTH 1
+#define FRF_BZ_RX_IP_HASH_LBN 46
+#define FRF_BZ_RX_IP_HASH_WIDTH 1
+#define FRF_BZ_RX_HASH_ALG_LBN 45
+#define FRF_BZ_RX_HASH_ALG_WIDTH 1
+#define FRF_BZ_RX_HASH_INSRT_HDR_LBN 44
+#define FRF_BZ_RX_HASH_INSRT_HDR_WIDTH 1
+#define FRF_BZ_RX_DESC_PUSH_EN_LBN 43
+#define FRF_BZ_RX_DESC_PUSH_EN_WIDTH 1
+#define FRF_BZ_RX_RDW_PATCH_EN_LBN 42
+#define FRF_BZ_RX_RDW_PATCH_EN_WIDTH 1
+#define FRF_BB_RX_PCI_BURST_SIZE_LBN 39
+#define FRF_BB_RX_PCI_BURST_SIZE_WIDTH 3
+#define FRF_BZ_RX_OWNERR_CTL_LBN 38
+#define FRF_BZ_RX_OWNERR_CTL_WIDTH 1
+#define FRF_BZ_RX_XON_TX_TH_LBN 33
+#define FRF_BZ_RX_XON_TX_TH_WIDTH 5
+#define FRF_AA_RX_DESC_PUSH_EN_LBN 35
+#define FRF_AA_RX_DESC_PUSH_EN_WIDTH 1
+#define FRF_AA_RX_RDW_PATCH_EN_LBN 34
+#define FRF_AA_RX_RDW_PATCH_EN_WIDTH 1
+#define FRF_AA_RX_PCI_BURST_SIZE_LBN 31
+#define FRF_AA_RX_PCI_BURST_SIZE_WIDTH 3
+#define FRF_BZ_RX_XOFF_TX_TH_LBN 28
+#define FRF_BZ_RX_XOFF_TX_TH_WIDTH 5
+#define FRF_AA_RX_OWNERR_CTL_LBN 30
+#define FRF_AA_RX_OWNERR_CTL_WIDTH 1
+#define FRF_AA_RX_XON_TX_TH_LBN 25
+#define FRF_AA_RX_XON_TX_TH_WIDTH 5
+#define FRF_BZ_RX_USR_BUF_SIZE_LBN 19
+#define FRF_BZ_RX_USR_BUF_SIZE_WIDTH 9
+#define FRF_AA_RX_XOFF_TX_TH_LBN 20
+#define FRF_AA_RX_XOFF_TX_TH_WIDTH 5
+#define FRF_AA_RX_USR_BUF_SIZE_LBN 11
+#define FRF_AA_RX_USR_BUF_SIZE_WIDTH 9
+#define FRF_BZ_RX_XON_MAC_TH_LBN 10
+#define FRF_BZ_RX_XON_MAC_TH_WIDTH 9
+#define FRF_AA_RX_XON_MAC_TH_LBN 6
+#define FRF_AA_RX_XON_MAC_TH_WIDTH 5
+#define FRF_BZ_RX_XOFF_MAC_TH_LBN 1
+#define FRF_BZ_RX_XOFF_MAC_TH_WIDTH 9
+#define FRF_AA_RX_XOFF_MAC_TH_LBN 1
+#define FRF_AA_RX_XOFF_MAC_TH_WIDTH 5
+#define FRF_AZ_RX_XOFF_MAC_EN_LBN 0
+#define FRF_AZ_RX_XOFF_MAC_EN_WIDTH 1
+
+/* RX_FILTER_CTL_REG: Receive filter control registers */
+#define FR_BZ_RX_FILTER_CTL 0x00000810
+#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_LBN 94
+#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_WIDTH 8
+#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_LBN 86
+#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_WIDTH 8
+#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_LBN 85
+#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_WIDTH 1
+#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_LBN 69
+#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_WIDTH 16
+#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_LBN 57
+#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_WIDTH 12
+#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_LBN 56
+#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_WIDTH 1
+#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_LBN 55
+#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_WIDTH 1
+#define FRF_CZ_UNICAST_NOMATCH_Q_ID_LBN 43
+#define FRF_CZ_UNICAST_NOMATCH_Q_ID_WIDTH 12
+#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_LBN 42
+#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_WIDTH 1
+#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_LBN 41
+#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_WIDTH 1
+#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_LBN 40
+#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_WIDTH 1
+#define FRF_BZ_UDP_FULL_SRCH_LIMIT_LBN 32
+#define FRF_BZ_UDP_FULL_SRCH_LIMIT_WIDTH 8
+#define FRF_BZ_NUM_KER_LBN 24
+#define FRF_BZ_NUM_KER_WIDTH 2
+#define FRF_BZ_UDP_WILD_SRCH_LIMIT_LBN 16
+#define FRF_BZ_UDP_WILD_SRCH_LIMIT_WIDTH 8
+#define FRF_BZ_TCP_WILD_SRCH_LIMIT_LBN 8
+#define FRF_BZ_TCP_WILD_SRCH_LIMIT_WIDTH 8
+#define FRF_BZ_TCP_FULL_SRCH_LIMIT_LBN 0
+#define FRF_BZ_TCP_FULL_SRCH_LIMIT_WIDTH 8
+
+/* RX_FLUSH_DESCQ_REG: Receive flush descriptor queue register */
+#define FR_AZ_RX_FLUSH_DESCQ 0x00000820
+#define FRF_AZ_RX_FLUSH_DESCQ_CMD_LBN 24
+#define FRF_AZ_RX_FLUSH_DESCQ_CMD_WIDTH 1
+#define FRF_AZ_RX_FLUSH_DESCQ_LBN 0
+#define FRF_AZ_RX_FLUSH_DESCQ_WIDTH 12
+
+/* RX_DESC_UPD_REGP0: Receive descriptor update register. */
+#define FR_BZ_RX_DESC_UPD_P0 0x00000830
+#define FR_BZ_RX_DESC_UPD_P0_STEP 8192
+#define FR_BZ_RX_DESC_UPD_P0_ROWS 1024
+/* RX_DESC_UPD_REG_KER: Receive descriptor update register. */
+#define FR_AA_RX_DESC_UPD_KER 0x00000830
+#define FR_AA_RX_DESC_UPD_KER_STEP 8192
+#define FR_AA_RX_DESC_UPD_KER_ROWS 4
+/* RX_DESC_UPD_REGP123: Receive descriptor update register. */
+#define FR_BB_RX_DESC_UPD_P123 0x01000830
+#define FR_BB_RX_DESC_UPD_P123_STEP 8192
+#define FR_BB_RX_DESC_UPD_P123_ROWS 3072
+#define FRF_AZ_RX_DESC_WPTR_LBN 96
+#define FRF_AZ_RX_DESC_WPTR_WIDTH 12
+#define FRF_AZ_RX_DESC_PUSH_CMD_LBN 95
+#define FRF_AZ_RX_DESC_PUSH_CMD_WIDTH 1
+#define FRF_AZ_RX_DESC_LBN 0
+#define FRF_AZ_RX_DESC_WIDTH 64
+
+/* RX_DC_CFG_REG: Receive descriptor cache configuration register */
+#define FR_AZ_RX_DC_CFG 0x00000840
+#define FRF_AB_RX_MAX_PF_LBN 2
+#define FRF_AB_RX_MAX_PF_WIDTH 2
+#define FRF_AZ_RX_DC_SIZE_LBN 0
+#define FRF_AZ_RX_DC_SIZE_WIDTH 2
+#define FFE_AZ_RX_DC_SIZE_64 3
+#define FFE_AZ_RX_DC_SIZE_32 2
+#define FFE_AZ_RX_DC_SIZE_16 1
+#define FFE_AZ_RX_DC_SIZE_8 0
+
+/* RX_DC_PF_WM_REG: Receive descriptor cache pre-fetch watermark register */
+#define FR_AZ_RX_DC_PF_WM 0x00000850
+#define FRF_AZ_RX_DC_PF_HWM_LBN 6
+#define FRF_AZ_RX_DC_PF_HWM_WIDTH 6
+#define FRF_AZ_RX_DC_PF_LWM_LBN 0
+#define FRF_AZ_RX_DC_PF_LWM_WIDTH 6
+
+/* RX_RSS_TKEY_REG: RSS Toeplitz hash key */
+#define FR_BZ_RX_RSS_TKEY 0x00000860
+#define FRF_BZ_RX_RSS_TKEY_HI_LBN 64
+#define FRF_BZ_RX_RSS_TKEY_HI_WIDTH 64
+#define FRF_BZ_RX_RSS_TKEY_LO_LBN 0
+#define FRF_BZ_RX_RSS_TKEY_LO_WIDTH 64
+
+/* RX_NODESC_DROP_REG: Receive dropped packet counter register */
+#define FR_AZ_RX_NODESC_DROP 0x00000880
+#define FRF_CZ_RX_NODESC_DROP_CNT_LBN 0
+#define FRF_CZ_RX_NODESC_DROP_CNT_WIDTH 32
+#define FRF_AB_RX_NODESC_DROP_CNT_LBN 0
+#define FRF_AB_RX_NODESC_DROP_CNT_WIDTH 16
+
+/* RX_SELF_RST_REG: Receive self reset register */
+#define FR_AA_RX_SELF_RST 0x00000890
+#define FRF_AA_RX_ISCSI_DIS_LBN 17
+#define FRF_AA_RX_ISCSI_DIS_WIDTH 1
+#define FRF_AA_RX_SW_RST_REG_LBN 16
+#define FRF_AA_RX_SW_RST_REG_WIDTH 1
+#define FRF_AA_RX_NODESC_WAIT_DIS_LBN 9
+#define FRF_AA_RX_NODESC_WAIT_DIS_WIDTH 1
+#define FRF_AA_RX_SELF_RST_EN_LBN 8
+#define FRF_AA_RX_SELF_RST_EN_WIDTH 1
+#define FRF_AA_RX_MAX_PF_LAT_LBN 4
+#define FRF_AA_RX_MAX_PF_LAT_WIDTH 4
+#define FRF_AA_RX_MAX_LU_LAT_LBN 0
+#define FRF_AA_RX_MAX_LU_LAT_WIDTH 4
+
+/* RX_DEBUG_REG: undocumented register */
+#define FR_AZ_RX_DEBUG 0x000008a0
+#define FRF_AZ_RX_DEBUG_LBN 0
+#define FRF_AZ_RX_DEBUG_WIDTH 64
+
+/* RX_PUSH_DROP_REG: Receive descriptor push dropped counter register */
+#define FR_AZ_RX_PUSH_DROP 0x000008b0
+#define FRF_AZ_RX_PUSH_DROP_CNT_LBN 0
+#define FRF_AZ_RX_PUSH_DROP_CNT_WIDTH 32
+
+/* RX_RSS_IPV6_REG1: IPv6 RSS Toeplitz hash key low bytes */
+#define FR_CZ_RX_RSS_IPV6_REG1 0x000008d0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_LBN 0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_WIDTH 128
+
+/* RX_RSS_IPV6_REG2: IPv6 RSS Toeplitz hash key middle bytes */
+#define FR_CZ_RX_RSS_IPV6_REG2 0x000008e0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_LBN 0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_WIDTH 128
+
+/* RX_RSS_IPV6_REG3: IPv6 RSS Toeplitz hash key upper bytes and IPv6 RSS settings */
+#define FR_CZ_RX_RSS_IPV6_REG3 0x000008f0
+#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_LBN 66
+#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_WIDTH 1
+#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_LBN 65
+#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_WIDTH 1
+#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_LBN 64
+#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_WIDTH 1
+#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_LBN 0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_WIDTH 64
+
+/* TX_FLUSH_DESCQ_REG: Transmit flush descriptor queue register */
+#define FR_AZ_TX_FLUSH_DESCQ 0x00000a00
+#define FRF_AZ_TX_FLUSH_DESCQ_CMD_LBN 12
+#define FRF_AZ_TX_FLUSH_DESCQ_CMD_WIDTH 1
+#define FRF_AZ_TX_FLUSH_DESCQ_LBN 0
+#define FRF_AZ_TX_FLUSH_DESCQ_WIDTH 12
+
+/* TX_DESC_UPD_REGP0: Transmit descriptor update register. */
+#define FR_BZ_TX_DESC_UPD_P0 0x00000a10
+#define FR_BZ_TX_DESC_UPD_P0_STEP 8192
+#define FR_BZ_TX_DESC_UPD_P0_ROWS 1024
+/* TX_DESC_UPD_REG_KER: Transmit descriptor update register. */
+#define FR_AA_TX_DESC_UPD_KER 0x00000a10
+#define FR_AA_TX_DESC_UPD_KER_STEP 8192
+#define FR_AA_TX_DESC_UPD_KER_ROWS 8
+/* TX_DESC_UPD_REGP123: Transmit descriptor update register. */
+#define FR_BB_TX_DESC_UPD_P123 0x01000a10
+#define FR_BB_TX_DESC_UPD_P123_STEP 8192
+#define FR_BB_TX_DESC_UPD_P123_ROWS 3072
+#define FRF_AZ_TX_DESC_WPTR_LBN 96
+#define FRF_AZ_TX_DESC_WPTR_WIDTH 12
+#define FRF_AZ_TX_DESC_PUSH_CMD_LBN 95
+#define FRF_AZ_TX_DESC_PUSH_CMD_WIDTH 1
+#define FRF_AZ_TX_DESC_LBN 0
+#define FRF_AZ_TX_DESC_WIDTH 95
+
+/* TX_DC_CFG_REG: Transmit descriptor cache configuration register */
+#define FR_AZ_TX_DC_CFG 0x00000a20
+#define FRF_AZ_TX_DC_SIZE_LBN 0
+#define FRF_AZ_TX_DC_SIZE_WIDTH 2
+#define FFE_AZ_TX_DC_SIZE_32 2
+#define FFE_AZ_TX_DC_SIZE_16 1
+#define FFE_AZ_TX_DC_SIZE_8 0
+
+/* TX_CHKSM_CFG_REG: Transmit checksum configuration register */
+#define FR_AA_TX_CHKSM_CFG 0x00000a30
+#define FRF_AA_TX_Q_CHKSM_DIS_96_127_LBN 96
+#define FRF_AA_TX_Q_CHKSM_DIS_96_127_WIDTH 32
+#define FRF_AA_TX_Q_CHKSM_DIS_64_95_LBN 64
+#define FRF_AA_TX_Q_CHKSM_DIS_64_95_WIDTH 32
+#define FRF_AA_TX_Q_CHKSM_DIS_32_63_LBN 32
+#define FRF_AA_TX_Q_CHKSM_DIS_32_63_WIDTH 32
+#define FRF_AA_TX_Q_CHKSM_DIS_0_31_LBN 0
+#define FRF_AA_TX_Q_CHKSM_DIS_0_31_WIDTH 32
+
+/* TX_CFG_REG: Transmit configuration register */
+#define FR_AZ_TX_CFG 0x00000a50
+#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_LBN 114
+#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_WIDTH 8
+#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_LBN 113
+#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_WIDTH 1
+#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_LBN 105
+#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_LBN 97
+#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_LBN 89
+#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_LBN 81
+#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_LBN 73
+#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_LBN 65
+#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_LBN 64
+#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_WIDTH 1
+#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_LBN 48
+#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_WIDTH 16
+#define FRF_CZ_TX_FILTER_EN_BIT_LBN 47
+#define FRF_CZ_TX_FILTER_EN_BIT_WIDTH 1
+#define FRF_AZ_TX_IP_ID_P0_OFS_LBN 16
+#define FRF_AZ_TX_IP_ID_P0_OFS_WIDTH 15
+#define FRF_AZ_TX_NO_EOP_DISC_EN_LBN 5
+#define FRF_AZ_TX_NO_EOP_DISC_EN_WIDTH 1
+#define FRF_AZ_TX_P1_PRI_EN_LBN 4
+#define FRF_AZ_TX_P1_PRI_EN_WIDTH 1
+#define FRF_AZ_TX_OWNERR_CTL_LBN 2
+#define FRF_AZ_TX_OWNERR_CTL_WIDTH 1
+#define FRF_AA_TX_NON_IP_DROP_DIS_LBN 1
+#define FRF_AA_TX_NON_IP_DROP_DIS_WIDTH 1
+#define FRF_AZ_TX_IP_ID_REP_EN_LBN 0
+#define FRF_AZ_TX_IP_ID_REP_EN_WIDTH 1
+
+/* TX_PUSH_DROP_REG: Transmit push dropped register */
+#define FR_AZ_TX_PUSH_DROP 0x00000a60
+#define FRF_AZ_TX_PUSH_DROP_CNT_LBN 0
+#define FRF_AZ_TX_PUSH_DROP_CNT_WIDTH 32
+
+/* TX_RESERVED_REG: Transmit configuration register */
+#define FR_AZ_TX_RESERVED 0x00000a80
+#define FRF_AZ_TX_EVT_CNT_LBN 121
+#define FRF_AZ_TX_EVT_CNT_WIDTH 7
+#define FRF_AZ_TX_PREF_AGE_CNT_LBN 119
+#define FRF_AZ_TX_PREF_AGE_CNT_WIDTH 2
+#define FRF_AZ_TX_RD_COMP_TMR_LBN 96
+#define FRF_AZ_TX_RD_COMP_TMR_WIDTH 23
+#define FRF_AZ_TX_PUSH_EN_LBN 89
+#define FRF_AZ_TX_PUSH_EN_WIDTH 1
+#define FRF_AZ_TX_PUSH_CHK_DIS_LBN 88
+#define FRF_AZ_TX_PUSH_CHK_DIS_WIDTH 1
+#define FRF_AZ_TX_D_FF_FULL_P0_LBN 85
+#define FRF_AZ_TX_D_FF_FULL_P0_WIDTH 1
+#define FRF_AZ_TX_DMAR_ST_P0_LBN 81
+#define FRF_AZ_TX_DMAR_ST_P0_WIDTH 1
+#define FRF_AZ_TX_DMAQ_ST_LBN 78
+#define FRF_AZ_TX_DMAQ_ST_WIDTH 1
+#define FRF_AZ_TX_RX_SPACER_LBN 64
+#define FRF_AZ_TX_RX_SPACER_WIDTH 8
+#define FRF_AZ_TX_DROP_ABORT_EN_LBN 60
+#define FRF_AZ_TX_DROP_ABORT_EN_WIDTH 1
+#define FRF_AZ_TX_SOFT_EVT_EN_LBN 59
+#define FRF_AZ_TX_SOFT_EVT_EN_WIDTH 1
+#define FRF_AZ_TX_PS_EVT_DIS_LBN 58
+#define FRF_AZ_TX_PS_EVT_DIS_WIDTH 1
+#define FRF_AZ_TX_RX_SPACER_EN_LBN 57
+#define FRF_AZ_TX_RX_SPACER_EN_WIDTH 1
+#define FRF_AZ_TX_XP_TIMER_LBN 52
+#define FRF_AZ_TX_XP_TIMER_WIDTH 5
+#define FRF_AZ_TX_PREF_SPACER_LBN 44
+#define FRF_AZ_TX_PREF_SPACER_WIDTH 8
+#define FRF_AZ_TX_PREF_WD_TMR_LBN 22
+#define FRF_AZ_TX_PREF_WD_TMR_WIDTH 22
+#define FRF_AZ_TX_ONLY1TAG_LBN 21
+#define FRF_AZ_TX_ONLY1TAG_WIDTH 1
+#define FRF_AZ_TX_PREF_THRESHOLD_LBN 19
+#define FRF_AZ_TX_PREF_THRESHOLD_WIDTH 2
+#define FRF_AZ_TX_ONE_PKT_PER_Q_LBN 18
+#define FRF_AZ_TX_ONE_PKT_PER_Q_WIDTH 1
+#define FRF_AZ_TX_DIS_NON_IP_EV_LBN 17
+#define FRF_AZ_TX_DIS_NON_IP_EV_WIDTH 1
+#define FRF_AA_TX_DMA_FF_THR_LBN 16
+#define FRF_AA_TX_DMA_FF_THR_WIDTH 1
+#define FRF_AZ_TX_DMA_SPACER_LBN 8
+#define FRF_AZ_TX_DMA_SPACER_WIDTH 8
+#define FRF_AA_TX_TCP_DIS_LBN 7
+#define FRF_AA_TX_TCP_DIS_WIDTH 1
+#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_LBN 7
+#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_WIDTH 1
+#define FRF_AA_TX_IP_DIS_LBN 6
+#define FRF_AA_TX_IP_DIS_WIDTH 1
+#define FRF_AZ_TX_MAX_CPL_LBN 2
+#define FRF_AZ_TX_MAX_CPL_WIDTH 2
+#define FFE_AZ_TX_MAX_CPL_16 3
+#define FFE_AZ_TX_MAX_CPL_8 2
+#define FFE_AZ_TX_MAX_CPL_4 1
+#define FFE_AZ_TX_MAX_CPL_NOLIMIT 0
+#define FRF_AZ_TX_MAX_PREF_LBN 0
+#define FRF_AZ_TX_MAX_PREF_WIDTH 2
+#define FFE_AZ_TX_MAX_PREF_32 3
+#define FFE_AZ_TX_MAX_PREF_16 2
+#define FFE_AZ_TX_MAX_PREF_8 1
+#define FFE_AZ_TX_MAX_PREF_OFF 0
+
+/* TX_PACE_REG: Transmit pace control register */
+#define FR_BZ_TX_PACE 0x00000a90
+#define FRF_BZ_TX_PACE_SB_NOT_AF_LBN 19
+#define FRF_BZ_TX_PACE_SB_NOT_AF_WIDTH 10
+#define FRF_BZ_TX_PACE_SB_AF_LBN 9
+#define FRF_BZ_TX_PACE_SB_AF_WIDTH 10
+#define FRF_BZ_TX_PACE_FB_BASE_LBN 5
+#define FRF_BZ_TX_PACE_FB_BASE_WIDTH 4
+#define FRF_BZ_TX_PACE_BIN_TH_LBN 0
+#define FRF_BZ_TX_PACE_BIN_TH_WIDTH 5
+
+/* TX_PACE_DROP_QID_REG: PACE Drop QID Counter */
+#define FR_BZ_TX_PACE_DROP_QID 0x00000aa0
+#define FRF_BZ_TX_PACE_QID_DRP_CNT_LBN 0
+#define FRF_BZ_TX_PACE_QID_DRP_CNT_WIDTH 16
+
+/* TX_VLAN_REG: Transmit VLAN tag register */
+#define FR_BB_TX_VLAN 0x00000ae0
+#define FRF_BB_TX_VLAN_EN_LBN 127
+#define FRF_BB_TX_VLAN_EN_WIDTH 1
+#define FRF_BB_TX_VLAN7_PORT1_EN_LBN 125
+#define FRF_BB_TX_VLAN7_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN7_PORT0_EN_LBN 124
+#define FRF_BB_TX_VLAN7_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN7_LBN 112
+#define FRF_BB_TX_VLAN7_WIDTH 12
+#define FRF_BB_TX_VLAN6_PORT1_EN_LBN 109
+#define FRF_BB_TX_VLAN6_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN6_PORT0_EN_LBN 108
+#define FRF_BB_TX_VLAN6_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN6_LBN 96
+#define FRF_BB_TX_VLAN6_WIDTH 12
+#define FRF_BB_TX_VLAN5_PORT1_EN_LBN 93
+#define FRF_BB_TX_VLAN5_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN5_PORT0_EN_LBN 92
+#define FRF_BB_TX_VLAN5_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN5_LBN 80
+#define FRF_BB_TX_VLAN5_WIDTH 12
+#define FRF_BB_TX_VLAN4_PORT1_EN_LBN 77
+#define FRF_BB_TX_VLAN4_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN4_PORT0_EN_LBN 76
+#define FRF_BB_TX_VLAN4_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN4_LBN 64
+#define FRF_BB_TX_VLAN4_WIDTH 12
+#define FRF_BB_TX_VLAN3_PORT1_EN_LBN 61
+#define FRF_BB_TX_VLAN3_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN3_PORT0_EN_LBN 60
+#define FRF_BB_TX_VLAN3_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN3_LBN 48
+#define FRF_BB_TX_VLAN3_WIDTH 12
+#define FRF_BB_TX_VLAN2_PORT1_EN_LBN 45
+#define FRF_BB_TX_VLAN2_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN2_PORT0_EN_LBN 44
+#define FRF_BB_TX_VLAN2_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN2_LBN 32
+#define FRF_BB_TX_VLAN2_WIDTH 12
+#define FRF_BB_TX_VLAN1_PORT1_EN_LBN 29
+#define FRF_BB_TX_VLAN1_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN1_PORT0_EN_LBN 28
+#define FRF_BB_TX_VLAN1_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN1_LBN 16
+#define FRF_BB_TX_VLAN1_WIDTH 12
+#define FRF_BB_TX_VLAN0_PORT1_EN_LBN 13
+#define FRF_BB_TX_VLAN0_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN0_PORT0_EN_LBN 12
+#define FRF_BB_TX_VLAN0_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN0_LBN 0
+#define FRF_BB_TX_VLAN0_WIDTH 12
+
+/* TX_IPFIL_PORTEN_REG: Transmit filter control register */
+#define FR_BZ_TX_IPFIL_PORTEN 0x00000af0
+#define FRF_BZ_TX_MADR0_FIL_EN_LBN 64
+#define FRF_BZ_TX_MADR0_FIL_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL31_PORT_EN_LBN 62
+#define FRF_BB_TX_IPFIL31_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL30_PORT_EN_LBN 60
+#define FRF_BB_TX_IPFIL30_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL29_PORT_EN_LBN 58
+#define FRF_BB_TX_IPFIL29_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL28_PORT_EN_LBN 56
+#define FRF_BB_TX_IPFIL28_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL27_PORT_EN_LBN 54
+#define FRF_BB_TX_IPFIL27_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL26_PORT_EN_LBN 52
+#define FRF_BB_TX_IPFIL26_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL25_PORT_EN_LBN 50
+#define FRF_BB_TX_IPFIL25_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL24_PORT_EN_LBN 48
+#define FRF_BB_TX_IPFIL24_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL23_PORT_EN_LBN 46
+#define FRF_BB_TX_IPFIL23_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL22_PORT_EN_LBN 44
+#define FRF_BB_TX_IPFIL22_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL21_PORT_EN_LBN 42
+#define FRF_BB_TX_IPFIL21_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL20_PORT_EN_LBN 40
+#define FRF_BB_TX_IPFIL20_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL19_PORT_EN_LBN 38
+#define FRF_BB_TX_IPFIL19_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL18_PORT_EN_LBN 36
+#define FRF_BB_TX_IPFIL18_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL17_PORT_EN_LBN 34
+#define FRF_BB_TX_IPFIL17_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL16_PORT_EN_LBN 32
+#define FRF_BB_TX_IPFIL16_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL15_PORT_EN_LBN 30
+#define FRF_BB_TX_IPFIL15_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL14_PORT_EN_LBN 28
+#define FRF_BB_TX_IPFIL14_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL13_PORT_EN_LBN 26
+#define FRF_BB_TX_IPFIL13_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL12_PORT_EN_LBN 24
+#define FRF_BB_TX_IPFIL12_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL11_PORT_EN_LBN 22
+#define FRF_BB_TX_IPFIL11_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL10_PORT_EN_LBN 20
+#define FRF_BB_TX_IPFIL10_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL9_PORT_EN_LBN 18
+#define FRF_BB_TX_IPFIL9_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL8_PORT_EN_LBN 16
+#define FRF_BB_TX_IPFIL8_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL7_PORT_EN_LBN 14
+#define FRF_BB_TX_IPFIL7_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL6_PORT_EN_LBN 12
+#define FRF_BB_TX_IPFIL6_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL5_PORT_EN_LBN 10
+#define FRF_BB_TX_IPFIL5_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL4_PORT_EN_LBN 8
+#define FRF_BB_TX_IPFIL4_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL3_PORT_EN_LBN 6
+#define FRF_BB_TX_IPFIL3_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL2_PORT_EN_LBN 4
+#define FRF_BB_TX_IPFIL2_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL1_PORT_EN_LBN 2
+#define FRF_BB_TX_IPFIL1_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL0_PORT_EN_LBN 0
+#define FRF_BB_TX_IPFIL0_PORT_EN_WIDTH 1
+
+/* TX_IPFIL_TBL: Transmit IP source address filter table */
+#define FR_BB_TX_IPFIL_TBL 0x00000b00
+#define FR_BB_TX_IPFIL_TBL_STEP 16
+#define FR_BB_TX_IPFIL_TBL_ROWS 16
+#define FRF_BB_TX_IPFIL_MASK_1_LBN 96
+#define FRF_BB_TX_IPFIL_MASK_1_WIDTH 32
+#define FRF_BB_TX_IP_SRC_ADR_1_LBN 64
+#define FRF_BB_TX_IP_SRC_ADR_1_WIDTH 32
+#define FRF_BB_TX_IPFIL_MASK_0_LBN 32
+#define FRF_BB_TX_IPFIL_MASK_0_WIDTH 32
+#define FRF_BB_TX_IP_SRC_ADR_0_LBN 0
+#define FRF_BB_TX_IP_SRC_ADR_0_WIDTH 32
+
+/* MD_TXD_REG: PHY management transmit data register */
+#define FR_AB_MD_TXD 0x00000c00
+#define FRF_AB_MD_TXD_LBN 0
+#define FRF_AB_MD_TXD_WIDTH 16
+
+/* MD_RXD_REG: PHY management receive data register */
+#define FR_AB_MD_RXD 0x00000c10
+#define FRF_AB_MD_RXD_LBN 0
+#define FRF_AB_MD_RXD_WIDTH 16
+
+/* MD_CS_REG: PHY management configuration & status register */
+#define FR_AB_MD_CS 0x00000c20
+#define FRF_AB_MD_RD_EN_CMD_LBN 15
+#define FRF_AB_MD_RD_EN_CMD_WIDTH 1
+#define FRF_AB_MD_WR_EN_CMD_LBN 14
+#define FRF_AB_MD_WR_EN_CMD_WIDTH 1
+#define FRF_AB_MD_ADDR_CMD_LBN 13
+#define FRF_AB_MD_ADDR_CMD_WIDTH 1
+#define FRF_AB_MD_PT_LBN 7
+#define FRF_AB_MD_PT_WIDTH 3
+#define FRF_AB_MD_PL_LBN 6
+#define FRF_AB_MD_PL_WIDTH 1
+#define FRF_AB_MD_INT_CLR_LBN 5
+#define FRF_AB_MD_INT_CLR_WIDTH 1
+#define FRF_AB_MD_GC_LBN 4
+#define FRF_AB_MD_GC_WIDTH 1
+#define FRF_AB_MD_PRSP_LBN 3
+#define FRF_AB_MD_PRSP_WIDTH 1
+#define FRF_AB_MD_RIC_LBN 2
+#define FRF_AB_MD_RIC_WIDTH 1
+#define FRF_AB_MD_RDC_LBN 1
+#define FRF_AB_MD_RDC_WIDTH 1
+#define FRF_AB_MD_WRC_LBN 0
+#define FRF_AB_MD_WRC_WIDTH 1
+
+/* MD_PHY_ADR_REG: PHY management PHY address register */
+#define FR_AB_MD_PHY_ADR 0x00000c30
+#define FRF_AB_MD_PHY_ADR_LBN 0
+#define FRF_AB_MD_PHY_ADR_WIDTH 16
+
+/* MD_ID_REG: PHY management ID register */
+#define FR_AB_MD_ID 0x00000c40
+#define FRF_AB_MD_PRT_ADR_LBN 11
+#define FRF_AB_MD_PRT_ADR_WIDTH 5
+#define FRF_AB_MD_DEV_ADR_LBN 6
+#define FRF_AB_MD_DEV_ADR_WIDTH 5
+
+/* MD_STAT_REG: PHY management status & mask register */
+#define FR_AB_MD_STAT 0x00000c50
+#define FRF_AB_MD_PINT_LBN 4
+#define FRF_AB_MD_PINT_WIDTH 1
+#define FRF_AB_MD_DONE_LBN 3
+#define FRF_AB_MD_DONE_WIDTH 1
+#define FRF_AB_MD_BSERR_LBN 2
+#define FRF_AB_MD_BSERR_WIDTH 1
+#define FRF_AB_MD_LNFL_LBN 1
+#define FRF_AB_MD_LNFL_WIDTH 1
+#define FRF_AB_MD_BSY_LBN 0
+#define FRF_AB_MD_BSY_WIDTH 1
+
+/* MAC_STAT_DMA_REG: Port MAC statistical counter DMA register */
+#define FR_AB_MAC_STAT_DMA 0x00000c60
+#define FRF_AB_MAC_STAT_DMA_CMD_LBN 48
+#define FRF_AB_MAC_STAT_DMA_CMD_WIDTH 1
+#define FRF_AB_MAC_STAT_DMA_ADR_LBN 0
+#define FRF_AB_MAC_STAT_DMA_ADR_WIDTH 48
+
+/* MAC_CTRL_REG: Port MAC control register */
+#define FR_AB_MAC_CTRL 0x00000c80
+#define FRF_AB_MAC_XOFF_VAL_LBN 16
+#define FRF_AB_MAC_XOFF_VAL_WIDTH 16
+#define FRF_BB_TXFIFO_DRAIN_EN_LBN 7
+#define FRF_BB_TXFIFO_DRAIN_EN_WIDTH 1
+#define FRF_AB_MAC_XG_DISTXCRC_LBN 5
+#define FRF_AB_MAC_XG_DISTXCRC_WIDTH 1
+#define FRF_AB_MAC_BCAD_ACPT_LBN 4
+#define FRF_AB_MAC_BCAD_ACPT_WIDTH 1
+#define FRF_AB_MAC_UC_PROM_LBN 3
+#define FRF_AB_MAC_UC_PROM_WIDTH 1
+#define FRF_AB_MAC_LINK_STATUS_LBN 2
+#define FRF_AB_MAC_LINK_STATUS_WIDTH 1
+#define FRF_AB_MAC_SPEED_LBN 0
+#define FRF_AB_MAC_SPEED_WIDTH 2
+#define FFE_AB_MAC_SPEED_10G 3
+#define FFE_AB_MAC_SPEED_1G 2
+#define FFE_AB_MAC_SPEED_100M 1
+#define FFE_AB_MAC_SPEED_10M 0
+
+/* GEN_MODE_REG: General Purpose mode register (external interrupt mask) */
+#define FR_BB_GEN_MODE 0x00000c90
+#define FRF_BB_XFP_PHY_INT_POL_SEL_LBN 3
+#define FRF_BB_XFP_PHY_INT_POL_SEL_WIDTH 1
+#define FRF_BB_XG_PHY_INT_POL_SEL_LBN 2
+#define FRF_BB_XG_PHY_INT_POL_SEL_WIDTH 1
+#define FRF_BB_XFP_PHY_INT_MASK_LBN 1
+#define FRF_BB_XFP_PHY_INT_MASK_WIDTH 1
+#define FRF_BB_XG_PHY_INT_MASK_LBN 0
+#define FRF_BB_XG_PHY_INT_MASK_WIDTH 1
+
+/* MAC_MC_HASH_REG0: Multicast address hash table */
+#define FR_AB_MAC_MC_HASH_REG0 0x00000ca0
+#define FRF_AB_MAC_MCAST_HASH0_LBN 0
+#define FRF_AB_MAC_MCAST_HASH0_WIDTH 128
+
+/* MAC_MC_HASH_REG1: Multicast address hash table */
+#define FR_AB_MAC_MC_HASH_REG1 0x00000cb0
+#define FRF_AB_MAC_MCAST_HASH1_LBN 0
+#define FRF_AB_MAC_MCAST_HASH1_WIDTH 128
+
+/* GM_CFG1_REG: GMAC configuration register 1 */
+#define FR_AB_GM_CFG1 0x00000e00
+#define FRF_AB_GM_SW_RST_LBN 31
+#define FRF_AB_GM_SW_RST_WIDTH 1
+#define FRF_AB_GM_SIM_RST_LBN 30
+#define FRF_AB_GM_SIM_RST_WIDTH 1
+#define FRF_AB_GM_RST_RX_MAC_CTL_LBN 19
+#define FRF_AB_GM_RST_RX_MAC_CTL_WIDTH 1
+#define FRF_AB_GM_RST_TX_MAC_CTL_LBN 18
+#define FRF_AB_GM_RST_TX_MAC_CTL_WIDTH 1
+#define FRF_AB_GM_RST_RX_FUNC_LBN 17
+#define FRF_AB_GM_RST_RX_FUNC_WIDTH 1
+#define FRF_AB_GM_RST_TX_FUNC_LBN 16
+#define FRF_AB_GM_RST_TX_FUNC_WIDTH 1
+#define FRF_AB_GM_LOOP_LBN 8
+#define FRF_AB_GM_LOOP_WIDTH 1
+#define FRF_AB_GM_RX_FC_EN_LBN 5
+#define FRF_AB_GM_RX_FC_EN_WIDTH 1
+#define FRF_AB_GM_TX_FC_EN_LBN 4
+#define FRF_AB_GM_TX_FC_EN_WIDTH 1
+#define FRF_AB_GM_SYNC_RXEN_LBN 3
+#define FRF_AB_GM_SYNC_RXEN_WIDTH 1
+#define FRF_AB_GM_RX_EN_LBN 2
+#define FRF_AB_GM_RX_EN_WIDTH 1
+#define FRF_AB_GM_SYNC_TXEN_LBN 1
+#define FRF_AB_GM_SYNC_TXEN_WIDTH 1
+#define FRF_AB_GM_TX_EN_LBN 0
+#define FRF_AB_GM_TX_EN_WIDTH 1
+
+/* GM_CFG2_REG: GMAC configuration register 2 */
+#define FR_AB_GM_CFG2 0x00000e10
+#define FRF_AB_GM_PAMBL_LEN_LBN 12
+#define FRF_AB_GM_PAMBL_LEN_WIDTH 4
+#define FRF_AB_GM_IF_MODE_LBN 8
+#define FRF_AB_GM_IF_MODE_WIDTH 2
+#define FFE_AB_IF_MODE_BYTE_MODE 2
+#define FFE_AB_IF_MODE_NIBBLE_MODE 1
+#define FRF_AB_GM_HUGE_FRM_EN_LBN 5
+#define FRF_AB_GM_HUGE_FRM_EN_WIDTH 1
+#define FRF_AB_GM_LEN_CHK_LBN 4
+#define FRF_AB_GM_LEN_CHK_WIDTH 1
+#define FRF_AB_GM_PAD_CRC_EN_LBN 2
+#define FRF_AB_GM_PAD_CRC_EN_WIDTH 1
+#define FRF_AB_GM_CRC_EN_LBN 1
+#define FRF_AB_GM_CRC_EN_WIDTH 1
+#define FRF_AB_GM_FD_LBN 0
+#define FRF_AB_GM_FD_WIDTH 1
+
+/* GM_IPG_REG: GMAC IPG register */
+#define FR_AB_GM_IPG 0x00000e20
+#define FRF_AB_GM_NONB2B_IPG1_LBN 24
+#define FRF_AB_GM_NONB2B_IPG1_WIDTH 7
+#define FRF_AB_GM_NONB2B_IPG2_LBN 16
+#define FRF_AB_GM_NONB2B_IPG2_WIDTH 7
+#define FRF_AB_GM_MIN_IPG_ENF_LBN 8
+#define FRF_AB_GM_MIN_IPG_ENF_WIDTH 8
+#define FRF_AB_GM_B2B_IPG_LBN 0
+#define FRF_AB_GM_B2B_IPG_WIDTH 7
+
+/* GM_HD_REG: GMAC half duplex register */
+#define FR_AB_GM_HD 0x00000e30
+#define FRF_AB_GM_ALT_BOFF_VAL_LBN 20
+#define FRF_AB_GM_ALT_BOFF_VAL_WIDTH 4
+#define FRF_AB_GM_ALT_BOFF_EN_LBN 19
+#define FRF_AB_GM_ALT_BOFF_EN_WIDTH 1
+#define FRF_AB_GM_BP_NO_BOFF_LBN 18
+#define FRF_AB_GM_BP_NO_BOFF_WIDTH 1
+#define FRF_AB_GM_DIS_BOFF_LBN 17
+#define FRF_AB_GM_DIS_BOFF_WIDTH 1
+#define FRF_AB_GM_EXDEF_TX_EN_LBN 16
+#define FRF_AB_GM_EXDEF_TX_EN_WIDTH 1
+#define FRF_AB_GM_RTRY_LIMIT_LBN 12
+#define FRF_AB_GM_RTRY_LIMIT_WIDTH 4
+#define FRF_AB_GM_COL_WIN_LBN 0
+#define FRF_AB_GM_COL_WIN_WIDTH 10
+
+/* GM_MAX_FLEN_REG: GMAC maximum frame length register */
+#define FR_AB_GM_MAX_FLEN 0x00000e40
+#define FRF_AB_GM_MAX_FLEN_LBN 0
+#define FRF_AB_GM_MAX_FLEN_WIDTH 16
+
+/* GM_TEST_REG: GMAC test register */
+#define FR_AB_GM_TEST 0x00000e70
+#define FRF_AB_GM_MAX_BOFF_LBN 3
+#define FRF_AB_GM_MAX_BOFF_WIDTH 1
+#define FRF_AB_GM_REG_TX_FLOW_EN_LBN 2
+#define FRF_AB_GM_REG_TX_FLOW_EN_WIDTH 1
+#define FRF_AB_GM_TEST_PAUSE_LBN 1
+#define FRF_AB_GM_TEST_PAUSE_WIDTH 1
+#define FRF_AB_GM_SHORT_SLOT_LBN 0
+#define FRF_AB_GM_SHORT_SLOT_WIDTH 1
+
+/* GM_ADR1_REG: GMAC station address register 1 */
+#define FR_AB_GM_ADR1 0x00000f00
+#define FRF_AB_GM_ADR_B0_LBN 24
+#define FRF_AB_GM_ADR_B0_WIDTH 8
+#define FRF_AB_GM_ADR_B1_LBN 16
+#define FRF_AB_GM_ADR_B1_WIDTH 8
+#define FRF_AB_GM_ADR_B2_LBN 8
+#define FRF_AB_GM_ADR_B2_WIDTH 8
+#define FRF_AB_GM_ADR_B3_LBN 0
+#define FRF_AB_GM_ADR_B3_WIDTH 8
+
+/* GM_ADR2_REG: GMAC station address register 2 */
+#define FR_AB_GM_ADR2 0x00000f10
+#define FRF_AB_GM_ADR_B4_LBN 24
+#define FRF_AB_GM_ADR_B4_WIDTH 8
+#define FRF_AB_GM_ADR_B5_LBN 16
+#define FRF_AB_GM_ADR_B5_WIDTH 8
+
+/* GMF_CFG0_REG: GMAC FIFO configuration register 0 */
+#define FR_AB_GMF_CFG0 0x00000f20
+#define FRF_AB_GMF_FTFENRPLY_LBN 20
+#define FRF_AB_GMF_FTFENRPLY_WIDTH 1
+#define FRF_AB_GMF_STFENRPLY_LBN 19
+#define FRF_AB_GMF_STFENRPLY_WIDTH 1
+#define FRF_AB_GMF_FRFENRPLY_LBN 18
+#define FRF_AB_GMF_FRFENRPLY_WIDTH 1
+#define FRF_AB_GMF_SRFENRPLY_LBN 17
+#define FRF_AB_GMF_SRFENRPLY_WIDTH 1
+#define FRF_AB_GMF_WTMENRPLY_LBN 16
+#define FRF_AB_GMF_WTMENRPLY_WIDTH 1
+#define FRF_AB_GMF_FTFENREQ_LBN 12
+#define FRF_AB_GMF_FTFENREQ_WIDTH 1
+#define FRF_AB_GMF_STFENREQ_LBN 11
+#define FRF_AB_GMF_STFENREQ_WIDTH 1
+#define FRF_AB_GMF_FRFENREQ_LBN 10
+#define FRF_AB_GMF_FRFENREQ_WIDTH 1
+#define FRF_AB_GMF_SRFENREQ_LBN 9
+#define FRF_AB_GMF_SRFENREQ_WIDTH 1
+#define FRF_AB_GMF_WTMENREQ_LBN 8
+#define FRF_AB_GMF_WTMENREQ_WIDTH 1
+#define FRF_AB_GMF_HSTRSTFT_LBN 4
+#define FRF_AB_GMF_HSTRSTFT_WIDTH 1
+#define FRF_AB_GMF_HSTRSTST_LBN 3
+#define FRF_AB_GMF_HSTRSTST_WIDTH 1
+#define FRF_AB_GMF_HSTRSTFR_LBN 2
+#define FRF_AB_GMF_HSTRSTFR_WIDTH 1
+#define FRF_AB_GMF_HSTRSTSR_LBN 1
+#define FRF_AB_GMF_HSTRSTSR_WIDTH 1
+#define FRF_AB_GMF_HSTRSTWT_LBN 0
+#define FRF_AB_GMF_HSTRSTWT_WIDTH 1
+
+/* GMF_CFG1_REG: GMAC FIFO configuration register 1 */
+#define FR_AB_GMF_CFG1 0x00000f30
+#define FRF_AB_GMF_CFGFRTH_LBN 16
+#define FRF_AB_GMF_CFGFRTH_WIDTH 5
+#define FRF_AB_GMF_CFGXOFFRTX_LBN 0
+#define FRF_AB_GMF_CFGXOFFRTX_WIDTH 16
+
+/* GMF_CFG2_REG: GMAC FIFO configuration register 2 */
+#define FR_AB_GMF_CFG2 0x00000f40
+#define FRF_AB_GMF_CFGHWM_LBN 16
+#define FRF_AB_GMF_CFGHWM_WIDTH 6
+#define FRF_AB_GMF_CFGLWM_LBN 0
+#define FRF_AB_GMF_CFGLWM_WIDTH 6
+
+/* GMF_CFG3_REG: GMAC FIFO configuration register 3 */
+#define FR_AB_GMF_CFG3 0x00000f50
+#define FRF_AB_GMF_CFGHWMFT_LBN 16
+#define FRF_AB_GMF_CFGHWMFT_WIDTH 6
+#define FRF_AB_GMF_CFGFTTH_LBN 0
+#define FRF_AB_GMF_CFGFTTH_WIDTH 6
+
+/* GMF_CFG4_REG: GMAC FIFO configuration register 4 */
+#define FR_AB_GMF_CFG4 0x00000f60
+#define FRF_AB_GMF_HSTFLTRFRM_LBN 0
+#define FRF_AB_GMF_HSTFLTRFRM_WIDTH 18
+
+/* GMF_CFG5_REG: GMAC FIFO configuration register 5 */
+#define FR_AB_GMF_CFG5 0x00000f70
+#define FRF_AB_GMF_CFGHDPLX_LBN 22
+#define FRF_AB_GMF_CFGHDPLX_WIDTH 1
+#define FRF_AB_GMF_SRFULL_LBN 21
+#define FRF_AB_GMF_SRFULL_WIDTH 1
+#define FRF_AB_GMF_HSTSRFULLCLR_LBN 20
+#define FRF_AB_GMF_HSTSRFULLCLR_WIDTH 1
+#define FRF_AB_GMF_CFGBYTMODE_LBN 19
+#define FRF_AB_GMF_CFGBYTMODE_WIDTH 1
+#define FRF_AB_GMF_HSTDRPLT64_LBN 18
+#define FRF_AB_GMF_HSTDRPLT64_WIDTH 1
+#define FRF_AB_GMF_HSTFLTRFRMDC_LBN 0
+#define FRF_AB_GMF_HSTFLTRFRMDC_WIDTH 18
+
+/* TX_SRC_MAC_TBL: Transmit IP source address filter table */
+#define FR_BB_TX_SRC_MAC_TBL 0x00001000
+#define FR_BB_TX_SRC_MAC_TBL_STEP 16
+#define FR_BB_TX_SRC_MAC_TBL_ROWS 16
+#define FRF_BB_TX_SRC_MAC_ADR_1_LBN 64
+#define FRF_BB_TX_SRC_MAC_ADR_1_WIDTH 48
+#define FRF_BB_TX_SRC_MAC_ADR_0_LBN 0
+#define FRF_BB_TX_SRC_MAC_ADR_0_WIDTH 48
+
+/* TX_SRC_MAC_CTL_REG: Transmit MAC source address filter control */
+#define FR_BB_TX_SRC_MAC_CTL 0x00001100
+#define FRF_BB_TX_SRC_DROP_CTR_LBN 16
+#define FRF_BB_TX_SRC_DROP_CTR_WIDTH 16
+#define FRF_BB_TX_SRC_FLTR_EN_LBN 15
+#define FRF_BB_TX_SRC_FLTR_EN_WIDTH 1
+#define FRF_BB_TX_DROP_CTR_CLR_LBN 12
+#define FRF_BB_TX_DROP_CTR_CLR_WIDTH 1
+#define FRF_BB_TX_MAC_QID_SEL_LBN 0
+#define FRF_BB_TX_MAC_QID_SEL_WIDTH 3
+
+/* XM_ADR_LO_REG: XGMAC address register low */
+#define FR_AB_XM_ADR_LO 0x00001200
+#define FRF_AB_XM_ADR_LO_LBN 0
+#define FRF_AB_XM_ADR_LO_WIDTH 32
+
+/* XM_ADR_HI_REG: XGMAC address register high */
+#define FR_AB_XM_ADR_HI 0x00001210
+#define FRF_AB_XM_ADR_HI_LBN 0
+#define FRF_AB_XM_ADR_HI_WIDTH 16
+
+/* XM_GLB_CFG_REG: XGMAC global configuration */
+#define FR_AB_XM_GLB_CFG 0x00001220
+#define FRF_AB_XM_RMTFLT_GEN_LBN 17
+#define FRF_AB_XM_RMTFLT_GEN_WIDTH 1
+#define FRF_AB_XM_DEBUG_MODE_LBN 16
+#define FRF_AB_XM_DEBUG_MODE_WIDTH 1
+#define FRF_AB_XM_RX_STAT_EN_LBN 11
+#define FRF_AB_XM_RX_STAT_EN_WIDTH 1
+#define FRF_AB_XM_TX_STAT_EN_LBN 10
+#define FRF_AB_XM_TX_STAT_EN_WIDTH 1
+#define FRF_AB_XM_RX_JUMBO_MODE_LBN 6
+#define FRF_AB_XM_RX_JUMBO_MODE_WIDTH 1
+#define FRF_AB_XM_WAN_MODE_LBN 5
+#define FRF_AB_XM_WAN_MODE_WIDTH 1
+#define FRF_AB_XM_INTCLR_MODE_LBN 3
+#define FRF_AB_XM_INTCLR_MODE_WIDTH 1
+#define FRF_AB_XM_CORE_RST_LBN 0
+#define FRF_AB_XM_CORE_RST_WIDTH 1
+
+/* XM_TX_CFG_REG: XGMAC transmit configuration */
+#define FR_AB_XM_TX_CFG 0x00001230
+#define FRF_AB_XM_TX_PROG_LBN 24
+#define FRF_AB_XM_TX_PROG_WIDTH 1
+#define FRF_AB_XM_IPG_LBN 16
+#define FRF_AB_XM_IPG_WIDTH 4
+#define FRF_AB_XM_FCNTL_LBN 10
+#define FRF_AB_XM_FCNTL_WIDTH 1
+#define FRF_AB_XM_TXCRC_LBN 8
+#define FRF_AB_XM_TXCRC_WIDTH 1
+#define FRF_AB_XM_EDRC_LBN 6
+#define FRF_AB_XM_EDRC_WIDTH 1
+#define FRF_AB_XM_AUTO_PAD_LBN 5
+#define FRF_AB_XM_AUTO_PAD_WIDTH 1
+#define FRF_AB_XM_TX_PRMBL_LBN 2
+#define FRF_AB_XM_TX_PRMBL_WIDTH 1
+#define FRF_AB_XM_TXEN_LBN 1
+#define FRF_AB_XM_TXEN_WIDTH 1
+#define FRF_AB_XM_TX_RST_LBN 0
+#define FRF_AB_XM_TX_RST_WIDTH 1
+
+/* XM_RX_CFG_REG: XGMAC receive configuration */
+#define FR_AB_XM_RX_CFG 0x00001240
+#define FRF_AB_XM_PASS_LENERR_LBN 26
+#define FRF_AB_XM_PASS_LENERR_WIDTH 1
+#define FRF_AB_XM_PASS_CRC_ERR_LBN 25
+#define FRF_AB_XM_PASS_CRC_ERR_WIDTH 1
+#define FRF_AB_XM_PASS_PRMBLE_ERR_LBN 24
+#define FRF_AB_XM_PASS_PRMBLE_ERR_WIDTH 1
+#define FRF_AB_XM_REJ_BCAST_LBN 20
+#define FRF_AB_XM_REJ_BCAST_WIDTH 1
+#define FRF_AB_XM_ACPT_ALL_MCAST_LBN 11
+#define FRF_AB_XM_ACPT_ALL_MCAST_WIDTH 1
+#define FRF_AB_XM_ACPT_ALL_UCAST_LBN 9
+#define FRF_AB_XM_ACPT_ALL_UCAST_WIDTH 1
+#define FRF_AB_XM_AUTO_DEPAD_LBN 8
+#define FRF_AB_XM_AUTO_DEPAD_WIDTH 1
+#define FRF_AB_XM_RXCRC_LBN 3
+#define FRF_AB_XM_RXCRC_WIDTH 1
+#define FRF_AB_XM_RX_PRMBL_LBN 2
+#define FRF_AB_XM_RX_PRMBL_WIDTH 1
+#define FRF_AB_XM_RXEN_LBN 1
+#define FRF_AB_XM_RXEN_WIDTH 1
+#define FRF_AB_XM_RX_RST_LBN 0
+#define FRF_AB_XM_RX_RST_WIDTH 1
+
+/* XM_MGT_INT_MASK: documentation to be written for sum_XM_MGT_INT_MASK */
+#define FR_AB_XM_MGT_INT_MASK 0x00001250
+#define FRF_AB_XM_MSK_STA_INTR_LBN 16
+#define FRF_AB_XM_MSK_STA_INTR_WIDTH 1
+#define FRF_AB_XM_MSK_STAT_CNTR_HF_LBN 9
+#define FRF_AB_XM_MSK_STAT_CNTR_HF_WIDTH 1
+#define FRF_AB_XM_MSK_STAT_CNTR_OF_LBN 8
+#define FRF_AB_XM_MSK_STAT_CNTR_OF_WIDTH 1
+#define FRF_AB_XM_MSK_PRMBLE_ERR_LBN 2
+#define FRF_AB_XM_MSK_PRMBLE_ERR_WIDTH 1
+#define FRF_AB_XM_MSK_RMTFLT_LBN 1
+#define FRF_AB_XM_MSK_RMTFLT_WIDTH 1
+#define FRF_AB_XM_MSK_LCLFLT_LBN 0
+#define FRF_AB_XM_MSK_LCLFLT_WIDTH 1
+
+/* XM_FC_REG: XGMAC flow control register */
+#define FR_AB_XM_FC 0x00001270
+#define FRF_AB_XM_PAUSE_TIME_LBN 16
+#define FRF_AB_XM_PAUSE_TIME_WIDTH 16
+#define FRF_AB_XM_RX_MAC_STAT_LBN 11
+#define FRF_AB_XM_RX_MAC_STAT_WIDTH 1
+#define FRF_AB_XM_TX_MAC_STAT_LBN 10
+#define FRF_AB_XM_TX_MAC_STAT_WIDTH 1
+#define FRF_AB_XM_MCNTL_PASS_LBN 8
+#define FRF_AB_XM_MCNTL_PASS_WIDTH 2
+#define FRF_AB_XM_REJ_CNTL_UCAST_LBN 6
+#define FRF_AB_XM_REJ_CNTL_UCAST_WIDTH 1
+#define FRF_AB_XM_REJ_CNTL_MCAST_LBN 5
+#define FRF_AB_XM_REJ_CNTL_MCAST_WIDTH 1
+#define FRF_AB_XM_ZPAUSE_LBN 2
+#define FRF_AB_XM_ZPAUSE_WIDTH 1
+#define FRF_AB_XM_XMIT_PAUSE_LBN 1
+#define FRF_AB_XM_XMIT_PAUSE_WIDTH 1
+#define FRF_AB_XM_DIS_FCNTL_LBN 0
+#define FRF_AB_XM_DIS_FCNTL_WIDTH 1
+
+/* XM_PAUSE_TIME_REG: XGMAC pause time register */
+#define FR_AB_XM_PAUSE_TIME 0x00001290
+#define FRF_AB_XM_TX_PAUSE_CNT_LBN 16
+#define FRF_AB_XM_TX_PAUSE_CNT_WIDTH 16
+#define FRF_AB_XM_RX_PAUSE_CNT_LBN 0
+#define FRF_AB_XM_RX_PAUSE_CNT_WIDTH 16
+
+/* XM_TX_PARAM_REG: XGMAC transmit parameter register */
+#define FR_AB_XM_TX_PARAM 0x000012d0
+#define FRF_AB_XM_TX_JUMBO_MODE_LBN 31
+#define FRF_AB_XM_TX_JUMBO_MODE_WIDTH 1
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_LBN 19
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_WIDTH 11
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_LBN 16
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_WIDTH 3
+#define FRF_AB_XM_PAD_CHAR_LBN 0
+#define FRF_AB_XM_PAD_CHAR_WIDTH 8
+
+/* XM_RX_PARAM_REG: XGMAC receive parameter register */
+#define FR_AB_XM_RX_PARAM 0x000012e0
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_LBN 3
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_WIDTH 11
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_LBN 0
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_WIDTH 3
+
+/* XM_MGT_INT_MSK_REG: XGMAC management interrupt mask register */
+#define FR_AB_XM_MGT_INT_MSK 0x000012f0
+#define FRF_AB_XM_STAT_CNTR_OF_LBN 9
+#define FRF_AB_XM_STAT_CNTR_OF_WIDTH 1
+#define FRF_AB_XM_STAT_CNTR_HF_LBN 8
+#define FRF_AB_XM_STAT_CNTR_HF_WIDTH 1
+#define FRF_AB_XM_PRMBLE_ERR_LBN 2
+#define FRF_AB_XM_PRMBLE_ERR_WIDTH 1
+#define FRF_AB_XM_RMTFLT_LBN 1
+#define FRF_AB_XM_RMTFLT_WIDTH 1
+#define FRF_AB_XM_LCLFLT_LBN 0
+#define FRF_AB_XM_LCLFLT_WIDTH 1
+
+/* XX_PWR_RST_REG: XGXS/XAUI powerdown/reset register */
+#define FR_AB_XX_PWR_RST 0x00001300
+#define FRF_AB_XX_PWRDND_SIG_LBN 31
+#define FRF_AB_XX_PWRDND_SIG_WIDTH 1
+#define FRF_AB_XX_PWRDNC_SIG_LBN 30
+#define FRF_AB_XX_PWRDNC_SIG_WIDTH 1
+#define FRF_AB_XX_PWRDNB_SIG_LBN 29
+#define FRF_AB_XX_PWRDNB_SIG_WIDTH 1
+#define FRF_AB_XX_PWRDNA_SIG_LBN 28
+#define FRF_AB_XX_PWRDNA_SIG_WIDTH 1
+#define FRF_AB_XX_SIM_MODE_LBN 27
+#define FRF_AB_XX_SIM_MODE_WIDTH 1
+#define FRF_AB_XX_RSTPLLCD_SIG_LBN 25
+#define FRF_AB_XX_RSTPLLCD_SIG_WIDTH 1
+#define FRF_AB_XX_RSTPLLAB_SIG_LBN 24
+#define FRF_AB_XX_RSTPLLAB_SIG_WIDTH 1
+#define FRF_AB_XX_RESETD_SIG_LBN 23
+#define FRF_AB_XX_RESETD_SIG_WIDTH 1
+#define FRF_AB_XX_RESETC_SIG_LBN 22
+#define FRF_AB_XX_RESETC_SIG_WIDTH 1
+#define FRF_AB_XX_RESETB_SIG_LBN 21
+#define FRF_AB_XX_RESETB_SIG_WIDTH 1
+#define FRF_AB_XX_RESETA_SIG_LBN 20
+#define FRF_AB_XX_RESETA_SIG_WIDTH 1
+#define FRF_AB_XX_RSTXGXSRX_SIG_LBN 18
+#define FRF_AB_XX_RSTXGXSRX_SIG_WIDTH 1
+#define FRF_AB_XX_RSTXGXSTX_SIG_LBN 17
+#define FRF_AB_XX_RSTXGXSTX_SIG_WIDTH 1
+#define FRF_AB_XX_SD_RST_ACT_LBN 16
+#define FRF_AB_XX_SD_RST_ACT_WIDTH 1
+#define FRF_AB_XX_PWRDND_EN_LBN 15
+#define FRF_AB_XX_PWRDND_EN_WIDTH 1
+#define FRF_AB_XX_PWRDNC_EN_LBN 14
+#define FRF_AB_XX_PWRDNC_EN_WIDTH 1
+#define FRF_AB_XX_PWRDNB_EN_LBN 13
+#define FRF_AB_XX_PWRDNB_EN_WIDTH 1
+#define FRF_AB_XX_PWRDNA_EN_LBN 12
+#define FRF_AB_XX_PWRDNA_EN_WIDTH 1
+#define FRF_AB_XX_RSTPLLCD_EN_LBN 9
+#define FRF_AB_XX_RSTPLLCD_EN_WIDTH 1
+#define FRF_AB_XX_RSTPLLAB_EN_LBN 8
+#define FRF_AB_XX_RSTPLLAB_EN_WIDTH 1
+#define FRF_AB_XX_RESETD_EN_LBN 7
+#define FRF_AB_XX_RESETD_EN_WIDTH 1
+#define FRF_AB_XX_RESETC_EN_LBN 6
+#define FRF_AB_XX_RESETC_EN_WIDTH 1
+#define FRF_AB_XX_RESETB_EN_LBN 5
+#define FRF_AB_XX_RESETB_EN_WIDTH 1
+#define FRF_AB_XX_RESETA_EN_LBN 4
+#define FRF_AB_XX_RESETA_EN_WIDTH 1
+#define FRF_AB_XX_RSTXGXSRX_EN_LBN 2
+#define FRF_AB_XX_RSTXGXSRX_EN_WIDTH 1
+#define FRF_AB_XX_RSTXGXSTX_EN_LBN 1
+#define FRF_AB_XX_RSTXGXSTX_EN_WIDTH 1
+#define FRF_AB_XX_RST_XX_EN_LBN 0
+#define FRF_AB_XX_RST_XX_EN_WIDTH 1
+
+/* XX_SD_CTL_REG: XGXS/XAUI powerdown/reset control register */
+#define FR_AB_XX_SD_CTL 0x00001310
+#define FRF_AB_XX_TERMADJ1_LBN 17
+#define FRF_AB_XX_TERMADJ1_WIDTH 1
+#define FRF_AB_XX_TERMADJ0_LBN 16
+#define FRF_AB_XX_TERMADJ0_WIDTH 1
+#define FRF_AB_XX_HIDRVD_LBN 15
+#define FRF_AB_XX_HIDRVD_WIDTH 1
+#define FRF_AB_XX_LODRVD_LBN 14
+#define FRF_AB_XX_LODRVD_WIDTH 1
+#define FRF_AB_XX_HIDRVC_LBN 13
+#define FRF_AB_XX_HIDRVC_WIDTH 1
+#define FRF_AB_XX_LODRVC_LBN 12
+#define FRF_AB_XX_LODRVC_WIDTH 1
+#define FRF_AB_XX_HIDRVB_LBN 11
+#define FRF_AB_XX_HIDRVB_WIDTH 1
+#define FRF_AB_XX_LODRVB_LBN 10
+#define FRF_AB_XX_LODRVB_WIDTH 1
+#define FRF_AB_XX_HIDRVA_LBN 9
+#define FRF_AB_XX_HIDRVA_WIDTH 1
+#define FRF_AB_XX_LODRVA_LBN 8
+#define FRF_AB_XX_LODRVA_WIDTH 1
+#define FRF_AB_XX_LPBKD_LBN 3
+#define FRF_AB_XX_LPBKD_WIDTH 1
+#define FRF_AB_XX_LPBKC_LBN 2
+#define FRF_AB_XX_LPBKC_WIDTH 1
+#define FRF_AB_XX_LPBKB_LBN 1
+#define FRF_AB_XX_LPBKB_WIDTH 1
+#define FRF_AB_XX_LPBKA_LBN 0
+#define FRF_AB_XX_LPBKA_WIDTH 1
+
+/* XX_TXDRV_CTL_REG: XAUI SerDes transmit drive control register */
+#define FR_AB_XX_TXDRV_CTL 0x00001320
+#define FRF_AB_XX_DEQD_LBN 28
+#define FRF_AB_XX_DEQD_WIDTH 4
+#define FRF_AB_XX_DEQC_LBN 24
+#define FRF_AB_XX_DEQC_WIDTH 4
+#define FRF_AB_XX_DEQB_LBN 20
+#define FRF_AB_XX_DEQB_WIDTH 4
+#define FRF_AB_XX_DEQA_LBN 16
+#define FRF_AB_XX_DEQA_WIDTH 4
+#define FRF_AB_XX_DTXD_LBN 12
+#define FRF_AB_XX_DTXD_WIDTH 4
+#define FRF_AB_XX_DTXC_LBN 8
+#define FRF_AB_XX_DTXC_WIDTH 4
+#define FRF_AB_XX_DTXB_LBN 4
+#define FRF_AB_XX_DTXB_WIDTH 4
+#define FRF_AB_XX_DTXA_LBN 0
+#define FRF_AB_XX_DTXA_WIDTH 4
+
+/* XX_PRBS_CTL_REG: documentation to be written for sum_XX_PRBS_CTL_REG */
+#define FR_AB_XX_PRBS_CTL 0x00001330
+#define FRF_AB_XX_CH3_RX_PRBS_SEL_LBN 30
+#define FRF_AB_XX_CH3_RX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH3_RX_PRBS_INV_LBN 29
+#define FRF_AB_XX_CH3_RX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_LBN 28
+#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH2_RX_PRBS_SEL_LBN 26
+#define FRF_AB_XX_CH2_RX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH2_RX_PRBS_INV_LBN 25
+#define FRF_AB_XX_CH2_RX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_LBN 24
+#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH1_RX_PRBS_SEL_LBN 22
+#define FRF_AB_XX_CH1_RX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH1_RX_PRBS_INV_LBN 21
+#define FRF_AB_XX_CH1_RX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_LBN 20
+#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH0_RX_PRBS_SEL_LBN 18
+#define FRF_AB_XX_CH0_RX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH0_RX_PRBS_INV_LBN 17
+#define FRF_AB_XX_CH0_RX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_LBN 16
+#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH3_TX_PRBS_SEL_LBN 14
+#define FRF_AB_XX_CH3_TX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH3_TX_PRBS_INV_LBN 13
+#define FRF_AB_XX_CH3_TX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_LBN 12
+#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH2_TX_PRBS_SEL_LBN 10
+#define FRF_AB_XX_CH2_TX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH2_TX_PRBS_INV_LBN 9
+#define FRF_AB_XX_CH2_TX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_LBN 8
+#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH1_TX_PRBS_SEL_LBN 6
+#define FRF_AB_XX_CH1_TX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH1_TX_PRBS_INV_LBN 5
+#define FRF_AB_XX_CH1_TX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_LBN 4
+#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH0_TX_PRBS_SEL_LBN 2
+#define FRF_AB_XX_CH0_TX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH0_TX_PRBS_INV_LBN 1
+#define FRF_AB_XX_CH0_TX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_LBN 0
+#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_WIDTH 1
+
+/* XX_PRBS_CHK_REG: documentation to be written for sum_XX_PRBS_CHK_REG */
+#define FR_AB_XX_PRBS_CHK 0x00001340
+#define FRF_AB_XX_REV_LB_EN_LBN 16
+#define FRF_AB_XX_REV_LB_EN_WIDTH 1
+#define FRF_AB_XX_CH3_DEG_DET_LBN 15
+#define FRF_AB_XX_CH3_DEG_DET_WIDTH 1
+#define FRF_AB_XX_CH3_LFSR_LOCK_IND_LBN 14
+#define FRF_AB_XX_CH3_LFSR_LOCK_IND_WIDTH 1
+#define FRF_AB_XX_CH3_PRBS_FRUN_LBN 13
+#define FRF_AB_XX_CH3_PRBS_FRUN_WIDTH 1
+#define FRF_AB_XX_CH3_ERR_CHK_LBN 12
+#define FRF_AB_XX_CH3_ERR_CHK_WIDTH 1
+#define FRF_AB_XX_CH2_DEG_DET_LBN 11
+#define FRF_AB_XX_CH2_DEG_DET_WIDTH 1
+#define FRF_AB_XX_CH2_LFSR_LOCK_IND_LBN 10
+#define FRF_AB_XX_CH2_LFSR_LOCK_IND_WIDTH 1
+#define FRF_AB_XX_CH2_PRBS_FRUN_LBN 9
+#define FRF_AB_XX_CH2_PRBS_FRUN_WIDTH 1
+#define FRF_AB_XX_CH2_ERR_CHK_LBN 8
+#define FRF_AB_XX_CH2_ERR_CHK_WIDTH 1
+#define FRF_AB_XX_CH1_DEG_DET_LBN 7
+#define FRF_AB_XX_CH1_DEG_DET_WIDTH 1
+#define FRF_AB_XX_CH1_LFSR_LOCK_IND_LBN 6
+#define FRF_AB_XX_CH1_LFSR_LOCK_IND_WIDTH 1
+#define FRF_AB_XX_CH1_PRBS_FRUN_LBN 5
+#define FRF_AB_XX_CH1_PRBS_FRUN_WIDTH 1
+#define FRF_AB_XX_CH1_ERR_CHK_LBN 4
+#define FRF_AB_XX_CH1_ERR_CHK_WIDTH 1
+#define FRF_AB_XX_CH0_DEG_DET_LBN 3
+#define FRF_AB_XX_CH0_DEG_DET_WIDTH 1
+#define FRF_AB_XX_CH0_LFSR_LOCK_IND_LBN 2
+#define FRF_AB_XX_CH0_LFSR_LOCK_IND_WIDTH 1
+#define FRF_AB_XX_CH0_PRBS_FRUN_LBN 1
+#define FRF_AB_XX_CH0_PRBS_FRUN_WIDTH 1
+#define FRF_AB_XX_CH0_ERR_CHK_LBN 0
+#define FRF_AB_XX_CH0_ERR_CHK_WIDTH 1
+
+/* XX_PRBS_ERR_REG: documentation to be written for sum_XX_PRBS_ERR_REG */
+#define FR_AB_XX_PRBS_ERR 0x00001350
+#define FRF_AB_XX_CH3_PRBS_ERR_CNT_LBN 24
+#define FRF_AB_XX_CH3_PRBS_ERR_CNT_WIDTH 8
+#define FRF_AB_XX_CH2_PRBS_ERR_CNT_LBN 16
+#define FRF_AB_XX_CH2_PRBS_ERR_CNT_WIDTH 8
+#define FRF_AB_XX_CH1_PRBS_ERR_CNT_LBN 8
+#define FRF_AB_XX_CH1_PRBS_ERR_CNT_WIDTH 8
+#define FRF_AB_XX_CH0_PRBS_ERR_CNT_LBN 0
+#define FRF_AB_XX_CH0_PRBS_ERR_CNT_WIDTH 8
+
+/* XX_CORE_STAT_REG: XAUI XGXS core status register */
+#define FR_AB_XX_CORE_STAT 0x00001360
+#define FRF_AB_XX_FORCE_SIG3_LBN 31
+#define FRF_AB_XX_FORCE_SIG3_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG3_VAL_LBN 30
+#define FRF_AB_XX_FORCE_SIG3_VAL_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG2_LBN 29
+#define FRF_AB_XX_FORCE_SIG2_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG2_VAL_LBN 28
+#define FRF_AB_XX_FORCE_SIG2_VAL_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG1_LBN 27
+#define FRF_AB_XX_FORCE_SIG1_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG1_VAL_LBN 26
+#define FRF_AB_XX_FORCE_SIG1_VAL_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG0_LBN 25
+#define FRF_AB_XX_FORCE_SIG0_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG0_VAL_LBN 24
+#define FRF_AB_XX_FORCE_SIG0_VAL_WIDTH 1
+#define FRF_AB_XX_XGXS_LB_EN_LBN 23
+#define FRF_AB_XX_XGXS_LB_EN_WIDTH 1
+#define FRF_AB_XX_XGMII_LB_EN_LBN 22
+#define FRF_AB_XX_XGMII_LB_EN_WIDTH 1
+#define FRF_AB_XX_MATCH_FAULT_LBN 21
+#define FRF_AB_XX_MATCH_FAULT_WIDTH 1
+#define FRF_AB_XX_ALIGN_DONE_LBN 20
+#define FRF_AB_XX_ALIGN_DONE_WIDTH 1
+#define FRF_AB_XX_SYNC_STAT3_LBN 19
+#define FRF_AB_XX_SYNC_STAT3_WIDTH 1
+#define FRF_AB_XX_SYNC_STAT2_LBN 18
+#define FRF_AB_XX_SYNC_STAT2_WIDTH 1
+#define FRF_AB_XX_SYNC_STAT1_LBN 17
+#define FRF_AB_XX_SYNC_STAT1_WIDTH 1
+#define FRF_AB_XX_SYNC_STAT0_LBN 16
+#define FRF_AB_XX_SYNC_STAT0_WIDTH 1
+#define FRF_AB_XX_COMMA_DET_CH3_LBN 15
+#define FRF_AB_XX_COMMA_DET_CH3_WIDTH 1
+#define FRF_AB_XX_COMMA_DET_CH2_LBN 14
+#define FRF_AB_XX_COMMA_DET_CH2_WIDTH 1
+#define FRF_AB_XX_COMMA_DET_CH1_LBN 13
+#define FRF_AB_XX_COMMA_DET_CH1_WIDTH 1
+#define FRF_AB_XX_COMMA_DET_CH0_LBN 12
+#define FRF_AB_XX_COMMA_DET_CH0_WIDTH 1
+#define FRF_AB_XX_CGRP_ALIGN_CH3_LBN 11
+#define FRF_AB_XX_CGRP_ALIGN_CH3_WIDTH 1
+#define FRF_AB_XX_CGRP_ALIGN_CH2_LBN 10
+#define FRF_AB_XX_CGRP_ALIGN_CH2_WIDTH 1
+#define FRF_AB_XX_CGRP_ALIGN_CH1_LBN 9
+#define FRF_AB_XX_CGRP_ALIGN_CH1_WIDTH 1
+#define FRF_AB_XX_CGRP_ALIGN_CH0_LBN 8
+#define FRF_AB_XX_CGRP_ALIGN_CH0_WIDTH 1
+#define FRF_AB_XX_CHAR_ERR_CH3_LBN 7
+#define FRF_AB_XX_CHAR_ERR_CH3_WIDTH 1
+#define FRF_AB_XX_CHAR_ERR_CH2_LBN 6
+#define FRF_AB_XX_CHAR_ERR_CH2_WIDTH 1
+#define FRF_AB_XX_CHAR_ERR_CH1_LBN 5
+#define FRF_AB_XX_CHAR_ERR_CH1_WIDTH 1
+#define FRF_AB_XX_CHAR_ERR_CH0_LBN 4
+#define FRF_AB_XX_CHAR_ERR_CH0_WIDTH 1
+#define FRF_AB_XX_DISPERR_CH3_LBN 3
+#define FRF_AB_XX_DISPERR_CH3_WIDTH 1
+#define FRF_AB_XX_DISPERR_CH2_LBN 2
+#define FRF_AB_XX_DISPERR_CH2_WIDTH 1
+#define FRF_AB_XX_DISPERR_CH1_LBN 1
+#define FRF_AB_XX_DISPERR_CH1_WIDTH 1
+#define FRF_AB_XX_DISPERR_CH0_LBN 0
+#define FRF_AB_XX_DISPERR_CH0_WIDTH 1
+
+/* RX_DESC_PTR_TBL_KER: Receive descriptor pointer table */
+#define FR_AA_RX_DESC_PTR_TBL_KER 0x00011800
+#define FR_AA_RX_DESC_PTR_TBL_KER_STEP 16
+#define FR_AA_RX_DESC_PTR_TBL_KER_ROWS 4
+/* RX_DESC_PTR_TBL: Receive descriptor pointer table */
+#define FR_BZ_RX_DESC_PTR_TBL 0x00f40000
+#define FR_BZ_RX_DESC_PTR_TBL_STEP 16
+#define FR_BB_RX_DESC_PTR_TBL_ROWS 4096
+#define FR_CZ_RX_DESC_PTR_TBL_ROWS 1024
+#define FRF_CZ_RX_HDR_SPLIT_LBN 90
+#define FRF_CZ_RX_HDR_SPLIT_WIDTH 1
+#define FRF_AA_RX_RESET_LBN 89
+#define FRF_AA_RX_RESET_WIDTH 1
+#define FRF_AZ_RX_ISCSI_DDIG_EN_LBN 88
+#define FRF_AZ_RX_ISCSI_DDIG_EN_WIDTH 1
+#define FRF_AZ_RX_ISCSI_HDIG_EN_LBN 87
+#define FRF_AZ_RX_ISCSI_HDIG_EN_WIDTH 1
+#define FRF_AZ_RX_DESC_PREF_ACT_LBN 86
+#define FRF_AZ_RX_DESC_PREF_ACT_WIDTH 1
+#define FRF_AZ_RX_DC_HW_RPTR_LBN 80
+#define FRF_AZ_RX_DC_HW_RPTR_WIDTH 6
+#define FRF_AZ_RX_DESCQ_HW_RPTR_LBN 68
+#define FRF_AZ_RX_DESCQ_HW_RPTR_WIDTH 12
+#define FRF_AZ_RX_DESCQ_SW_WPTR_LBN 56
+#define FRF_AZ_RX_DESCQ_SW_WPTR_WIDTH 12
+#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_LBN 36
+#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_WIDTH 20
+#define FRF_AZ_RX_DESCQ_EVQ_ID_LBN 24
+#define FRF_AZ_RX_DESCQ_EVQ_ID_WIDTH 12
+#define FRF_AZ_RX_DESCQ_OWNER_ID_LBN 10
+#define FRF_AZ_RX_DESCQ_OWNER_ID_WIDTH 14
+#define FRF_AZ_RX_DESCQ_LABEL_LBN 5
+#define FRF_AZ_RX_DESCQ_LABEL_WIDTH 5
+#define FRF_AZ_RX_DESCQ_SIZE_LBN 3
+#define FRF_AZ_RX_DESCQ_SIZE_WIDTH 2
+#define FFE_AZ_RX_DESCQ_SIZE_4K 3
+#define FFE_AZ_RX_DESCQ_SIZE_2K 2
+#define FFE_AZ_RX_DESCQ_SIZE_1K 1
+#define FFE_AZ_RX_DESCQ_SIZE_512 0
+#define FRF_AZ_RX_DESCQ_TYPE_LBN 2
+#define FRF_AZ_RX_DESCQ_TYPE_WIDTH 1
+#define FRF_AZ_RX_DESCQ_JUMBO_LBN 1
+#define FRF_AZ_RX_DESCQ_JUMBO_WIDTH 1
+#define FRF_AZ_RX_DESCQ_EN_LBN 0
+#define FRF_AZ_RX_DESCQ_EN_WIDTH 1
+
+/* TX_DESC_PTR_TBL_KER: Transmit descriptor pointer */
+#define FR_AA_TX_DESC_PTR_TBL_KER 0x00011900
+#define FR_AA_TX_DESC_PTR_TBL_KER_STEP 16
+#define FR_AA_TX_DESC_PTR_TBL_KER_ROWS 8
+/* TX_DESC_PTR_TBL: Transmit descriptor pointer */
+#define FR_BZ_TX_DESC_PTR_TBL 0x00f50000
+#define FR_BZ_TX_DESC_PTR_TBL_STEP 16
+#define FR_BB_TX_DESC_PTR_TBL_ROWS 4096
+#define FR_CZ_TX_DESC_PTR_TBL_ROWS 1024
+#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_LBN 94
+#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_WIDTH 2
+#define FRF_CZ_TX_DPT_ETH_FILT_EN_LBN 93
+#define FRF_CZ_TX_DPT_ETH_FILT_EN_WIDTH 1
+#define FRF_CZ_TX_DPT_IP_FILT_EN_LBN 92
+#define FRF_CZ_TX_DPT_IP_FILT_EN_WIDTH 1
+#define FRF_BZ_TX_NON_IP_DROP_DIS_LBN 91
+#define FRF_BZ_TX_NON_IP_DROP_DIS_WIDTH 1
+#define FRF_BZ_TX_IP_CHKSM_DIS_LBN 90
+#define FRF_BZ_TX_IP_CHKSM_DIS_WIDTH 1
+#define FRF_BZ_TX_TCP_CHKSM_DIS_LBN 89
+#define FRF_BZ_TX_TCP_CHKSM_DIS_WIDTH 1
+#define FRF_AZ_TX_DESCQ_EN_LBN 88
+#define FRF_AZ_TX_DESCQ_EN_WIDTH 1
+#define FRF_AZ_TX_ISCSI_DDIG_EN_LBN 87
+#define FRF_AZ_TX_ISCSI_DDIG_EN_WIDTH 1
+#define FRF_AZ_TX_ISCSI_HDIG_EN_LBN 86
+#define FRF_AZ_TX_ISCSI_HDIG_EN_WIDTH 1
+#define FRF_AZ_TX_DC_HW_RPTR_LBN 80
+#define FRF_AZ_TX_DC_HW_RPTR_WIDTH 6
+#define FRF_AZ_TX_DESCQ_HW_RPTR_LBN 68
+#define FRF_AZ_TX_DESCQ_HW_RPTR_WIDTH 12
+#define FRF_AZ_TX_DESCQ_SW_WPTR_LBN 56
+#define FRF_AZ_TX_DESCQ_SW_WPTR_WIDTH 12
+#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_LBN 36
+#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_WIDTH 20
+#define FRF_AZ_TX_DESCQ_EVQ_ID_LBN 24
+#define FRF_AZ_TX_DESCQ_EVQ_ID_WIDTH 12
+#define FRF_AZ_TX_DESCQ_OWNER_ID_LBN 10
+#define FRF_AZ_TX_DESCQ_OWNER_ID_WIDTH 14
+#define FRF_AZ_TX_DESCQ_LABEL_LBN 5
+#define FRF_AZ_TX_DESCQ_LABEL_WIDTH 5
+#define FRF_AZ_TX_DESCQ_SIZE_LBN 3
+#define FRF_AZ_TX_DESCQ_SIZE_WIDTH 2
+#define FFE_AZ_TX_DESCQ_SIZE_4K 3
+#define FFE_AZ_TX_DESCQ_SIZE_2K 2
+#define FFE_AZ_TX_DESCQ_SIZE_1K 1
+#define FFE_AZ_TX_DESCQ_SIZE_512 0
+#define FRF_AZ_TX_DESCQ_TYPE_LBN 1
+#define FRF_AZ_TX_DESCQ_TYPE_WIDTH 2
+#define FRF_AZ_TX_DESCQ_FLUSH_LBN 0
+#define FRF_AZ_TX_DESCQ_FLUSH_WIDTH 1
+
+/* EVQ_PTR_TBL_KER: Event queue pointer table */
+#define FR_AA_EVQ_PTR_TBL_KER 0x00011a00
+#define FR_AA_EVQ_PTR_TBL_KER_STEP 16
+#define FR_AA_EVQ_PTR_TBL_KER_ROWS 4
+/* EVQ_PTR_TBL: Event queue pointer table */
+#define FR_BZ_EVQ_PTR_TBL 0x00f60000
+#define FR_BZ_EVQ_PTR_TBL_STEP 16
+#define FR_CZ_EVQ_PTR_TBL_ROWS 1024
+#define FR_BB_EVQ_PTR_TBL_ROWS 4096
+#define FRF_BZ_EVQ_RPTR_IGN_LBN 40
+#define FRF_BZ_EVQ_RPTR_IGN_WIDTH 1
+#define FRF_AB_EVQ_WKUP_OR_INT_EN_LBN 39
+#define FRF_AB_EVQ_WKUP_OR_INT_EN_WIDTH 1
+#define FRF_CZ_EVQ_DOS_PROTECT_EN_LBN 39
+#define FRF_CZ_EVQ_DOS_PROTECT_EN_WIDTH 1
+#define FRF_AZ_EVQ_NXT_WPTR_LBN 24
+#define FRF_AZ_EVQ_NXT_WPTR_WIDTH 15
+#define FRF_AZ_EVQ_EN_LBN 23
+#define FRF_AZ_EVQ_EN_WIDTH 1
+#define FRF_AZ_EVQ_SIZE_LBN 20
+#define FRF_AZ_EVQ_SIZE_WIDTH 3
+#define FFE_AZ_EVQ_SIZE_32K 6
+#define FFE_AZ_EVQ_SIZE_16K 5
+#define FFE_AZ_EVQ_SIZE_8K 4
+#define FFE_AZ_EVQ_SIZE_4K 3
+#define FFE_AZ_EVQ_SIZE_2K 2
+#define FFE_AZ_EVQ_SIZE_1K 1
+#define FFE_AZ_EVQ_SIZE_512 0
+#define FRF_AZ_EVQ_BUF_BASE_ID_LBN 0
+#define FRF_AZ_EVQ_BUF_BASE_ID_WIDTH 20
+
+/* BUF_HALF_TBL_KER: Buffer table in half buffer table mode direct access by driver */
+#define FR_AA_BUF_HALF_TBL_KER 0x00018000
+#define FR_AA_BUF_HALF_TBL_KER_STEP 8
+#define FR_AA_BUF_HALF_TBL_KER_ROWS 4096
+/* BUF_HALF_TBL: Buffer table in half buffer table mode direct access by driver */
+#define FR_BZ_BUF_HALF_TBL 0x00800000
+#define FR_BZ_BUF_HALF_TBL_STEP 8
+#define FR_CZ_BUF_HALF_TBL_ROWS 147456
+#define FR_BB_BUF_HALF_TBL_ROWS 524288
+#define FRF_AZ_BUF_ADR_HBUF_ODD_LBN 44
+#define FRF_AZ_BUF_ADR_HBUF_ODD_WIDTH 20
+#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_LBN 32
+#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_WIDTH 12
+#define FRF_AZ_BUF_ADR_HBUF_EVEN_LBN 12
+#define FRF_AZ_BUF_ADR_HBUF_EVEN_WIDTH 20
+#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_LBN 0
+#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_WIDTH 12
+
+/* BUF_FULL_TBL_KER: Buffer table in full buffer table mode direct access by driver */
+#define FR_AA_BUF_FULL_TBL_KER 0x00018000
+#define FR_AA_BUF_FULL_TBL_KER_STEP 8
+#define FR_AA_BUF_FULL_TBL_KER_ROWS 4096
+/* BUF_FULL_TBL: Buffer table in full buffer table mode direct access by driver */
+#define FR_BZ_BUF_FULL_TBL 0x00800000
+#define FR_BZ_BUF_FULL_TBL_STEP 8
+#define FR_CZ_BUF_FULL_TBL_ROWS 147456
+#define FR_BB_BUF_FULL_TBL_ROWS 917504
+#define FRF_AZ_BUF_FULL_UNUSED_LBN 51
+#define FRF_AZ_BUF_FULL_UNUSED_WIDTH 13
+#define FRF_AZ_IP_DAT_BUF_SIZE_LBN 50
+#define FRF_AZ_IP_DAT_BUF_SIZE_WIDTH 1
+#define FRF_AZ_BUF_ADR_REGION_LBN 48
+#define FRF_AZ_BUF_ADR_REGION_WIDTH 2
+#define FFE_AZ_BUF_ADR_REGN3 3
+#define FFE_AZ_BUF_ADR_REGN2 2
+#define FFE_AZ_BUF_ADR_REGN1 1
+#define FFE_AZ_BUF_ADR_REGN0 0
+#define FRF_AZ_BUF_ADR_FBUF_LBN 14
+#define FRF_AZ_BUF_ADR_FBUF_WIDTH 34
+#define FRF_AZ_BUF_OWNER_ID_FBUF_LBN 0
+#define FRF_AZ_BUF_OWNER_ID_FBUF_WIDTH 14
+
+/* RX_FILTER_TBL0: TCP/IPv4 Receive filter table */
+#define FR_BZ_RX_FILTER_TBL0 0x00f00000
+#define FR_BZ_RX_FILTER_TBL0_STEP 32
+#define FR_BZ_RX_FILTER_TBL0_ROWS 8192
+/* RX_FILTER_TBL1: TCP/IPv4 Receive filter table */
+#define FR_BB_RX_FILTER_TBL1 0x00f00010
+#define FR_BB_RX_FILTER_TBL1_STEP 32
+#define FR_BB_RX_FILTER_TBL1_ROWS 8192
+#define FRF_BZ_RSS_EN_LBN 110
+#define FRF_BZ_RSS_EN_WIDTH 1
+#define FRF_BZ_SCATTER_EN_LBN 109
+#define FRF_BZ_SCATTER_EN_WIDTH 1
+#define FRF_BZ_TCP_UDP_LBN 108
+#define FRF_BZ_TCP_UDP_WIDTH 1
+#define FRF_BZ_RXQ_ID_LBN 96
+#define FRF_BZ_RXQ_ID_WIDTH 12
+#define FRF_BZ_DEST_IP_LBN 64
+#define FRF_BZ_DEST_IP_WIDTH 32
+#define FRF_BZ_DEST_PORT_TCP_LBN 48
+#define FRF_BZ_DEST_PORT_TCP_WIDTH 16
+#define FRF_BZ_SRC_IP_LBN 16
+#define FRF_BZ_SRC_IP_WIDTH 32
+#define FRF_BZ_SRC_TCP_DEST_UDP_LBN 0
+#define FRF_BZ_SRC_TCP_DEST_UDP_WIDTH 16
+
+/* RX_MAC_FILTER_TBL0: Receive Ethernet filter table */
+#define FR_CZ_RX_MAC_FILTER_TBL0 0x00f00010
+#define FR_CZ_RX_MAC_FILTER_TBL0_STEP 32
+#define FR_CZ_RX_MAC_FILTER_TBL0_ROWS 512
+#define FRF_CZ_RMFT_RSS_EN_LBN 75
+#define FRF_CZ_RMFT_RSS_EN_WIDTH 1
+#define FRF_CZ_RMFT_SCATTER_EN_LBN 74
+#define FRF_CZ_RMFT_SCATTER_EN_WIDTH 1
+#define FRF_CZ_RMFT_IP_OVERRIDE_LBN 73
+#define FRF_CZ_RMFT_IP_OVERRIDE_WIDTH 1
+#define FRF_CZ_RMFT_RXQ_ID_LBN 61
+#define FRF_CZ_RMFT_RXQ_ID_WIDTH 12
+#define FRF_CZ_RMFT_WILDCARD_MATCH_LBN 60
+#define FRF_CZ_RMFT_WILDCARD_MATCH_WIDTH 1
+#define FRF_CZ_RMFT_DEST_MAC_LBN 12
+#define FRF_CZ_RMFT_DEST_MAC_WIDTH 48
+#define FRF_CZ_RMFT_VLAN_ID_LBN 0
+#define FRF_CZ_RMFT_VLAN_ID_WIDTH 12
+
+/* TIMER_TBL: Timer table */
+#define FR_BZ_TIMER_TBL 0x00f70000
+#define FR_BZ_TIMER_TBL_STEP 16
+#define FR_CZ_TIMER_TBL_ROWS 1024
+#define FR_BB_TIMER_TBL_ROWS 4096
+#define FRF_CZ_TIMER_Q_EN_LBN 33
+#define FRF_CZ_TIMER_Q_EN_WIDTH 1
+#define FRF_CZ_INT_ARMD_LBN 32
+#define FRF_CZ_INT_ARMD_WIDTH 1
+#define FRF_CZ_INT_PEND_LBN 31
+#define FRF_CZ_INT_PEND_WIDTH 1
+#define FRF_CZ_HOST_NOTIFY_MODE_LBN 30
+#define FRF_CZ_HOST_NOTIFY_MODE_WIDTH 1
+#define FRF_CZ_RELOAD_TIMER_VAL_LBN 16
+#define FRF_CZ_RELOAD_TIMER_VAL_WIDTH 14
+#define FRF_CZ_TIMER_MODE_LBN 14
+#define FRF_CZ_TIMER_MODE_WIDTH 2
+#define FFE_CZ_TIMER_MODE_INT_HLDOFF 3
+#define FFE_CZ_TIMER_MODE_TRIG_START 2
+#define FFE_CZ_TIMER_MODE_IMMED_START 1
+#define FFE_CZ_TIMER_MODE_DIS 0
+#define FRF_BB_TIMER_MODE_LBN 12
+#define FRF_BB_TIMER_MODE_WIDTH 2
+#define FFE_BB_TIMER_MODE_INT_HLDOFF 2
+#define FFE_BB_TIMER_MODE_TRIG_START 2
+#define FFE_BB_TIMER_MODE_IMMED_START 1
+#define FFE_BB_TIMER_MODE_DIS 0
+#define FRF_CZ_TIMER_VAL_LBN 0
+#define FRF_CZ_TIMER_VAL_WIDTH 14
+#define FRF_BB_TIMER_VAL_LBN 0
+#define FRF_BB_TIMER_VAL_WIDTH 12
+
+/* TX_PACE_TBL: Transmit pacing table */
+#define FR_BZ_TX_PACE_TBL 0x00f80000
+#define FR_BZ_TX_PACE_TBL_STEP 16
+#define FR_CZ_TX_PACE_TBL_ROWS 1024
+#define FR_BB_TX_PACE_TBL_ROWS 4096
+#define FRF_BZ_TX_PACE_LBN 0
+#define FRF_BZ_TX_PACE_WIDTH 5
+
+/* RX_INDIRECTION_TBL: RX Indirection Table */
+#define FR_BZ_RX_INDIRECTION_TBL 0x00fb0000
+#define FR_BZ_RX_INDIRECTION_TBL_STEP 16
+#define FR_BZ_RX_INDIRECTION_TBL_ROWS 128
+#define FRF_BZ_IT_QUEUE_LBN 0
+#define FRF_BZ_IT_QUEUE_WIDTH 6
+
+/* TX_FILTER_TBL0: TCP/IPv4 Transmit filter table */
+#define FR_CZ_TX_FILTER_TBL0 0x00fc0000
+#define FR_CZ_TX_FILTER_TBL0_STEP 16
+#define FR_CZ_TX_FILTER_TBL0_ROWS 8192
+#define FRF_CZ_TIFT_TCP_UDP_LBN 108
+#define FRF_CZ_TIFT_TCP_UDP_WIDTH 1
+#define FRF_CZ_TIFT_TXQ_ID_LBN 96
+#define FRF_CZ_TIFT_TXQ_ID_WIDTH 12
+#define FRF_CZ_TIFT_DEST_IP_LBN 64
+#define FRF_CZ_TIFT_DEST_IP_WIDTH 32
+#define FRF_CZ_TIFT_DEST_PORT_TCP_LBN 48
+#define FRF_CZ_TIFT_DEST_PORT_TCP_WIDTH 16
+#define FRF_CZ_TIFT_SRC_IP_LBN 16
+#define FRF_CZ_TIFT_SRC_IP_WIDTH 32
+#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_LBN 0
+#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_WIDTH 16
+
+/* TX_MAC_FILTER_TBL0: Transmit Ethernet filter table */
+#define FR_CZ_TX_MAC_FILTER_TBL0 0x00fe0000
+#define FR_CZ_TX_MAC_FILTER_TBL0_STEP 16
+#define FR_CZ_TX_MAC_FILTER_TBL0_ROWS 512
+#define FRF_CZ_TMFT_TXQ_ID_LBN 61
+#define FRF_CZ_TMFT_TXQ_ID_WIDTH 12
+#define FRF_CZ_TMFT_WILDCARD_MATCH_LBN 60
+#define FRF_CZ_TMFT_WILDCARD_MATCH_WIDTH 1
+#define FRF_CZ_TMFT_SRC_MAC_LBN 12
+#define FRF_CZ_TMFT_SRC_MAC_WIDTH 48
+#define FRF_CZ_TMFT_VLAN_ID_LBN 0
+#define FRF_CZ_TMFT_VLAN_ID_WIDTH 12
+
+/* MC_TREG_SMEM: MC Shared Memory */
+#define FR_CZ_MC_TREG_SMEM 0x00ff0000
+#define FR_CZ_MC_TREG_SMEM_STEP 4
+#define FR_CZ_MC_TREG_SMEM_ROWS 512
+#define FRF_CZ_MC_TREG_SMEM_ROW_LBN 0
+#define FRF_CZ_MC_TREG_SMEM_ROW_WIDTH 32
+
+/* EF10 architecture register definitions
+ * (from linux/drivers/net/ethernet/sfc/ef10_regs.h)
+ */
+
+/* BIU_HW_REV_ID_REG: */
+#define ER_DZ_BIU_HW_REV_ID 0x00000000
+#define ERF_DZ_HW_REV_ID_LBN 0
+#define ERF_DZ_HW_REV_ID_WIDTH 32
+
+/* BIU_MC_SFT_STATUS_REG: */
+#define ER_DZ_BIU_MC_SFT_STATUS 0x00000010
+#define ER_DZ_BIU_MC_SFT_STATUS_STEP 4
+#define ER_DZ_BIU_MC_SFT_STATUS_ROWS 8
+#define ERF_DZ_MC_SFT_STATUS_LBN 0
+#define ERF_DZ_MC_SFT_STATUS_WIDTH 32
+
+/* BIU_INT_ISR_REG: */
+#define ER_DZ_BIU_INT_ISR 0x00000090
+#define ERF_DZ_ISR_REG_LBN 0
+#define ERF_DZ_ISR_REG_WIDTH 32
+
+/* MC_DB_LWRD_REG: */
+#define ER_DZ_MC_DB_LWRD 0x00000200
+#define ERF_DZ_MC_DOORBELL_L_LBN 0
+#define ERF_DZ_MC_DOORBELL_L_WIDTH 32
+
+/* MC_DB_HWRD_REG: */
+#define ER_DZ_MC_DB_HWRD 0x00000204
+#define ERF_DZ_MC_DOORBELL_H_LBN 0
+#define ERF_DZ_MC_DOORBELL_H_WIDTH 32
+
+/*
+ * Register dump definition. This is mostly taken from
+ * linux/drivers/net/ethernet/sfc/nic.c but has names and bitfield
+ * definitions added.
+ *
+ * The definitions of efx_nic_regs and efx_nic_reg_tables should be
+ * textually identical to those in the driver, though the structure
+ * definitions and the macros REGISTER and REGISTER_TABLE_DIMENSIONS
+ * are defined differently.
+ */
+
+#define REGISTER_REVISION_FA 1
+#define REGISTER_REVISION_FB 2
+#define REGISTER_REVISION_FC 3
+#define REGISTER_REVISION_FZ 3 /* last Falcon arch revision */
+#define REGISTER_REVISION_ED 4
+#define REGISTER_REVISION_EZ 4 /* latest EF10 arch revision */
+
+struct efx_nic_reg_field {
+ const char *name;
+ u8 lbn, width;
+ u8 min_revision, max_revision;
+};
+
+#define REGISTER_FIELD_RENAME(name, display_name, arch, min_rev, max_rev) { \
+ display_name, \
+ arch ## RF_ ## min_rev ## max_rev ## _ ## name ## _LBN, \
+ arch ## RF_ ## min_rev ## max_rev ## _ ## name ## _WIDTH, \
+ REGISTER_REVISION_ ## arch ## min_rev, \
+ REGISTER_REVISION_ ## arch ## max_rev \
+}
+#define REGISTER_FIELD(name, arch, min_rev, max_rev) \
+ REGISTER_FIELD_RENAME(name, #name, arch, min_rev, max_rev)
+#define REGISTER_FIELD_AA(name) REGISTER_FIELD(name, F, A, A)
+#define REGISTER_FIELD_AB(name) REGISTER_FIELD(name, F, A, B)
+#define REGISTER_FIELD_AZ(name) REGISTER_FIELD(name, F, A, Z)
+#define REGISTER_FIELD_BB(name) REGISTER_FIELD(name, F, B, B)
+#define REGISTER_FIELD_BZ(name) REGISTER_FIELD(name, F, B, Z)
+#define REGISTER_FIELD_CZ(name) REGISTER_FIELD(name, F, C, Z)
+#define REGISTER_FIELD_DZ(name) REGISTER_FIELD(name, E, D, Z)
+#define REGISTER_FIELD_AZ_RENAME(name, display_name) \
+ REGISTER_FIELD_RENAME(name, display_name, F, A, Z)
+#define REGISTER_FIELD_BZ_RENAME(name, display_name) \
+ REGISTER_FIELD_RENAME(name, display_name, F, B, Z)
+#define REGISTER_FIELD_CZ_RENAME(name, display_name) \
+ REGISTER_FIELD_RENAME(name, display_name, F, C, Z)
+
+static const struct efx_nic_reg_field efx_nic_reg_fields_ADR_REGION[] = {
+ REGISTER_FIELD_AZ(ADR_REGION0),
+ REGISTER_FIELD_AZ(ADR_REGION1),
+ REGISTER_FIELD_AZ(ADR_REGION2),
+ REGISTER_FIELD_AZ(ADR_REGION3),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_INT_EN_KER[] = {
+ REGISTER_FIELD_AZ(DRV_INT_EN_KER),
+ REGISTER_FIELD_AZ(KER_INT_KER),
+ REGISTER_FIELD_AZ(KER_INT_CHAR),
+ REGISTER_FIELD_AZ(KER_INT_LEVE_SEL),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_INT_EN_CHAR[] = {
+ REGISTER_FIELD_BZ(DRV_INT_EN_CHAR),
+ REGISTER_FIELD_BZ(CHAR_INT_KER),
+ REGISTER_FIELD_BZ(CHAR_INT_CHAR),
+ REGISTER_FIELD_BZ(CHAR_INT_LEVE_SEL),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_INT_ADR_KER[] = {
+ REGISTER_FIELD_AZ(INT_ADR_KER),
+ REGISTER_FIELD_AZ(NORM_INT_VEC_DIS_KER),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_INT_ADR_CHAR[] = {
+ REGISTER_FIELD_BZ(INT_ADR_CHAR),
+ REGISTER_FIELD_BZ(NORM_INT_VEC_DIS_CHAR),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_HW_INIT[] = {
+ REGISTER_FIELD_AZ(TLP_TD),
+ REGISTER_FIELD_AZ(TD_SEL),
+ REGISTER_FIELD_AZ(ATTR_SEL),
+ REGISTER_FIELD_AZ(TLP_EP),
+ REGISTER_FIELD_AZ(US_DISABLE),
+ REGISTER_FIELD_AZ(WD_TIMER),
+ REGISTER_FIELD_AB(INTA_VEC),
+ REGISTER_FIELD_AB(INTB_VEC),
+ REGISTER_FIELD_AZ(TLP_ATTR),
+ REGISTER_FIELD_AZ(TLP_TC),
+ REGISTER_FIELD_AZ(POST_WR_MASK),
+ REGISTER_FIELD_BB(FC_BLOCKING_EN),
+ REGISTER_FIELD_AA(B2B_REQ_EN),
+ REGISTER_FIELD_BZ(B2B_REQ_EN),
+ REGISTER_FIELD_AA(FC_BLOCKING_EN),
+ REGISTER_FIELD_AB(PE_EIDLE_DIS),
+ REGISTER_FIELD_AB(TX_RREQ_MASK_EN),
+ REGISTER_FIELD_AZ(DOORBELL_DROP),
+ REGISTER_FIELD_AB(TRGT_MASK_ALL),
+ REGISTER_FIELD_CZ(TX_MRG_TAGS),
+ REGISTER_FIELD_BB(PCIE_CPL_TIMEOUT_CTRL),
+ REGISTER_FIELD_BB(BDMRD_CPLF_FULL),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_USR_EV_CFG[] = {
+ REGISTER_FIELD_CZ(DFLT_EVQ),
+ REGISTER_FIELD_CZ(USREV_DIS),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_EE_SPI_HCMD[] = {
+ REGISTER_FIELD_AB(EE_SPI_HCMD_ENC),
+ REGISTER_FIELD_AB(EE_SPI_HCMD_ADBCNT),
+ REGISTER_FIELD_AB(EE_SPI_HCMD_DUBCNT),
+ REGISTER_FIELD_AB(EE_SPI_HCMD_READ),
+ REGISTER_FIELD_AB(EE_SPI_HCMD_DABCNT),
+ REGISTER_FIELD_AB(EE_SPI_HCMD_SF_SEL),
+ REGISTER_FIELD_AB(EE_WR_TIMER_ACTIVE),
+ REGISTER_FIELD_AB(EE_SPI_HCMD_CMD_EN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_EE_SPI_HADR[] = {
+ REGISTER_FIELD_AB(EE_SPI_HADR_ADR),
+ REGISTER_FIELD_AB(EE_SPI_HADR_DUBYTE),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_EE_SPI_HDATA[] = {
+ REGISTER_FIELD_AB(EE_SPI_HDATA0),
+ REGISTER_FIELD_AB(EE_SPI_HDATA1),
+ REGISTER_FIELD_AB(EE_SPI_HDATA2),
+ REGISTER_FIELD_AB(EE_SPI_HDATA3),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_EE_BASE_PAGE[] = {
+ REGISTER_FIELD_AB(EE_EXP_ROM_WINDOW_BASE),
+ REGISTER_FIELD_AB(EE_EXPROM_MASK),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_EE_VPD_CFG0[] = {
+ REGISTER_FIELD_AB(EE_VPD_EN),
+ REGISTER_FIELD_AB(EE_VPD_EN_AD9_MODE),
+ REGISTER_FIELD_AB(EE_VPD_DEV_SF_SEL),
+ REGISTER_FIELD_AB(EE_VPD_ACCESS_BLOCK),
+ REGISTER_FIELD_AB(EE_VPD_ACCESS_ON),
+ REGISTER_FIELD_AB(EE_VPD_AD_SIZE),
+ REGISTER_FIELD_AB(EE_VPD_LENGTH),
+ REGISTER_FIELD_AB(EE_VPD_BASE),
+ REGISTER_FIELD_AB(EE_VPD_WR_CMD_EN),
+ REGISTER_FIELD_AB(EE_VPDW_BASE),
+ REGISTER_FIELD_AB(EE_VPDW_LENGTH),
+ REGISTER_FIELD_AB(EE_EE_WR_TMR_VALUE),
+ REGISTER_FIELD_AB(EE_EE_CLOCK_DIV),
+ REGISTER_FIELD_AB(EE_VPD_WIP_POLL),
+ REGISTER_FIELD_AB(EE_SF_CLOCK_DIV),
+ REGISTER_FIELD_AB(EE_SF_FASTRD_EN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_NIC_STAT[] = {
+ REGISTER_FIELD_AB(STRAP_PINS),
+ REGISTER_FIELD_AB(ATE_MODE),
+ REGISTER_FIELD_AB(EE_PRST),
+ REGISTER_FIELD_AB(SF_PRST),
+ REGISTER_FIELD_AB(ONCHIP_SRAM),
+ REGISTER_FIELD_BB(REVISION_ID),
+ REGISTER_FIELD_BB(EE_STRAP),
+ REGISTER_FIELD_BB(EE_STRAP_EN),
+ REGISTER_FIELD_BB(AER_DIS),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GPIO_CTL[] = {
+ REGISTER_FIELD_AB(GPIO0_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO1_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO2_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO3_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO4_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO5_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO6_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO7_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO0_IN),
+ REGISTER_FIELD_AB(GPIO1_IN),
+ REGISTER_FIELD_AB(GPIO2_IN),
+ REGISTER_FIELD_AB(GPIO3_IN),
+ REGISTER_FIELD_AB(GPIO4_IN),
+ REGISTER_FIELD_AB(GPIO5_IN),
+ REGISTER_FIELD_AB(GPIO6_IN),
+ REGISTER_FIELD_AB(GPIO7_IN),
+ REGISTER_FIELD_AB(GPIO0_OUT),
+ REGISTER_FIELD_AB(GPIO1_OUT),
+ REGISTER_FIELD_AB(GPIO2_OUT),
+ REGISTER_FIELD_AB(GPIO3_OUT),
+ REGISTER_FIELD_AB(GPIO4_OUT),
+ REGISTER_FIELD_AB(GPIO5_OUT),
+ REGISTER_FIELD_AB(GPIO6_OUT),
+ REGISTER_FIELD_AB(GPIO7_OUT),
+ REGISTER_FIELD_AB(GPIO0_OEN),
+ REGISTER_FIELD_AB(GPIO1_OEN),
+ REGISTER_FIELD_AB(GPIO2_OEN),
+ REGISTER_FIELD_AB(GPIO3_OEN),
+ REGISTER_FIELD_AB(GPIO4_OEN),
+ REGISTER_FIELD_AB(GPIO5_OEN),
+ REGISTER_FIELD_AB(USE_NIC_CLK),
+ REGISTER_FIELD_AB(CLK156_OUT_EN),
+ REGISTER_FIELD_AB(GPIO8_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO9_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO10_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO11_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO12_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO13_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO14_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO15_PWRUP_VALUE),
+ REGISTER_FIELD_AB(GPIO8_IN),
+ REGISTER_FIELD_AB(GPIO9_IN),
+ REGISTER_FIELD_AB(GPIO10_IN),
+ REGISTER_FIELD_AB(GPIO11_IN),
+ REGISTER_FIELD_AB(GPIO12_IN),
+ REGISTER_FIELD_AB(GPIO13_IN),
+ REGISTER_FIELD_AB(GPIO14_IN),
+ REGISTER_FIELD_AB(GPIO15_IN),
+ REGISTER_FIELD_AB(GPIO8_OUT),
+ REGISTER_FIELD_AB(GPIO9_OUT),
+ REGISTER_FIELD_AB(GPIO10_OUT),
+ REGISTER_FIELD_AB(GPIO11_OUT),
+ REGISTER_FIELD_AB(GPIO12_OUT),
+ REGISTER_FIELD_AB(GPIO13_OUT),
+ REGISTER_FIELD_AB(GPIO14_OUT),
+ REGISTER_FIELD_AB(GPIO15_OUT),
+ REGISTER_FIELD_AB(GPIO8_OEN),
+ REGISTER_FIELD_AB(GPIO9_OEN),
+ REGISTER_FIELD_AB(GPIO10_OEN),
+ REGISTER_FIELD_AB(GPIO11_OEN),
+ REGISTER_FIELD_AB(GPIO12_OEN),
+ REGISTER_FIELD_AB(GPIO13_OEN),
+ REGISTER_FIELD_AB(GPIO14_OEN),
+ REGISTER_FIELD_AB(GPIO15_OEN),
+ REGISTER_FIELD_AB(GPIO_PWRUP_VALUE2),
+ REGISTER_FIELD_AB(GPIO_IN2),
+ REGISTER_FIELD_AB(GPIO_OUT2),
+ REGISTER_FIELD_AB(GPIO_PWRUP_VALUE3),
+ REGISTER_FIELD_AB(GPIO_IN3),
+ REGISTER_FIELD_AB(GPIO_OUT3),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GLB_CTL[] = {
+ REGISTER_FIELD_AB(SWRST),
+ REGISTER_FIELD_AB(EXT_PHY_RST_DUR),
+ REGISTER_FIELD_AB(INT_RST_DUR),
+ REGISTER_FIELD_AB(RST_CS),
+ REGISTER_FIELD_AB(RST_SF),
+ REGISTER_FIELD_AB(RST_TX),
+ REGISTER_FIELD_AB(RST_RX),
+ REGISTER_FIELD_AB(RST_SR),
+ REGISTER_FIELD_AB(RST_EV),
+ REGISTER_FIELD_AB(RST_EM),
+ REGISTER_FIELD_AB(RST_XGTX),
+ REGISTER_FIELD_AB(RST_XGRX),
+ REGISTER_FIELD_AB(RST_PCIE_CORE),
+ REGISTER_FIELD_AB(RST_PCIE_NSTKY),
+ REGISTER_FIELD_AB(RST_PCIE_STKY),
+ REGISTER_FIELD_BB(RST_BIU),
+ REGISTER_FIELD_AA(RST_PCIX),
+ REGISTER_FIELD_AB(RST_PCIE_SD),
+ REGISTER_FIELD_AB(RST_XAUI_SD),
+ REGISTER_FIELD_AB(RST_EXT_PHY),
+ REGISTER_FIELD_AB(HOT_RST_CTL),
+ REGISTER_FIELD_AB(CS_RST_CTL),
+ REGISTER_FIELD_AB(EE_RST_CTL),
+ REGISTER_FIELD_AB(TX_RST_CTL),
+ REGISTER_FIELD_AB(RX_RST_CTL),
+ REGISTER_FIELD_AB(SR_RST_CTL),
+ REGISTER_FIELD_AB(EV_RST_CTL),
+ REGISTER_FIELD_AB(EM_RST_CTL),
+ REGISTER_FIELD_AB(XGTX_RST_CTL),
+ REGISTER_FIELD_AB(XGRX_RST_CTL),
+ REGISTER_FIELD_AB(PCIE_CORE_RST_CTL),
+ REGISTER_FIELD_AB(PCIE_NSTKY_RST_CTL),
+ REGISTER_FIELD_AB(PCIE_STKY_RST_CTL),
+ REGISTER_FIELD_BB(BIU_RST_CTL),
+ REGISTER_FIELD_AA(PCIX_RST_CTL),
+ REGISTER_FIELD_AB(PCIE_SD_RST_CTL),
+ REGISTER_FIELD_AB(XAUI_SD_RST_CTL),
+ REGISTER_FIELD_AB(EXT_PHY_RST_CTL),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_DP_CTRL[] = {
+ REGISTER_FIELD_BZ(FLS_EVQ_ID),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MEM_STAT[] = {
+ REGISTER_FIELD_CZ(MEM_PERR_VEC),
+ REGISTER_FIELD_AB(MBIST_ERR),
+ REGISTER_FIELD_AB(MBIST_CORR),
+ REGISTER_FIELD_AB(MEM_PERR_VEC),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_CS_DEBUG[] = {
+ /* This is not a complete list of fields */
+ REGISTER_FIELD_AZ(CS_DEBUG_EN),
+ REGISTER_FIELD_CZ(CS_PORT_NUM),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_ALTERA_BUILD[] = {
+ REGISTER_FIELD_AZ(ALTERA_BUILD_VER),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_CSR_SPARE[] = {
+ REGISTER_FIELD_AZ(CSR_SPARE_BITS),
+ REGISTER_FIELD_AB(MEM_PERR_EN_TX_DATA),
+ REGISTER_FIELD_CZ(MEM_PERR_EN),
+ REGISTER_FIELD_AB(MEM_PERR_EN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_PCIE_SD_CTL0123[] = {
+ REGISTER_FIELD_AB(PCIE_LODRV),
+ REGISTER_FIELD_AB(PCIE_HIDRV),
+ REGISTER_FIELD_AB(PCIE_RXEQCTL_L),
+ REGISTER_FIELD_AB(PCIE_RXEQCTL_H),
+ REGISTER_FIELD_AB(PCIE_TXTERMADJ_L),
+ REGISTER_FIELD_AB(PCIE_TXTERMADJ_H),
+ REGISTER_FIELD_AB(PCIE_RXTERMADJ_L),
+ REGISTER_FIELD_AB(PCIE_RXTERMADJ_H),
+ REGISTER_FIELD_AB(PCIE_PARLPBK),
+ REGISTER_FIELD_AB(PCIE_LPBK),
+ REGISTER_FIELD_AB(PCIE_LPBKWDRV_L),
+ REGISTER_FIELD_AB(PCIE_LPBKWDRV_H),
+ REGISTER_FIELD_AB(PCIE_PARRESET_L),
+ REGISTER_FIELD_AB(PCIE_PARRESET_H),
+ REGISTER_FIELD_AB(PCIE_HIVMODE_L),
+ REGISTER_FIELD_AB(PCIE_HIVMODE_H),
+ REGISTER_FIELD_AB(PCIE_OFFSETEN_L),
+ REGISTER_FIELD_AB(PCIE_OFFSETEN_H),
+ REGISTER_FIELD_AB(PCIE_OFFSET),
+ REGISTER_FIELD_AB(PCIE_TESTSIG_L),
+ REGISTER_FIELD_AB(PCIE_TESTSIG_H),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_PCIE_SD_CTL45[] = {
+ REGISTER_FIELD_AB(PCIE_DEQ0),
+ REGISTER_FIELD_AB(PCIE_DEQ1),
+ REGISTER_FIELD_AB(PCIE_DEQ2),
+ REGISTER_FIELD_AB(PCIE_DEQ3),
+ REGISTER_FIELD_AB(PCIE_DEQ4),
+ REGISTER_FIELD_AB(PCIE_DEQ5),
+ REGISTER_FIELD_AB(PCIE_DEQ6),
+ REGISTER_FIELD_AB(PCIE_DEQ7),
+ REGISTER_FIELD_AB(PCIE_DTX0),
+ REGISTER_FIELD_AB(PCIE_DTX1),
+ REGISTER_FIELD_AB(PCIE_DTX2),
+ REGISTER_FIELD_AB(PCIE_DTX3),
+ REGISTER_FIELD_AB(PCIE_DTX4),
+ REGISTER_FIELD_AB(PCIE_DTX5),
+ REGISTER_FIELD_AB(PCIE_DTX6),
+ REGISTER_FIELD_AB(PCIE_DTX7),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_PCIE_PCS_CTL_STAT[] = {
+ REGISTER_FIELD_AB(PCIE_PRBSSEL),
+ REGISTER_FIELD_AB(PCIE_PRBSERRACK_L),
+ REGISTER_FIELD_AB(PCIE_PRBSERRACK_H),
+ REGISTER_FIELD_AB(PCIE_PRBSSYNC_L),
+ REGISTER_FIELD_AB(PCIE_PRBSSYNC_H),
+ REGISTER_FIELD_AB(PCIE_CTCDISABLE_L),
+ REGISTER_FIELD_AB(PCIE_CTCDISABLE_H),
+ REGISTER_FIELD_AB(PCIE_FASTINIT_L),
+ REGISTER_FIELD_AB(PCIE_FASTINIT_H),
+ REGISTER_FIELD_AB(PCIE_PRBSERRH0),
+ REGISTER_FIELD_AB(PCIE_PRBSERR),
+ REGISTER_FIELD_AB(PCIE_PRBSERRCOUNT0_L),
+ REGISTER_FIELD_AB(PCIE_PRBSERRCOUNT0_H),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_EVQ_CTL[] = {
+ REGISTER_FIELD_AZ(EVQ_FIFO_NOTAF_TH),
+ REGISTER_FIELD_AZ(EVQ_FIFO_AF_TH),
+ REGISTER_FIELD_AZ(EVQ_OWNERR_CTL),
+ REGISTER_FIELD_BB(RX_EVQ_WAKEUP_MASK),
+ REGISTER_FIELD_CZ(RX_EVQ_WAKEUP_MASK),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_EVQ_CNT1[] = {
+ REGISTER_FIELD_AZ(EVQ_ERR_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_CSR_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_EM_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_RX_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_TX_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_CNT_TOBIU),
+ REGISTER_FIELD_AZ(EVQ_CNT_PRE_FIFO),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_EVQ_CNT2[] = {
+ REGISTER_FIELD_AZ(EVQ_TM_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_INIT_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_WET_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_WU_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_RDY_CNT),
+ REGISTER_FIELD_AZ(EVQ_CLR_REQ_CNT),
+ REGISTER_FIELD_AZ(EVQ_UPD_REQ_CNT),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_BUF_TBL_CFG[] = {
+ REGISTER_FIELD_AZ(BUF_TBL_MODE),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_SRM_RX_DC_CFG[] = {
+ REGISTER_FIELD_AZ(SRM_RX_DC_BASE_ADR),
+ REGISTER_FIELD_AZ(SRM_CLK_TMP_EN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_SRM_TX_DC_CFG[] = {
+ REGISTER_FIELD_AZ(SRM_TX_DC_BASE_ADR),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_SRM_CFG[] = {
+ REGISTER_FIELD_AZ(SRM_BANK_SIZE),
+ REGISTER_FIELD_AZ(SRM_NUM_BANK),
+ REGISTER_FIELD_AZ(SRM_INIT_EN),
+ REGISTER_FIELD_AZ(SRM_OOB_BUF_INTEN),
+ REGISTER_FIELD_AZ(SRM_OOB_ADR_INTEN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_SRM_UPD_EVQ[] = {
+ REGISTER_FIELD_AZ(SRM_UPD_EVQ_ID),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_SRAM_PARITY[] = {
+ REGISTER_FIELD_CZ(FORCE_SRAM_SINGLE_ERR),
+ REGISTER_FIELD_AB(FORCE_SRAM_PERR),
+ REGISTER_FIELD_CZ(FORCE_SRAM_DOUBLE_ERR),
+ REGISTER_FIELD_CZ(SEC_INT),
+ REGISTER_FIELD_CZ(BYPASS_ECC),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_CFG[] = {
+ REGISTER_FIELD_AZ(RX_XOFF_MAC_EN),
+ REGISTER_FIELD_AA(RX_XOFF_MAC_TH),
+ REGISTER_FIELD_BZ(RX_XOFF_MAC_TH),
+ REGISTER_FIELD_AA(RX_XON_MAC_TH),
+ REGISTER_FIELD_BZ(RX_XON_MAC_TH),
+ REGISTER_FIELD_AA(RX_USR_BUF_SIZE),
+ REGISTER_FIELD_AA(RX_XOFF_TX_TH),
+ REGISTER_FIELD_BZ(RX_USR_BUF_SIZE),
+ REGISTER_FIELD_AA(RX_XON_TX_TH),
+ REGISTER_FIELD_AA(RX_OWNERR_CTL),
+ REGISTER_FIELD_BZ(RX_XOFF_TX_TH),
+ REGISTER_FIELD_AA(RX_PCI_BURST_SIZE),
+ REGISTER_FIELD_AA(RX_RDW_PATCH_EN),
+ REGISTER_FIELD_AA(RX_DESC_PUSH_EN),
+ REGISTER_FIELD_BZ(RX_XON_TX_TH),
+ REGISTER_FIELD_BZ(RX_OWNERR_CTL),
+ REGISTER_FIELD_BB(RX_PCI_BURST_SIZE),
+ REGISTER_FIELD_BZ(RX_RDW_PATCH_EN),
+ REGISTER_FIELD_BZ(RX_DESC_PUSH_EN),
+ REGISTER_FIELD_BZ(RX_HASH_INSRT_HDR),
+ REGISTER_FIELD_BZ(RX_HASH_ALG),
+ REGISTER_FIELD_BZ(RX_IP_HASH),
+ REGISTER_FIELD_BZ(RX_INGR_EN),
+ REGISTER_FIELD_BZ(RX_TCP_SUP),
+ REGISTER_FIELD_CZ(RX_PRE_RFF_IPG),
+ REGISTER_FIELD_CZ(RX_HDR_SPLIT_HDR_BUF_SIZE),
+ REGISTER_FIELD_CZ(RX_HDR_SPLIT_PLD_BUF_SIZE),
+ REGISTER_FIELD_CZ(RX_HDR_SPLIT_EN),
+ REGISTER_FIELD_CZ(RX_MIN_KBUF_SIZE),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_FILTER_CTL[] = {
+ REGISTER_FIELD_BZ(TCP_FULL_SRCH_LIMIT),
+ REGISTER_FIELD_BZ(TCP_WILD_SRCH_LIMIT),
+ REGISTER_FIELD_BZ(UDP_WILD_SRCH_LIMIT),
+ REGISTER_FIELD_BZ(NUM_KER),
+ REGISTER_FIELD_BZ(UDP_FULL_SRCH_LIMIT),
+ REGISTER_FIELD_BZ(SCATTER_ENBL_NO_MATCH_Q),
+ REGISTER_FIELD_CZ(UNICAST_NOMATCH_IP_OVERRIDE),
+ REGISTER_FIELD_CZ(UNICAST_NOMATCH_RSS_ENABLED),
+ REGISTER_FIELD_CZ(UNICAST_NOMATCH_Q_ID),
+ REGISTER_FIELD_CZ(MULTICAST_NOMATCH_IP_OVERRIDE),
+ REGISTER_FIELD_CZ(MULTICAST_NOMATCH_RSS_ENABLED),
+ REGISTER_FIELD_CZ(MULTICAST_NOMATCH_Q_ID),
+ REGISTER_FIELD_CZ(RX_VLAN_MATCH_ETHERTYPE),
+ REGISTER_FIELD_CZ(RX_FILTER_ALL_VLAN_ETHERTYPES),
+ REGISTER_FIELD_CZ(ETHERNET_FULL_SEARCH_LIMIT),
+ REGISTER_FIELD_CZ(ETHERNET_WILDCARD_SEARCH_LIMIT),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_DC_CFG[] = {
+ REGISTER_FIELD_AZ(RX_DC_SIZE),
+ REGISTER_FIELD_AB(RX_MAX_PF),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_DC_PF_WM[] = {
+ REGISTER_FIELD_AZ(RX_DC_PF_LWM),
+ REGISTER_FIELD_AZ(RX_DC_PF_HWM),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_RSS_TKEY[] = {
+ REGISTER_FIELD_BZ(RX_RSS_TKEY_LO),
+ REGISTER_FIELD_BZ(RX_RSS_TKEY_HI),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_SELF_RST[] = {
+ REGISTER_FIELD_AA(RX_MAX_LU_LAT),
+ REGISTER_FIELD_AA(RX_MAX_PF_LAT),
+ REGISTER_FIELD_AA(RX_SELF_RST_EN),
+ REGISTER_FIELD_AA(RX_NODESC_WAIT_DIS),
+ REGISTER_FIELD_AA(RX_SW_RST_REG),
+ REGISTER_FIELD_AA(RX_ISCSI_DIS),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_RSS_IPV6_REG1[] = {
+ REGISTER_FIELD_CZ(RX_RSS_IPV6_TKEY_LO),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_RSS_IPV6_REG2[] = {
+ REGISTER_FIELD_CZ(RX_RSS_IPV6_TKEY_MID),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_RSS_IPV6_REG3[] = {
+ REGISTER_FIELD_CZ(RX_RSS_IPV6_TKEY_HI),
+ REGISTER_FIELD_CZ(RX_RSS_IPV6_TCP_SUPPRESS),
+ REGISTER_FIELD_CZ(RX_RSS_IPV6_IP_THASH_ENABLE),
+ REGISTER_FIELD_CZ(RX_RSS_IPV6_THASH_ENABLE),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_DC_CFG[] = {
+ REGISTER_FIELD_AZ(TX_DC_SIZE),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_CHKSM_CFG[] = {
+ REGISTER_FIELD_AA(TX_Q_CHKSM_DIS_0_31),
+ REGISTER_FIELD_AA(TX_Q_CHKSM_DIS_32_63),
+ REGISTER_FIELD_AA(TX_Q_CHKSM_DIS_64_95),
+ REGISTER_FIELD_AA(TX_Q_CHKSM_DIS_96_127),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_CFG[] = {
+ REGISTER_FIELD_AZ(TX_IP_ID_REP_EN),
+ REGISTER_FIELD_AA(TX_NON_IP_DROP_DIS),
+ REGISTER_FIELD_AZ(TX_OWNERR_CTL),
+ REGISTER_FIELD_AZ(TX_P1_PRI_EN),
+ REGISTER_FIELD_AZ(TX_NO_EOP_DISC_EN),
+ REGISTER_FIELD_AZ(TX_IP_ID_P0_OFS),
+ REGISTER_FIELD_CZ(TX_FILTER_EN_BIT),
+ REGISTER_FIELD_CZ(TX_VLAN_MATCH_ETHERTYPE_RANGE),
+ REGISTER_FIELD_CZ(TX_FILTER_ALL_VLAN_ETHERTYPES_BIT),
+ REGISTER_FIELD_CZ(TX_TCPIP_FILTER_FULL_SEARCH_RANGE),
+ REGISTER_FIELD_CZ(TX_TCPIP_FILTER_WILD_SEARCH_RANGE),
+ REGISTER_FIELD_CZ(TX_UDPIP_FILTER_FULL_SEARCH_RANGE),
+ REGISTER_FIELD_CZ(TX_UDPIP_FILTER_WILD_SEARCH_RANGE),
+ REGISTER_FIELD_CZ(TX_ETH_FILTER_FULL_SEARCH_RANGE),
+ REGISTER_FIELD_CZ(TX_ETH_FILTER_WILD_SEARCH_RANGE),
+ REGISTER_FIELD_CZ(TX_FILTER_TEST_MODE_BIT),
+ REGISTER_FIELD_CZ(TX_CONT_LOOKUP_THRESH_RANGE),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_RESERVED[] = {
+ REGISTER_FIELD_AZ(TX_MAX_PREF),
+ REGISTER_FIELD_AZ(TX_MAX_CPL),
+ REGISTER_FIELD_AA(TX_IP_DIS),
+ REGISTER_FIELD_BZ(TX_FLUSH_MIN_LEN_EN),
+ REGISTER_FIELD_AA(TX_TCP_DIS),
+ REGISTER_FIELD_AZ(TX_DMA_SPACER),
+ REGISTER_FIELD_AA(TX_DMA_FF_THR),
+ REGISTER_FIELD_AZ(TX_DIS_NON_IP_EV),
+ REGISTER_FIELD_AZ(TX_ONE_PKT_PER_Q),
+ REGISTER_FIELD_AZ(TX_PREF_THRESHOLD),
+ REGISTER_FIELD_AZ(TX_ONLY1TAG),
+ REGISTER_FIELD_AZ(TX_PREF_WD_TMR),
+ REGISTER_FIELD_AZ(TX_PREF_SPACER),
+ REGISTER_FIELD_AZ(TX_XP_TIMER),
+ REGISTER_FIELD_AZ(TX_RX_SPACER_EN),
+ REGISTER_FIELD_AZ(TX_PS_EVT_DIS),
+ REGISTER_FIELD_AZ(TX_SOFT_EVT_EN),
+ REGISTER_FIELD_AZ(TX_DROP_ABORT_EN),
+ REGISTER_FIELD_AZ(TX_RX_SPACER),
+ REGISTER_FIELD_AZ(TX_DMAQ_ST),
+ REGISTER_FIELD_AZ(TX_DMAR_ST_P0),
+ REGISTER_FIELD_AZ(TX_D_FF_FULL_P0),
+ REGISTER_FIELD_AZ(TX_PUSH_CHK_DIS),
+ REGISTER_FIELD_AZ(TX_PUSH_EN),
+ REGISTER_FIELD_AZ(TX_RD_COMP_TMR),
+ REGISTER_FIELD_AZ(TX_PREF_AGE_CNT),
+ REGISTER_FIELD_AZ(TX_EVT_CNT),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_PACE[] = {
+ REGISTER_FIELD_BZ(TX_PACE_BIN_TH),
+ REGISTER_FIELD_BZ(TX_PACE_FB_BASE),
+ REGISTER_FIELD_BZ(TX_PACE_SB_AF),
+ REGISTER_FIELD_BZ(TX_PACE_SB_NOT_AF),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_VLAN[] = {
+ REGISTER_FIELD_BB(TX_VLAN0),
+ REGISTER_FIELD_BB(TX_VLAN0_PORT0_EN),
+ REGISTER_FIELD_BB(TX_VLAN0_PORT1_EN),
+ REGISTER_FIELD_BB(TX_VLAN1),
+ REGISTER_FIELD_BB(TX_VLAN1_PORT0_EN),
+ REGISTER_FIELD_BB(TX_VLAN1_PORT1_EN),
+ REGISTER_FIELD_BB(TX_VLAN2),
+ REGISTER_FIELD_BB(TX_VLAN2_PORT0_EN),
+ REGISTER_FIELD_BB(TX_VLAN2_PORT1_EN),
+ REGISTER_FIELD_BB(TX_VLAN3),
+ REGISTER_FIELD_BB(TX_VLAN3_PORT0_EN),
+ REGISTER_FIELD_BB(TX_VLAN3_PORT1_EN),
+ REGISTER_FIELD_BB(TX_VLAN4),
+ REGISTER_FIELD_BB(TX_VLAN4_PORT0_EN),
+ REGISTER_FIELD_BB(TX_VLAN4_PORT1_EN),
+ REGISTER_FIELD_BB(TX_VLAN5),
+ REGISTER_FIELD_BB(TX_VLAN5_PORT0_EN),
+ REGISTER_FIELD_BB(TX_VLAN5_PORT1_EN),
+ REGISTER_FIELD_BB(TX_VLAN6),
+ REGISTER_FIELD_BB(TX_VLAN6_PORT0_EN),
+ REGISTER_FIELD_BB(TX_VLAN6_PORT1_EN),
+ REGISTER_FIELD_BB(TX_VLAN7),
+ REGISTER_FIELD_BB(TX_VLAN7_PORT0_EN),
+ REGISTER_FIELD_BB(TX_VLAN7_PORT1_EN),
+ REGISTER_FIELD_BB(TX_VLAN_EN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_IPFIL_PORTEN[] = {
+ REGISTER_FIELD_BB(TX_IPFIL0_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL1_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL2_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL3_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL4_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL5_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL6_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL7_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL8_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL9_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL10_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL11_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL12_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL13_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL14_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL15_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL16_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL17_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL18_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL19_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL20_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL21_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL22_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL23_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL24_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL25_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL26_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL27_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL28_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL29_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL30_PORT_EN),
+ REGISTER_FIELD_BB(TX_IPFIL31_PORT_EN),
+ REGISTER_FIELD_BZ(TX_MADR0_FIL_EN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_IPFIL_TBL[] = {
+ REGISTER_FIELD_BB(TX_IP_SRC_ADR_0),
+ REGISTER_FIELD_BB(TX_IPFIL_MASK_0),
+ REGISTER_FIELD_BB(TX_IP_SRC_ADR_1),
+ REGISTER_FIELD_BB(TX_IPFIL_MASK_1),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MD_TXD[] = {
+ REGISTER_FIELD_AB(MD_TXD),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MD_RXD[] = {
+ REGISTER_FIELD_AB(MD_RXD),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MD_CS[] = {
+ REGISTER_FIELD_AB(MD_WRC),
+ REGISTER_FIELD_AB(MD_RDC),
+ REGISTER_FIELD_AB(MD_RIC),
+ REGISTER_FIELD_AB(MD_PRSP),
+ REGISTER_FIELD_AB(MD_GC),
+ REGISTER_FIELD_AB(MD_INT_CLR),
+ REGISTER_FIELD_AB(MD_PL),
+ REGISTER_FIELD_AB(MD_PT),
+ REGISTER_FIELD_AB(MD_ADDR_CMD),
+ REGISTER_FIELD_AB(MD_WR_EN_CMD),
+ REGISTER_FIELD_AB(MD_RD_EN_CMD),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MD_PHY_ADR[] = {
+ REGISTER_FIELD_AB(MD_PHY_ADR),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MD_ID[] = {
+ REGISTER_FIELD_AB(MD_DEV_ADR),
+ REGISTER_FIELD_AB(MD_PRT_ADR),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MAC_STAT_DMA[] = {
+ REGISTER_FIELD_AB(MAC_STAT_DMA_ADR),
+ REGISTER_FIELD_AB(MAC_STAT_DMA_CMD),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MAC_CTRL[] = {
+ REGISTER_FIELD_AB(MAC_SPEED),
+ REGISTER_FIELD_AB(MAC_LINK_STATUS),
+ REGISTER_FIELD_AB(MAC_UC_PROM),
+ REGISTER_FIELD_AB(MAC_BCAD_ACPT),
+ REGISTER_FIELD_AB(MAC_XG_DISTXCRC),
+ REGISTER_FIELD_BB(TXFIFO_DRAIN_EN),
+ REGISTER_FIELD_AB(MAC_XOFF_VAL),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GEN_MODE[] = {
+ REGISTER_FIELD_BB(XG_PHY_INT_MASK),
+ REGISTER_FIELD_BB(XFP_PHY_INT_MASK),
+ REGISTER_FIELD_BB(XG_PHY_INT_POL_SEL),
+ REGISTER_FIELD_BB(XFP_PHY_INT_POL_SEL),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MAC_MC_HASH_REG0[] = {
+ REGISTER_FIELD_AB(MAC_MCAST_HASH0),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MAC_MC_HASH_REG1[] = {
+ REGISTER_FIELD_AB(MAC_MCAST_HASH1),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GM_CFG1[] = {
+ REGISTER_FIELD_AB(GM_TX_EN),
+ REGISTER_FIELD_AB(GM_SYNC_TXEN),
+ REGISTER_FIELD_AB(GM_RX_EN),
+ REGISTER_FIELD_AB(GM_SYNC_RXEN),
+ REGISTER_FIELD_AB(GM_TX_FC_EN),
+ REGISTER_FIELD_AB(GM_RX_FC_EN),
+ REGISTER_FIELD_AB(GM_LOOP),
+ REGISTER_FIELD_AB(GM_RST_TX_FUNC),
+ REGISTER_FIELD_AB(GM_RST_RX_FUNC),
+ REGISTER_FIELD_AB(GM_RST_TX_MAC_CTL),
+ REGISTER_FIELD_AB(GM_RST_RX_MAC_CTL),
+ REGISTER_FIELD_AB(GM_SIM_RST),
+ REGISTER_FIELD_AB(GM_SW_RST),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GM_CFG2[] = {
+ REGISTER_FIELD_AB(GM_FD),
+ REGISTER_FIELD_AB(GM_CRC_EN),
+ REGISTER_FIELD_AB(GM_PAD_CRC_EN),
+ REGISTER_FIELD_AB(GM_LEN_CHK),
+ REGISTER_FIELD_AB(GM_HUGE_FRM_EN),
+ REGISTER_FIELD_AB(GM_IF_MODE),
+ REGISTER_FIELD_AB(GM_PAMBL_LEN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GM_MAX_FLEN[] = {
+ REGISTER_FIELD_AB(GM_MAX_FLEN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GM_ADR1[] = {
+ REGISTER_FIELD_AB(GM_ADR_B3),
+ REGISTER_FIELD_AB(GM_ADR_B2),
+ REGISTER_FIELD_AB(GM_ADR_B1),
+ REGISTER_FIELD_AB(GM_ADR_B0),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GM_ADR2[] = {
+ REGISTER_FIELD_AB(GM_ADR_B5),
+ REGISTER_FIELD_AB(GM_ADR_B4),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GMF_CFG0[] = {
+ REGISTER_FIELD_AB(GMF_HSTRSTWT),
+ REGISTER_FIELD_AB(GMF_HSTRSTSR),
+ REGISTER_FIELD_AB(GMF_HSTRSTFR),
+ REGISTER_FIELD_AB(GMF_HSTRSTST),
+ REGISTER_FIELD_AB(GMF_HSTRSTFT),
+ REGISTER_FIELD_AB(GMF_WTMENREQ),
+ REGISTER_FIELD_AB(GMF_SRFENREQ),
+ REGISTER_FIELD_AB(GMF_FRFENREQ),
+ REGISTER_FIELD_AB(GMF_STFENREQ),
+ REGISTER_FIELD_AB(GMF_FTFENREQ),
+ REGISTER_FIELD_AB(GMF_WTMENRPLY),
+ REGISTER_FIELD_AB(GMF_SRFENRPLY),
+ REGISTER_FIELD_AB(GMF_FRFENRPLY),
+ REGISTER_FIELD_AB(GMF_STFENRPLY),
+ REGISTER_FIELD_AB(GMF_FTFENRPLY),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GMF_CFG1[] = {
+ REGISTER_FIELD_AB(GMF_CFGXOFFRTX),
+ REGISTER_FIELD_AB(GMF_CFGFRTH),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GMF_CFG2[] = {
+ REGISTER_FIELD_AB(GMF_CFGLWM),
+ REGISTER_FIELD_AB(GMF_CFGHWM),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GMF_CFG3[] = {
+ REGISTER_FIELD_AB(GMF_CFGFTTH),
+ REGISTER_FIELD_AB(GMF_CFGHWMFT),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GMF_CFG4[] = {
+ REGISTER_FIELD_AB(GMF_HSTFLTRFRM),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_GMF_CFG5[] = {
+ REGISTER_FIELD_AB(GMF_HSTFLTRFRMDC),
+ REGISTER_FIELD_AB(GMF_HSTDRPLT64),
+ REGISTER_FIELD_AB(GMF_CFGBYTMODE),
+ REGISTER_FIELD_AB(GMF_HSTSRFULLCLR),
+ REGISTER_FIELD_AB(GMF_SRFULL),
+ REGISTER_FIELD_AB(GMF_CFGHDPLX),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_SRC_MAC_TBL[] = {
+ REGISTER_FIELD_BB(TX_SRC_MAC_ADR_0),
+ REGISTER_FIELD_BB(TX_SRC_MAC_ADR_1),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_SRC_MAC_CTL[] = {
+ REGISTER_FIELD_BB(TX_MAC_QID_SEL),
+ REGISTER_FIELD_BB(TX_DROP_CTR_CLR),
+ REGISTER_FIELD_BB(TX_SRC_FLTR_EN),
+ REGISTER_FIELD_BB(TX_SRC_DROP_CTR),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_ADR_LO[] = {
+ REGISTER_FIELD_AB(XM_ADR_LO),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_ADR_HI[] = {
+ REGISTER_FIELD_AB(XM_ADR_HI),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_GLB_CFG[] = {
+ REGISTER_FIELD_AB(XM_CORE_RST),
+ REGISTER_FIELD_AB(XM_INTCLR_MODE),
+ REGISTER_FIELD_AB(XM_WAN_MODE),
+ REGISTER_FIELD_AB(XM_RX_JUMBO_MODE),
+ REGISTER_FIELD_AB(XM_TX_STAT_EN),
+ REGISTER_FIELD_AB(XM_RX_STAT_EN),
+ REGISTER_FIELD_AB(XM_DEBUG_MODE),
+ REGISTER_FIELD_AB(XM_RMTFLT_GEN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_TX_CFG[] = {
+ REGISTER_FIELD_AB(XM_TX_RST),
+ REGISTER_FIELD_AB(XM_TXEN),
+ REGISTER_FIELD_AB(XM_TX_PRMBL),
+ REGISTER_FIELD_AB(XM_AUTO_PAD),
+ REGISTER_FIELD_AB(XM_EDRC),
+ REGISTER_FIELD_AB(XM_TXCRC),
+ REGISTER_FIELD_AB(XM_FCNTL),
+ REGISTER_FIELD_AB(XM_IPG),
+ REGISTER_FIELD_AB(XM_TX_PROG),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_RX_CFG[] = {
+ REGISTER_FIELD_AB(XM_RX_RST),
+ REGISTER_FIELD_AB(XM_RXEN),
+ REGISTER_FIELD_AB(XM_RX_PRMBL),
+ REGISTER_FIELD_AB(XM_RXCRC),
+ REGISTER_FIELD_AB(XM_AUTO_DEPAD),
+ REGISTER_FIELD_AB(XM_ACPT_ALL_UCAST),
+ REGISTER_FIELD_AB(XM_ACPT_ALL_MCAST),
+ REGISTER_FIELD_AB(XM_REJ_BCAST),
+ REGISTER_FIELD_AB(XM_PASS_PRMBLE_ERR),
+ REGISTER_FIELD_AB(XM_PASS_CRC_ERR),
+ REGISTER_FIELD_AB(XM_PASS_LENERR),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_MGT_INT_MASK[] = {
+ REGISTER_FIELD_AB(XM_MSK_LCLFLT),
+ REGISTER_FIELD_AB(XM_MSK_RMTFLT),
+ REGISTER_FIELD_AB(XM_MSK_PRMBLE_ERR),
+ REGISTER_FIELD_AB(XM_MSK_STAT_CNTR_OF),
+ REGISTER_FIELD_AB(XM_MSK_STAT_CNTR_HF),
+ REGISTER_FIELD_AB(XM_MSK_STA_INTR),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_FC[] = {
+ REGISTER_FIELD_AB(XM_DIS_FCNTL),
+ REGISTER_FIELD_AB(XM_XMIT_PAUSE),
+ REGISTER_FIELD_AB(XM_ZPAUSE),
+ REGISTER_FIELD_AB(XM_REJ_CNTL_MCAST),
+ REGISTER_FIELD_AB(XM_REJ_CNTL_UCAST),
+ REGISTER_FIELD_AB(XM_MCNTL_PASS),
+ REGISTER_FIELD_AB(XM_TX_MAC_STAT),
+ REGISTER_FIELD_AB(XM_RX_MAC_STAT),
+ REGISTER_FIELD_AB(XM_PAUSE_TIME),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_PAUSE_TIME[] = {
+ REGISTER_FIELD_AB(XM_RX_PAUSE_CNT),
+ REGISTER_FIELD_AB(XM_TX_PAUSE_CNT),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_TX_PARAM[] = {
+ REGISTER_FIELD_AB(XM_PAD_CHAR),
+ REGISTER_FIELD_AB(XM_MAX_TX_FRM_SIZE_LO),
+ REGISTER_FIELD_AB(XM_MAX_TX_FRM_SIZE_HI),
+ REGISTER_FIELD_AB(XM_TX_JUMBO_MODE),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XM_RX_PARAM[] = {
+ REGISTER_FIELD_AB(XM_MAX_RX_FRM_SIZE_LO),
+ REGISTER_FIELD_AB(XM_MAX_RX_FRM_SIZE_HI),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XX_PWR_RST[] = {
+ REGISTER_FIELD_AB(XX_RST_XX_EN),
+ REGISTER_FIELD_AB(XX_RSTXGXSTX_EN),
+ REGISTER_FIELD_AB(XX_RSTXGXSRX_EN),
+ REGISTER_FIELD_AB(XX_RESETA_EN),
+ REGISTER_FIELD_AB(XX_RESETB_EN),
+ REGISTER_FIELD_AB(XX_RESETC_EN),
+ REGISTER_FIELD_AB(XX_RESETD_EN),
+ REGISTER_FIELD_AB(XX_RSTPLLAB_EN),
+ REGISTER_FIELD_AB(XX_RSTPLLCD_EN),
+ REGISTER_FIELD_AB(XX_PWRDNA_EN),
+ REGISTER_FIELD_AB(XX_PWRDNB_EN),
+ REGISTER_FIELD_AB(XX_PWRDNC_EN),
+ REGISTER_FIELD_AB(XX_PWRDND_EN),
+ REGISTER_FIELD_AB(XX_SD_RST_ACT),
+ REGISTER_FIELD_AB(XX_RSTXGXSTX_SIG),
+ REGISTER_FIELD_AB(XX_RSTXGXSRX_SIG),
+ REGISTER_FIELD_AB(XX_RESETA_SIG),
+ REGISTER_FIELD_AB(XX_RESETB_SIG),
+ REGISTER_FIELD_AB(XX_RESETC_SIG),
+ REGISTER_FIELD_AB(XX_RESETD_SIG),
+ REGISTER_FIELD_AB(XX_RSTPLLAB_SIG),
+ REGISTER_FIELD_AB(XX_RSTPLLCD_SIG),
+ REGISTER_FIELD_AB(XX_SIM_MODE),
+ REGISTER_FIELD_AB(XX_PWRDNA_SIG),
+ REGISTER_FIELD_AB(XX_PWRDNB_SIG),
+ REGISTER_FIELD_AB(XX_PWRDNC_SIG),
+ REGISTER_FIELD_AB(XX_PWRDND_SIG),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XX_SD_CTL[] = {
+ REGISTER_FIELD_AB(XX_LPBKA),
+ REGISTER_FIELD_AB(XX_LPBKB),
+ REGISTER_FIELD_AB(XX_LPBKC),
+ REGISTER_FIELD_AB(XX_LPBKD),
+ REGISTER_FIELD_AB(XX_LODRVA),
+ REGISTER_FIELD_AB(XX_HIDRVA),
+ REGISTER_FIELD_AB(XX_LODRVB),
+ REGISTER_FIELD_AB(XX_HIDRVB),
+ REGISTER_FIELD_AB(XX_LODRVC),
+ REGISTER_FIELD_AB(XX_HIDRVC),
+ REGISTER_FIELD_AB(XX_LODRVD),
+ REGISTER_FIELD_AB(XX_HIDRVD),
+ REGISTER_FIELD_AB(XX_TERMADJ0),
+ REGISTER_FIELD_AB(XX_TERMADJ1),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_XX_TXDRV_CTL[] = {
+ REGISTER_FIELD_AB(XX_DTXA),
+ REGISTER_FIELD_AB(XX_DTXB),
+ REGISTER_FIELD_AB(XX_DTXC),
+ REGISTER_FIELD_AB(XX_DTXD),
+ REGISTER_FIELD_AB(XX_DEQA),
+ REGISTER_FIELD_AB(XX_DEQB),
+ REGISTER_FIELD_AB(XX_DEQC),
+ REGISTER_FIELD_AB(XX_DEQD),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_BIU_HW_REV_ID[] = {
+ REGISTER_FIELD_DZ(HW_REV_ID),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MC_DB_LWRD[] = {
+ REGISTER_FIELD_DZ(MC_DOORBELL_L),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MC_DB_HWRD[] = {
+ REGISTER_FIELD_DZ(MC_DOORBELL_H),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_DESC_PTR_TBL[] = {
+ /* Abbreviate field names to reduce the table width */
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_EN, "EN"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_JUMBO, "JUMBO"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_TYPE, "TYPE"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_SIZE, "SIZE"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_LABEL, "LABEL"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_OWNER_ID, "OWNER"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_EVQ_ID, "EVQ"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_BUF_BASE_ID, "BUF_BASE"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_SW_WPTR, "SW_WPTR"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESCQ_HW_RPTR, "HW_RPTR"),
+ REGISTER_FIELD_AZ_RENAME(RX_DC_HW_RPTR, "DC_HW_RPTR"),
+ REGISTER_FIELD_AZ_RENAME(RX_DESC_PREF_ACT, "PREF_ACT"),
+ REGISTER_FIELD_AZ_RENAME(RX_ISCSI_HDIG_EN, "HDIG"),
+ REGISTER_FIELD_AZ_RENAME(RX_ISCSI_DDIG_EN, "DDIG"),
+ REGISTER_FIELD_AA(RX_RESET),
+ REGISTER_FIELD_CZ_RENAME(RX_HDR_SPLIT, "HDR_SPLIT"),
+};
+#define efx_nic_reg_fields_RX_DESC_PTR_TBL_KER efx_nic_reg_fields_RX_DESC_PTR_TBL
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_DESC_PTR_TBL[] = {
+ /* Abbreviate field names to reduce the table width */
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_FLUSH, "FLUSH"),
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_TYPE, "TYPE"),
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_SIZE, "SIZE"),
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_LABEL, "LABEL"),
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_OWNER_ID, "OWNER"),
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_EVQ_ID, "EVQ"),
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_BUF_BASE_ID, "BUF_BASE"),
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_SW_WPTR, "SW_WPTR"),
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_HW_RPTR, "HW_RPTR"),
+ REGISTER_FIELD_AZ_RENAME(TX_DC_HW_RPTR, "DC_HW_RPTR"),
+ REGISTER_FIELD_AZ_RENAME(TX_ISCSI_HDIG_EN, "HDIG"),
+ REGISTER_FIELD_AZ_RENAME(TX_ISCSI_DDIG_EN, "DDIG"),
+ REGISTER_FIELD_AZ_RENAME(TX_DESCQ_EN, "EN"),
+ REGISTER_FIELD_BZ_RENAME(TX_TCP_CHKSM_DIS, "!TCP_CHKSM"),
+ REGISTER_FIELD_BZ_RENAME(TX_IP_CHKSM_DIS, "!IP_CHKSM"),
+ REGISTER_FIELD_BZ_RENAME(TX_NON_IP_DROP_DIS, "!NON_IP_DROP"),
+ REGISTER_FIELD_CZ_RENAME(TX_DPT_IP_FILT_EN, "IP_FILT"),
+ REGISTER_FIELD_CZ_RENAME(TX_DPT_ETH_FILT_EN, "ETH_FILT"),
+ REGISTER_FIELD_CZ_RENAME(TX_DPT_Q_MASK_WIDTH, "Q_MASK_WIDTH"),
+};
+#define efx_nic_reg_fields_TX_DESC_PTR_TBL_KER efx_nic_reg_fields_TX_DESC_PTR_TBL
+static const struct efx_nic_reg_field efx_nic_reg_fields_EVQ_PTR_TBL[] = {
+ REGISTER_FIELD_AZ(EVQ_BUF_BASE_ID),
+ REGISTER_FIELD_AZ(EVQ_SIZE),
+ REGISTER_FIELD_AZ(EVQ_EN),
+ REGISTER_FIELD_AZ(EVQ_NXT_WPTR),
+ REGISTER_FIELD_CZ(EVQ_DOS_PROTECT_EN),
+ REGISTER_FIELD_AB(EVQ_WKUP_OR_INT_EN),
+ REGISTER_FIELD_BZ(EVQ_RPTR_IGN),
+};
+#define efx_nic_reg_fields_EVQ_PTR_TBL_KER efx_nic_reg_fields_EVQ_PTR_TBL
+static const struct efx_nic_reg_field efx_nic_reg_fields_BUF_FULL_TBL[] = {
+ REGISTER_FIELD_AZ(BUF_OWNER_ID_FBUF),
+ REGISTER_FIELD_AZ(BUF_ADR_FBUF),
+ REGISTER_FIELD_AZ(BUF_ADR_REGION),
+ REGISTER_FIELD_AZ(IP_DAT_BUF_SIZE),
+ REGISTER_FIELD_AZ(BUF_FULL_UNUSED),
+};
+#define efx_nic_reg_fields_BUF_FULL_TBL_KER efx_nic_reg_fields_BUF_FULL_TBL
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_FILTER_TBL0[] = {
+ /* Source port for full match; destination port for UDP wild match */
+ REGISTER_FIELD_BZ_RENAME(SRC_TCP_DEST_UDP, "SRC_PORT"),
+ REGISTER_FIELD_BZ(SRC_IP),
+ /* Destination port for full match or TCP wild match */
+ REGISTER_FIELD_BZ_RENAME(DEST_PORT_TCP, "DEST_PORT"),
+ REGISTER_FIELD_BZ(DEST_IP),
+ REGISTER_FIELD_BZ(RXQ_ID),
+ REGISTER_FIELD_BZ(TCP_UDP),
+ REGISTER_FIELD_BZ(SCATTER_EN),
+ REGISTER_FIELD_BZ(RSS_EN),
+};
+#define efx_nic_reg_fields_RX_FILTER_TBL1 efx_nic_reg_fields_RX_FILTER_TBL0
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_MAC_FILTER_TBL0[] = {
+ REGISTER_FIELD_CZ(RMFT_VLAN_ID),
+ REGISTER_FIELD_CZ(RMFT_DEST_MAC),
+ REGISTER_FIELD_CZ(RMFT_WILDCARD_MATCH),
+ REGISTER_FIELD_CZ(RMFT_RXQ_ID),
+ REGISTER_FIELD_CZ(RMFT_IP_OVERRIDE),
+ REGISTER_FIELD_CZ(RMFT_SCATTER_EN),
+ REGISTER_FIELD_CZ(RMFT_RSS_EN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TIMER_TBL[] = {
+ REGISTER_FIELD_BB(TIMER_VAL),
+ REGISTER_FIELD_CZ(TIMER_VAL),
+ REGISTER_FIELD_BB(TIMER_MODE),
+ REGISTER_FIELD_CZ(TIMER_MODE),
+ REGISTER_FIELD_CZ(RELOAD_TIMER_VAL),
+ REGISTER_FIELD_CZ(HOST_NOTIFY_MODE),
+ REGISTER_FIELD_CZ(INT_PEND),
+ REGISTER_FIELD_CZ(INT_ARMD),
+ REGISTER_FIELD_CZ(TIMER_Q_EN),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_PACE_TBL[] = {
+ REGISTER_FIELD_BZ(TX_PACE),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_RX_INDIRECTION_TBL[] = {
+ REGISTER_FIELD_BZ(IT_QUEUE),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_TX_MAC_FILTER_TBL0[] = {
+ REGISTER_FIELD_CZ(TMFT_VLAN_ID),
+ REGISTER_FIELD_CZ(TMFT_SRC_MAC),
+ REGISTER_FIELD_CZ(TMFT_WILDCARD_MATCH),
+ REGISTER_FIELD_CZ(TMFT_TXQ_ID),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_MC_TREG_SMEM[] = {
+ REGISTER_FIELD_CZ(MC_TREG_SMEM_ROW),
+};
+static const struct efx_nic_reg_field efx_nic_reg_fields_BIU_MC_SFT_STATUS[] = {
+ REGISTER_FIELD_DZ(MC_SFT_STATUS),
+};
+
+struct efx_nic_reg {
+ const char *name;
+ const struct efx_nic_reg_field *fields;
+ u8 field_count;
+ u8 min_revision, max_revision;
+};
+
+#define REGISTER(name, arch, min_rev, max_rev) { \
+ #name, \
+ efx_nic_reg_fields_ ## name, \
+ ARRAY_SIZE(efx_nic_reg_fields_ ## name), \
+ REGISTER_REVISION_ ## arch ## min_rev, \
+ REGISTER_REVISION_ ## arch ## max_rev \
+}
+#define REGISTER_AA(name) REGISTER(name, F, A, A)
+#define REGISTER_AB(name) REGISTER(name, F, A, B)
+#define REGISTER_AZ(name) REGISTER(name, F, A, Z)
+#define REGISTER_BB(name) REGISTER(name, F, B, B)
+#define REGISTER_BZ(name) REGISTER(name, F, B, Z)
+#define REGISTER_CZ(name) REGISTER(name, F, C, Z)
+#define REGISTER_DZ(name) REGISTER(name, E, D, Z)
+
+static const struct efx_nic_reg efx_nic_regs[] = {
+ REGISTER_AZ(ADR_REGION),
+ REGISTER_AZ(INT_EN_KER),
+ REGISTER_BZ(INT_EN_CHAR),
+ REGISTER_AZ(INT_ADR_KER),
+ REGISTER_BZ(INT_ADR_CHAR),
+ /* INT_ACK_KER is WO */
+ /* INT_ISR0 is RC */
+ REGISTER_AZ(HW_INIT),
+ REGISTER_CZ(USR_EV_CFG),
+ REGISTER_AB(EE_SPI_HCMD),
+ REGISTER_AB(EE_SPI_HADR),
+ REGISTER_AB(EE_SPI_HDATA),
+ REGISTER_AB(EE_BASE_PAGE),
+ REGISTER_AB(EE_VPD_CFG0),
+ /* EE_VPD_SW_CNTL and EE_VPD_SW_DATA are not used */
+ /* PMBX_DBG_IADDR and PBMX_DBG_IDATA are indirect */
+ /* PCIE_CORE_INDIRECT is indirect */
+ REGISTER_AB(NIC_STAT),
+ REGISTER_AB(GPIO_CTL),
+ REGISTER_AB(GLB_CTL),
+ /* FATAL_INTR_KER and FATAL_INTR_CHAR are partly RC */
+ REGISTER_BZ(DP_CTRL),
+ REGISTER_AZ(MEM_STAT),
+ REGISTER_AZ(CS_DEBUG),
+ REGISTER_AZ(ALTERA_BUILD),
+ REGISTER_AZ(CSR_SPARE),
+ REGISTER_AB(PCIE_SD_CTL0123),
+ REGISTER_AB(PCIE_SD_CTL45),
+ REGISTER_AB(PCIE_PCS_CTL_STAT),
+ /* DEBUG_DATA_OUT is not used */
+ /* DRV_EV is WO */
+ REGISTER_AZ(EVQ_CTL),
+ REGISTER_AZ(EVQ_CNT1),
+ REGISTER_AZ(EVQ_CNT2),
+ REGISTER_AZ(BUF_TBL_CFG),
+ REGISTER_AZ(SRM_RX_DC_CFG),
+ REGISTER_AZ(SRM_TX_DC_CFG),
+ REGISTER_AZ(SRM_CFG),
+ /* BUF_TBL_UPD is WO */
+ REGISTER_AZ(SRM_UPD_EVQ),
+ REGISTER_AZ(SRAM_PARITY),
+ REGISTER_AZ(RX_CFG),
+ REGISTER_BZ(RX_FILTER_CTL),
+ /* RX_FLUSH_DESCQ is WO */
+ REGISTER_AZ(RX_DC_CFG),
+ REGISTER_AZ(RX_DC_PF_WM),
+ REGISTER_BZ(RX_RSS_TKEY),
+ /* RX_NODESC_DROP is RC */
+ REGISTER_AA(RX_SELF_RST),
+ /* RX_DEBUG, RX_PUSH_DROP are not used */
+ REGISTER_CZ(RX_RSS_IPV6_REG1),
+ REGISTER_CZ(RX_RSS_IPV6_REG2),
+ REGISTER_CZ(RX_RSS_IPV6_REG3),
+ /* TX_FLUSH_DESCQ is WO */
+ REGISTER_AZ(TX_DC_CFG),
+ REGISTER_AA(TX_CHKSM_CFG),
+ REGISTER_AZ(TX_CFG),
+ /* TX_PUSH_DROP is not used */
+ REGISTER_AZ(TX_RESERVED),
+ REGISTER_BZ(TX_PACE),
+ /* TX_PACE_DROP_QID is RC */
+ REGISTER_BB(TX_VLAN),
+ REGISTER_BZ(TX_IPFIL_PORTEN),
+ REGISTER_AB(MD_TXD),
+ REGISTER_AB(MD_RXD),
+ REGISTER_AB(MD_CS),
+ REGISTER_AB(MD_PHY_ADR),
+ REGISTER_AB(MD_ID),
+ /* MD_STAT is RC */
+ REGISTER_AB(MAC_STAT_DMA),
+ REGISTER_AB(MAC_CTRL),
+ REGISTER_BB(GEN_MODE),
+ REGISTER_AB(MAC_MC_HASH_REG0),
+ REGISTER_AB(MAC_MC_HASH_REG1),
+ REGISTER_AB(GM_CFG1),
+ REGISTER_AB(GM_CFG2),
+ /* GM_IPG and GM_HD are not used */
+ REGISTER_AB(GM_MAX_FLEN),
+ /* GM_TEST is not used */
+ REGISTER_AB(GM_ADR1),
+ REGISTER_AB(GM_ADR2),
+ REGISTER_AB(GMF_CFG0),
+ REGISTER_AB(GMF_CFG1),
+ REGISTER_AB(GMF_CFG2),
+ REGISTER_AB(GMF_CFG3),
+ REGISTER_AB(GMF_CFG4),
+ REGISTER_AB(GMF_CFG5),
+ REGISTER_BB(TX_SRC_MAC_CTL),
+ REGISTER_AB(XM_ADR_LO),
+ REGISTER_AB(XM_ADR_HI),
+ REGISTER_AB(XM_GLB_CFG),
+ REGISTER_AB(XM_TX_CFG),
+ REGISTER_AB(XM_RX_CFG),
+ REGISTER_AB(XM_MGT_INT_MASK),
+ REGISTER_AB(XM_FC),
+ REGISTER_AB(XM_PAUSE_TIME),
+ REGISTER_AB(XM_TX_PARAM),
+ REGISTER_AB(XM_RX_PARAM),
+ /* XM_MGT_INT_MSK (note no 'A') is RC */
+ REGISTER_AB(XX_PWR_RST),
+ REGISTER_AB(XX_SD_CTL),
+ REGISTER_AB(XX_TXDRV_CTL),
+ /* XX_PRBS_CTL, XX_PRBS_CHK and XX_PRBS_ERR are not used */
+ /* XX_CORE_STAT is partly RC */
+ REGISTER_DZ(BIU_HW_REV_ID),
+ REGISTER_DZ(MC_DB_LWRD),
+ REGISTER_DZ(MC_DB_HWRD),
+};
+
+struct efx_nic_reg_table {
+ const char *name;
+ const struct efx_nic_reg_field *fields;
+ u8 field_count;
+ u8 min_revision, max_revision;
+ u8 step;
+ u32 rows;
+};
+
+#define REGISTER_TABLE_DIMENSIONS(name, _, arch, min_rev, max_rev, step, rows) { \
+ #name, \
+ efx_nic_reg_fields_ ## name, \
+ ARRAY_SIZE(efx_nic_reg_fields_ ## name), \
+ REGISTER_REVISION_ ## arch ## min_rev, \
+ REGISTER_REVISION_ ## arch ## max_rev, \
+ step, rows \
+}
+#define REGISTER_TABLE(name, arch, min_rev, max_rev) \
+ REGISTER_TABLE_DIMENSIONS( \
+ name, arch ## R_ ## min_rev ## max_rev ## _ ## name, \
+ arch, min_rev, max_rev, \
+ arch ## R_ ## min_rev ## max_rev ## _ ## name ## _STEP, \
+ arch ## R_ ## min_rev ## max_rev ## _ ## name ## _ROWS)
+#define REGISTER_TABLE_AA(name) REGISTER_TABLE(name, F, A, A)
+#define REGISTER_TABLE_AZ(name) REGISTER_TABLE(name, F, A, Z)
+#define REGISTER_TABLE_BB(name) REGISTER_TABLE(name, F, B, B)
+#define REGISTER_TABLE_BZ(name) REGISTER_TABLE(name, F, B, Z)
+#define REGISTER_TABLE_BB_CZ(name) \
+ REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, F, B, B, \
+ FR_BZ_ ## name ## _STEP, \
+ FR_BB_ ## name ## _ROWS), \
+ REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, F, C, Z, \
+ FR_BZ_ ## name ## _STEP, \
+ FR_CZ_ ## name ## _ROWS)
+#define REGISTER_TABLE_CZ(name) REGISTER_TABLE(name, F, C, Z)
+#define REGISTER_TABLE_DZ(name) REGISTER_TABLE(name, E, D, Z)
+
+static const struct efx_nic_reg_table efx_nic_reg_tables[] = {
+ /* DRIVER is not used */
+ /* EVQ_RPTR, TIMER_COMMAND, USR_EV and {RX,TX}_DESC_UPD are WO */
+ REGISTER_TABLE_BB(TX_IPFIL_TBL),
+ REGISTER_TABLE_BB(TX_SRC_MAC_TBL),
+ REGISTER_TABLE_AA(RX_DESC_PTR_TBL_KER),
+ REGISTER_TABLE_BB_CZ(RX_DESC_PTR_TBL),
+ REGISTER_TABLE_AA(TX_DESC_PTR_TBL_KER),
+ REGISTER_TABLE_BB_CZ(TX_DESC_PTR_TBL),
+ REGISTER_TABLE_AA(EVQ_PTR_TBL_KER),
+ REGISTER_TABLE_BB_CZ(EVQ_PTR_TBL),
+ /* We can't reasonably read all of the buffer table (up to 8MB!).
+ * However this driver will only use a few entries. Reading
+ * 1K entries allows for some expansion of queue count and
+ * size before we need to change the version. */
+ REGISTER_TABLE_DIMENSIONS(BUF_FULL_TBL_KER, FR_AA_BUF_FULL_TBL_KER,
+ F, A, A, 8, 1024),
+ REGISTER_TABLE_DIMENSIONS(BUF_FULL_TBL, FR_BZ_BUF_FULL_TBL,
+ F, B, Z, 8, 1024),
+ REGISTER_TABLE_CZ(RX_MAC_FILTER_TBL0),
+ REGISTER_TABLE_BB_CZ(TIMER_TBL),
+ REGISTER_TABLE_BB_CZ(TX_PACE_TBL),
+ REGISTER_TABLE_BZ(RX_INDIRECTION_TBL),
+ /* TX_FILTER_TBL0 is huge and not used by this driver */
+ REGISTER_TABLE_CZ(TX_MAC_FILTER_TBL0),
+ REGISTER_TABLE_CZ(MC_TREG_SMEM),
+ /* MSIX_PBA_TABLE is not mapped */
+ /* SRM_DBG is not mapped (and is redundant with BUF_FLL_TBL) */
+ REGISTER_TABLE_BZ(RX_FILTER_TBL0),
+ REGISTER_TABLE_DZ(BIU_MC_SFT_STATUS),
+};
+
+static size_t column_width(const struct efx_nic_reg_field *field)
+{
+ size_t name_width, value_width;
+
+ name_width = strlen(field->name);
+ value_width = (field->width + 3) >> 2;
+
+ return name_width > value_width ? name_width : value_width;
+}
+
+static size_t column_padding(const struct efx_nic_reg_field *field)
+{
+ size_t name_width, value_width;
+
+ name_width = strlen(field->name);
+ value_width = (field->width + 3) >> 2;
+
+ return name_width > value_width ? name_width - value_width : 0;
+}
+
+static void
+print_field_value(const struct efx_nic_reg_field *field, const u8 *buf)
+{
+ unsigned left, right, sig_bits, digit;
+
+ right = field->lbn;
+ left = right + ((field->width + 3) & ~3);
+
+ /* How many bits are valid for the most significant hex digit? */
+ sig_bits = (field->width & 3) ? (field->width & 3) : 4;
+
+ while (left > right) {
+ left -= 4;
+ digit = buf[left >> 3];
+ if ((left & 7) + sig_bits > 8)
+ digit |= buf[(left >> 3) + 1] << 8;
+ digit = (digit >> (left & 7)) & ((1 << sig_bits) - 1);
+ printf("%x", digit);
+ sig_bits = 4; /* for all subsequent digits */
+ }
+}
+
+static const void *
+print_single_register(unsigned revision, const struct efx_nic_reg *reg,
+ const void *buf)
+{
+ const struct efx_nic_reg_field *field;
+ int indent = 0;
+ size_t i;
+
+ for (i = 0; i < reg->field_count; i++) {
+ field = &reg->fields[i];
+ if (revision >= field->min_revision &&
+ revision <= field->max_revision) {
+ if (indent == 0)
+ indent = printf("%s: ", reg->name);
+ else
+ printf("%*s", indent, "");
+ printf("%s = ", field->name);
+ print_field_value(field, buf);
+ fputc('\n', stdout);
+ }
+ }
+
+ return (const u8 *)buf + 16;
+}
+
+static const void *
+print_simple_table(const struct efx_nic_reg_table *table, const void *buf)
+{
+ const struct efx_nic_reg_field *field = &table->fields[0];
+ size_t value_width = (field->width + 3) >> 2;
+ size_t column_count = 72 / (value_width + 1);
+ size_t size = table->step > 16 ? 16 : table->step;
+ size_t i;
+
+ for (i = 0; i < table->rows; i++) {
+ if (i % column_count == 0) {
+ if (i != 0)
+ fputc('\n', stdout);
+ printf("%4zu ", i);
+ }
+ fputc(' ', stdout);
+ print_field_value(field, buf);
+ buf = (const u8 *)buf + size;
+ }
+ fputc('\n', stdout);
+
+ return buf;
+}
+
+static int buf_is_zero(const u8 *buf, size_t size)
+{
+ size_t i;
+ for (i = 0; i < size; i++)
+ if (buf[i])
+ return 0;
+ return 1;
+}
+
+static const void *
+print_complex_table(unsigned revision, const struct efx_nic_reg_table *table,
+ const void *buf)
+{
+ const struct efx_nic_reg_field *field;
+ size_t size = table->step > 16 ? 16 : table->step;
+ size_t i, j;
+
+ /* Column headings */
+ fputs("Row ", stdout);
+ for (i = 0; i < table->field_count; i++) {
+ field = &table->fields[i];
+ if (revision >= field->min_revision &&
+ revision <= field->max_revision)
+ printf(" %-*s", (int)column_width(field),
+ field->name);
+ }
+ fputc('\n', stdout);
+ fputs("----", stdout);
+ for (i = 0; i < table->field_count; i++) {
+ field = &table->fields[i];
+ if (revision >= field->min_revision &&
+ revision <= field->max_revision) {
+ fputc(' ', stdout);
+ for (j = column_width(field); j > 0; j--)
+ fputc('-', stdout);
+ }
+ }
+ fputc('\n', stdout);
+
+ for (j = 0; j < table->rows; j++) {
+ if (!buf_is_zero(buf, size)) {
+ printf("%4zu", j);
+ for (i = 0; i < table->field_count; i++) {
+ field = &table->fields[i];
+ if (!(revision >= field->min_revision &&
+ revision <= field->max_revision))
+ continue;
+ printf(" %*s", (int)column_padding(field), "");
+ print_field_value(field, buf);
+ }
+ fputc('\n', stdout);
+ }
+ buf = (const u8 *)buf + size;
+ }
+
+ return buf;
+}
+
+int
+sfc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ const struct efx_nic_reg *reg;
+ const struct efx_nic_reg_table *table;
+ unsigned revision = regs->version;
+ const void *buf = regs->data;
+ const void *end = regs->data + regs->len;
+
+ if (revision > REGISTER_REVISION_ED)
+ return -1;
+
+ for (reg = efx_nic_regs;
+ reg < efx_nic_regs + ARRAY_SIZE(efx_nic_regs) && buf < end;
+ reg++) {
+ if (revision >= reg->min_revision &&
+ revision <= reg->max_revision)
+ buf = print_single_register(revision, reg, buf);
+ }
+
+ for (table = efx_nic_reg_tables;
+ table < efx_nic_reg_tables + ARRAY_SIZE(efx_nic_reg_tables) &&
+ buf < end;
+ table++) {
+ if (revision >= table->min_revision &&
+ revision <= table->max_revision) {
+ printf("\n%s:\n", table->name);
+ if (table->field_count == 1)
+ buf = print_simple_table(table, buf);
+ else
+ buf = print_complex_table(revision, table, buf);
+ }
+ }
+
+ return 0;
+}
diff --git a/sff-common.c b/sff-common.c
new file mode 100644
index 0000000..a412a6e
--- /dev/null
+++ b/sff-common.c
@@ -0,0 +1,387 @@
+/*
+ * sff-common.c: Implements SFF-8024 Rev 4.0 i.e. Specifcation
+ * of pluggable I/O configuration
+ *
+ * Common utilities across SFF-8436/8636 and SFF-8472/8079
+ * are defined in this file
+ *
+ * Copyright 2010 Solarflare Communications Inc.
+ * Aurelien Guillaume <aurelien@iwi.me> (C) 2012
+ * Copyright (C) 2014 Cumulus networks Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Freeoftware Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Vidya Sagar Ravipati <vidya@cumulusnetworks.com>
+ * This implementation is loosely based on current SFP parser
+ * and SFF-8024 Rev 4.0 spec (ftp://ftp.seagate.com/pub/sff/SFF-8024.PDF)
+ * by SFF Committee.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include "sff-common.h"
+
+double convert_mw_to_dbm(double mw)
+{
+ return (10. * log10(mw / 1000.)) + 30.;
+}
+
+void sff_show_value_with_unit(const __u8 *id, unsigned int reg,
+ const char *name, unsigned int mult,
+ const char *unit)
+{
+ unsigned int val = id[reg];
+
+ printf("\t%-41s : %u%s\n", name, val * mult, unit);
+}
+
+void sff_show_ascii(const __u8 *id, unsigned int first_reg,
+ unsigned int last_reg, const char *name)
+{
+ unsigned int reg, val;
+
+ printf("\t%-41s : ", name);
+ while (first_reg <= last_reg && id[last_reg] == ' ')
+ last_reg--;
+ for (reg = first_reg; reg <= last_reg; reg++) {
+ val = id[reg];
+ putchar(((val >= 32) && (val <= 126)) ? val : '_');
+ }
+ printf("\n");
+}
+
+void sff_show_lane_status(const char *name, unsigned int lane_cnt,
+ const char *yes, const char *no, unsigned int value)
+{
+ printf("\t%-41s : ", name);
+ if (!value) {
+ printf("None\n");
+ return;
+ }
+
+ printf("[");
+ while (lane_cnt--) {
+ printf(" %s%c", value & 1 ? yes : no, lane_cnt ? ',': ' ');
+ value >>= 1;
+ }
+ printf("]\n");
+}
+
+void sff8024_show_oui(const __u8 *id, int id_offset)
+{
+ printf("\t%-41s : %02x:%02x:%02x\n", "Vendor OUI",
+ id[id_offset], id[(id_offset) + 1],
+ id[(id_offset) + 2]);
+}
+
+void sff8024_show_identifier(const __u8 *id, int id_offset)
+{
+ printf("\t%-41s : 0x%02x", "Identifier", id[id_offset]);
+ switch (id[id_offset]) {
+ case SFF8024_ID_UNKNOWN:
+ printf(" (no module present, unknown, or unspecified)\n");
+ break;
+ case SFF8024_ID_GBIC:
+ printf(" (GBIC)\n");
+ break;
+ case SFF8024_ID_SOLDERED_MODULE:
+ printf(" (module soldered to motherboard)\n");
+ break;
+ case SFF8024_ID_SFP:
+ printf(" (SFP)\n");
+ break;
+ case SFF8024_ID_300_PIN_XBI:
+ printf(" (300 pin XBI)\n");
+ break;
+ case SFF8024_ID_XENPAK:
+ printf(" (XENPAK)\n");
+ break;
+ case SFF8024_ID_XFP:
+ printf(" (XFP)\n");
+ break;
+ case SFF8024_ID_XFF:
+ printf(" (XFF)\n");
+ break;
+ case SFF8024_ID_XFP_E:
+ printf(" (XFP-E)\n");
+ break;
+ case SFF8024_ID_XPAK:
+ printf(" (XPAK)\n");
+ break;
+ case SFF8024_ID_X2:
+ printf(" (X2)\n");
+ break;
+ case SFF8024_ID_DWDM_SFP:
+ printf(" (DWDM-SFP)\n");
+ break;
+ case SFF8024_ID_QSFP:
+ printf(" (QSFP)\n");
+ break;
+ case SFF8024_ID_QSFP_PLUS:
+ printf(" (QSFP+)\n");
+ break;
+ case SFF8024_ID_CXP:
+ printf(" (CXP)\n");
+ break;
+ case SFF8024_ID_HD4X:
+ printf(" (Shielded Mini Multilane HD 4X)\n");
+ break;
+ case SFF8024_ID_HD8X:
+ printf(" (Shielded Mini Multilane HD 8X)\n");
+ break;
+ case SFF8024_ID_QSFP28:
+ printf(" (QSFP28)\n");
+ break;
+ case SFF8024_ID_CXP2:
+ printf(" (CXP2/CXP28)\n");
+ break;
+ case SFF8024_ID_CDFP:
+ printf(" (CDFP Style 1/Style 2)\n");
+ break;
+ case SFF8024_ID_HD4X_FANOUT:
+ printf(" (Shielded Mini Multilane HD 4X Fanout Cable)\n");
+ break;
+ case SFF8024_ID_HD8X_FANOUT:
+ printf(" (Shielded Mini Multilane HD 8X Fanout Cable)\n");
+ break;
+ case SFF8024_ID_CDFP_S3:
+ printf(" (CDFP Style 3)\n");
+ break;
+ case SFF8024_ID_MICRO_QSFP:
+ printf(" (microQSFP)\n");
+ break;
+ case SFF8024_ID_QSFP_DD:
+ printf(" (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))\n");
+ break;
+ case SFF8024_ID_OSFP:
+ printf(" (OSFP 8X Pluggable Transceiver)\n");
+ break;
+ case SFF8024_ID_DSFP:
+ printf(" (DSFP Dual Small Form Factor Pluggable Transceiver)\n");
+ break;
+ case SFF8024_ID_QSFP_PLUS_CMIS:
+ printf(" (QSFP+ or later with Common Management Interface Specification (CMIS))\n");
+ break;
+ case SFF8024_ID_SFP_DD_CMIS:
+ printf(" (SFP-DD Double Density 2X Pluggable Transceiver with Common Management Interface Specification (CMIS))\n");
+ break;
+ case SFF8024_ID_SFP_PLUS_CMIS:
+ printf(" (SFP+ and later with Common Management Interface Specification (CMIS))\n");
+ break;
+ default:
+ printf(" (reserved or unknown)\n");
+ break;
+ }
+}
+
+void sff8024_show_connector(const __u8 *id, int ctor_offset)
+{
+ printf("\t%-41s : 0x%02x", "Connector", id[ctor_offset]);
+ switch (id[ctor_offset]) {
+ case SFF8024_CTOR_UNKNOWN:
+ printf(" (unknown or unspecified)\n");
+ break;
+ case SFF8024_CTOR_SC:
+ printf(" (SC)\n");
+ break;
+ case SFF8024_CTOR_FC_STYLE_1:
+ printf(" (Fibre Channel Style 1 copper)\n");
+ break;
+ case SFF8024_CTOR_FC_STYLE_2:
+ printf(" (Fibre Channel Style 2 copper)\n");
+ break;
+ case SFF8024_CTOR_BNC_TNC:
+ printf(" (BNC/TNC)\n");
+ break;
+ case SFF8024_CTOR_FC_COAX:
+ printf(" (Fibre Channel coaxial headers)\n");
+ break;
+ case SFF8024_CTOR_FIBER_JACK:
+ printf(" (FibreJack)\n");
+ break;
+ case SFF8024_CTOR_LC:
+ printf(" (LC)\n");
+ break;
+ case SFF8024_CTOR_MT_RJ:
+ printf(" (MT-RJ)\n");
+ break;
+ case SFF8024_CTOR_MU:
+ printf(" (MU)\n");
+ break;
+ case SFF8024_CTOR_SG:
+ printf(" (SG)\n");
+ break;
+ case SFF8024_CTOR_OPT_PT:
+ printf(" (Optical pigtail)\n");
+ break;
+ case SFF8024_CTOR_MPO:
+ printf(" (MPO Parallel Optic)\n");
+ break;
+ case SFF8024_CTOR_MPO_2:
+ printf(" (MPO Parallel Optic - 2x16)\n");
+ break;
+ case SFF8024_CTOR_HSDC_II:
+ printf(" (HSSDC II)\n");
+ break;
+ case SFF8024_CTOR_COPPER_PT:
+ printf(" (Copper pigtail)\n");
+ break;
+ case SFF8024_CTOR_RJ45:
+ printf(" (RJ45)\n");
+ break;
+ case SFF8024_CTOR_NO_SEPARABLE:
+ printf(" (No separable connector)\n");
+ break;
+ case SFF8024_CTOR_MXC_2x16:
+ printf(" (MXC 2x16)\n");
+ break;
+ case SFF8024_CTOR_CS_OPTICAL:
+ printf(" (CS optical connector)\n");
+ break;
+ case SFF8024_CTOR_CS_OPTICAL_MINI:
+ printf(" (Mini CS optical connector)\n");
+ break;
+ case SFF8024_CTOR_MPO_2X12:
+ printf(" (MPO 2x12)\n");
+ break;
+ case SFF8024_CTOR_MPO_1X16:
+ printf(" (MPO 1x16)\n");
+ break;
+ default:
+ printf(" (reserved or unknown)\n");
+ break;
+ }
+}
+
+void sff8024_show_encoding(const __u8 *id, int encoding_offset, int sff_type)
+{
+ printf("\t%-41s : 0x%02x", "Encoding", id[encoding_offset]);
+ switch (id[encoding_offset]) {
+ case SFF8024_ENCODING_UNSPEC:
+ printf(" (unspecified)\n");
+ break;
+ case SFF8024_ENCODING_8B10B:
+ printf(" (8B/10B)\n");
+ break;
+ case SFF8024_ENCODING_4B5B:
+ printf(" (4B/5B)\n");
+ break;
+ case SFF8024_ENCODING_NRZ:
+ printf(" (NRZ)\n");
+ break;
+ case SFF8024_ENCODING_4h:
+ if (sff_type == ETH_MODULE_SFF_8472)
+ printf(" (Manchester)\n");
+ else if (sff_type == ETH_MODULE_SFF_8636)
+ printf(" (SONET Scrambled)\n");
+ break;
+ case SFF8024_ENCODING_5h:
+ if (sff_type == ETH_MODULE_SFF_8472)
+ printf(" (SONET Scrambled)\n");
+ else if (sff_type == ETH_MODULE_SFF_8636)
+ printf(" (64B/66B)\n");
+ break;
+ case SFF8024_ENCODING_6h:
+ if (sff_type == ETH_MODULE_SFF_8472)
+ printf(" (64B/66B)\n");
+ else if (sff_type == ETH_MODULE_SFF_8636)
+ printf(" (Manchester)\n");
+ break;
+ case SFF8024_ENCODING_256B:
+ printf(" ((256B/257B (transcoded FEC-enabled data))\n");
+ break;
+ case SFF8024_ENCODING_PAM4:
+ printf(" (PAM4)\n");
+ break;
+ default:
+ printf(" (reserved or unknown)\n");
+ break;
+ }
+}
+
+void sff_show_thresholds(struct sff_diags sd)
+{
+ PRINT_BIAS("Laser bias current high alarm threshold",
+ sd.bias_cur[HALRM]);
+ PRINT_BIAS("Laser bias current low alarm threshold",
+ sd.bias_cur[LALRM]);
+ PRINT_BIAS("Laser bias current high warning threshold",
+ sd.bias_cur[HWARN]);
+ PRINT_BIAS("Laser bias current low warning threshold",
+ sd.bias_cur[LWARN]);
+
+ PRINT_xX_PWR("Laser output power high alarm threshold",
+ sd.tx_power[HALRM]);
+ PRINT_xX_PWR("Laser output power low alarm threshold",
+ sd.tx_power[LALRM]);
+ PRINT_xX_PWR("Laser output power high warning threshold",
+ sd.tx_power[HWARN]);
+ PRINT_xX_PWR("Laser output power low warning threshold",
+ sd.tx_power[LWARN]);
+
+ PRINT_TEMP("Module temperature high alarm threshold",
+ sd.sfp_temp[HALRM]);
+ PRINT_TEMP("Module temperature low alarm threshold",
+ sd.sfp_temp[LALRM]);
+ PRINT_TEMP("Module temperature high warning threshold",
+ sd.sfp_temp[HWARN]);
+ PRINT_TEMP("Module temperature low warning threshold",
+ sd.sfp_temp[LWARN]);
+
+ PRINT_VCC("Module voltage high alarm threshold",
+ sd.sfp_voltage[HALRM]);
+ PRINT_VCC("Module voltage low alarm threshold",
+ sd.sfp_voltage[LALRM]);
+ PRINT_VCC("Module voltage high warning threshold",
+ sd.sfp_voltage[HWARN]);
+ PRINT_VCC("Module voltage low warning threshold",
+ sd.sfp_voltage[LWARN]);
+
+ PRINT_xX_PWR("Laser rx power high alarm threshold",
+ sd.rx_power[HALRM]);
+ PRINT_xX_PWR("Laser rx power low alarm threshold",
+ sd.rx_power[LALRM]);
+ PRINT_xX_PWR("Laser rx power high warning threshold",
+ sd.rx_power[HWARN]);
+ PRINT_xX_PWR("Laser rx power low warning threshold",
+ sd.rx_power[LWARN]);
+}
+
+void sff_show_revision_compliance(const __u8 *id, int rev_offset)
+{
+ static const char *pfx =
+ "\tRevision Compliance :";
+
+ switch (id[rev_offset]) {
+ case SFF8636_REV_UNSPECIFIED:
+ printf("%s Revision not specified\n", pfx);
+ break;
+ case SFF8636_REV_8436_48:
+ printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
+ break;
+ case SFF8636_REV_8436_8636:
+ printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
+ break;
+ case SFF8636_REV_8636_13:
+ printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx);
+ break;
+ case SFF8636_REV_8636_14:
+ printf("%s SFF-8636 Rev 1.4\n", pfx);
+ break;
+ case SFF8636_REV_8636_15:
+ printf("%s SFF-8636 Rev 1.5\n", pfx);
+ break;
+ case SFF8636_REV_8636_20:
+ printf("%s SFF-8636 Rev 2.0\n", pfx);
+ break;
+ case SFF8636_REV_8636_27:
+ printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx);
+ break;
+ default:
+ printf("%s Unallocated\n", pfx);
+ break;
+ }
+}
diff --git a/sff-common.h b/sff-common.h
new file mode 100644
index 0000000..899dc5b
--- /dev/null
+++ b/sff-common.h
@@ -0,0 +1,214 @@
+/*
+ * sff-common.h: Implements SFF-8024 Rev 4.0 i.e. Specifcation
+ * of pluggable I/O configuration
+ *
+ * Common utilities across SFF-8436/8636 and SFF-8472/8079
+ * are defined in this file
+ *
+ * Copyright 2010 Solarflare Communications Inc.
+ * Aurelien Guillaume <aurelien@iwi.me> (C) 2012
+ * Copyright (C) 2014 Cumulus networks Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Freeoftware Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Vidya Sagar Ravipati <vidya@cumulusnetworks.com>
+ * This implementation is loosely based on current SFP parser
+ * and SFF-8024 specs (ftp://ftp.seagate.com/pub/sff/SFF-8024.PDF)
+ * by SFF Committee.
+ */
+
+#ifndef SFF_COMMON_H__
+#define SFF_COMMON_H__
+
+#include <stdio.h>
+#include "internal.h"
+
+/* Revision compliance */
+#define SFF8636_REV_UNSPECIFIED 0x00
+#define SFF8636_REV_8436_48 0x01
+#define SFF8636_REV_8436_8636 0x02
+#define SFF8636_REV_8636_13 0x03
+#define SFF8636_REV_8636_14 0x04
+#define SFF8636_REV_8636_15 0x05
+#define SFF8636_REV_8636_20 0x06
+#define SFF8636_REV_8636_27 0x07
+
+#define SFF8024_ID_OFFSET 0x00
+#define SFF8024_ID_UNKNOWN 0x00
+#define SFF8024_ID_GBIC 0x01
+#define SFF8024_ID_SOLDERED_MODULE 0x02
+#define SFF8024_ID_SFP 0x03
+#define SFF8024_ID_300_PIN_XBI 0x04
+#define SFF8024_ID_XENPAK 0x05
+#define SFF8024_ID_XFP 0x06
+#define SFF8024_ID_XFF 0x07
+#define SFF8024_ID_XFP_E 0x08
+#define SFF8024_ID_XPAK 0x09
+#define SFF8024_ID_X2 0x0A
+#define SFF8024_ID_DWDM_SFP 0x0B
+#define SFF8024_ID_QSFP 0x0C
+#define SFF8024_ID_QSFP_PLUS 0x0D
+#define SFF8024_ID_CXP 0x0E
+#define SFF8024_ID_HD4X 0x0F
+#define SFF8024_ID_HD8X 0x10
+#define SFF8024_ID_QSFP28 0x11
+#define SFF8024_ID_CXP2 0x12
+#define SFF8024_ID_CDFP 0x13
+#define SFF8024_ID_HD4X_FANOUT 0x14
+#define SFF8024_ID_HD8X_FANOUT 0x15
+#define SFF8024_ID_CDFP_S3 0x16
+#define SFF8024_ID_MICRO_QSFP 0x17
+#define SFF8024_ID_QSFP_DD 0x18
+#define SFF8024_ID_OSFP 0x19
+#define SFF8024_ID_DSFP 0x1B
+#define SFF8024_ID_QSFP_PLUS_CMIS 0x1E
+#define SFF8024_ID_SFP_DD_CMIS 0x1F
+#define SFF8024_ID_SFP_PLUS_CMIS 0x20
+#define SFF8024_ID_LAST SFF8024_ID_SFP_PLUS_CMIS
+#define SFF8024_ID_UNALLOCATED_LAST 0x7F
+#define SFF8024_ID_VENDOR_START 0x80
+#define SFF8024_ID_VENDOR_LAST 0xFF
+
+#define SFF8024_CTOR_UNKNOWN 0x00
+#define SFF8024_CTOR_SC 0x01
+#define SFF8024_CTOR_FC_STYLE_1 0x02
+#define SFF8024_CTOR_FC_STYLE_2 0x03
+#define SFF8024_CTOR_BNC_TNC 0x04
+#define SFF8024_CTOR_FC_COAX 0x05
+#define SFF8024_CTOR_FIBER_JACK 0x06
+#define SFF8024_CTOR_LC 0x07
+#define SFF8024_CTOR_MT_RJ 0x08
+#define SFF8024_CTOR_MU 0x09
+#define SFF8024_CTOR_SG 0x0A
+#define SFF8024_CTOR_OPT_PT 0x0B
+#define SFF8024_CTOR_MPO 0x0C
+#define SFF8024_CTOR_MPO_2 0x0D
+/* 0E-1Fh --- Reserved */
+#define SFF8024_CTOR_HSDC_II 0x20
+#define SFF8024_CTOR_COPPER_PT 0x21
+#define SFF8024_CTOR_RJ45 0x22
+#define SFF8024_CTOR_NO_SEPARABLE 0x23
+#define SFF8024_CTOR_MXC_2x16 0x24
+#define SFF8024_CTOR_CS_OPTICAL 0x25
+#define SFF8024_CTOR_CS_OPTICAL_MINI 0x26
+#define SFF8024_CTOR_MPO_2X12 0x27
+#define SFF8024_CTOR_MPO_1X16 0x28
+#define SFF8024_CTOR_LAST SFF8024_CTOR_MPO_1X16
+
+#define SFF8024_CTOR_NO_SEP_QSFP_DD 0x6F
+#define SFF8024_CTOR_UNALLOCATED_LAST 0x7F
+#define SFF8024_CTOR_VENDOR_START 0x80
+#define SFF8024_CTOR_VENDOR_LAST 0xFF
+
+/* ENCODING Values */
+#define SFF8024_ENCODING_UNSPEC 0x00
+#define SFF8024_ENCODING_8B10B 0x01
+#define SFF8024_ENCODING_4B5B 0x02
+#define SFF8024_ENCODING_NRZ 0x03
+/*
+ * Value: 04h
+ * SFF-8472 - Manchester
+ * SFF-8436/8636 - SONET Scrambled
+ */
+#define SFF8024_ENCODING_4h 0x04
+/*
+ * Value: 05h
+ * SFF-8472 - SONET Scrambled
+ * SFF-8436/8636 - 64B/66B
+ */
+#define SFF8024_ENCODING_5h 0x05
+/*
+ * Value: 06h
+ * SFF-8472 - 64B/66B
+ * SFF-8436/8636 - Manchester
+ */
+#define SFF8024_ENCODING_6h 0x06
+#define SFF8024_ENCODING_256B 0x07
+#define SFF8024_ENCODING_PAM4 0x08
+
+/* Most common case: 16-bit unsigned integer in a certain unit */
+#define OFFSET_TO_U16_PTR(ptr, offset) (ptr[offset] << 8 | ptr[(offset) + 1])
+#define OFFSET_TO_U16(offset) OFFSET_TO_U16_PTR(id, offset)
+
+# define PRINT_xX_PWR(string, var) \
+ printf("\t%-41s : %.4f mW / %.2f dBm\n", (string), \
+ (double)((var) / 10000.), \
+ convert_mw_to_dbm((double)((var) / 10000.)))
+
+#define PRINT_BIAS(string, bias_cur) \
+ printf("\t%-41s : %.3f mA\n", (string), \
+ (double)(bias_cur / 500.))
+
+#define PRINT_TEMP(string, temp) \
+ printf("\t%-41s : %.2f degrees C / %.2f degrees F\n", \
+ (string), (double)(temp / 256.), \
+ (double)(temp / 256. * 1.8 + 32.))
+
+#define PRINT_VCC(string, sfp_voltage) \
+ printf("\t%-41s : %.4f V\n", (string), \
+ (double)(sfp_voltage / 10000.))
+
+# define PRINT_xX_THRESH_PWR(string, var, index) \
+ PRINT_xX_PWR(string, (var)[(index)])
+
+/* Channel Monitoring Fields */
+struct sff_channel_diags {
+ __u16 bias_cur; /* Measured bias current in 2uA units */
+ __u16 rx_power; /* Measured RX Power */
+ __u16 tx_power; /* Measured TX Power */
+};
+
+/* Module Monitoring Fields */
+struct sff_diags {
+
+#define MAX_CHANNEL_NUM 32
+#define LWARN 0
+#define HWARN 1
+#define LALRM 2
+#define HALRM 3
+#define MCURR 4
+
+ /* Supports DOM */
+ __u8 supports_dom;
+ /* Supports alarm/warning thold */
+ __u8 supports_alarms;
+ /* RX Power: 0 = OMA, 1 = Average power */
+ __u8 rx_power_type;
+ /* TX Power: 0 = Not supported, 1 = Average power */
+ __u8 tx_power_type;
+
+ __u8 calibrated_ext; /* Is externally calibrated */
+ /* [5] tables are low/high warn, low/high alarm, current */
+ /* SFP voltage in 0.1mV units */
+ __u16 sfp_voltage[5];
+ /* SFP Temp in 16-bit signed 1/256 Celcius */
+ __s16 sfp_temp[5];
+ /* Measured bias current in 2uA units */
+ __u16 bias_cur[5];
+ /* Measured TX Power */
+ __u16 tx_power[5];
+ /* Measured RX Power */
+ __u16 rx_power[5];
+ struct sff_channel_diags scd[MAX_CHANNEL_NUM];
+};
+
+double convert_mw_to_dbm(double mw);
+void sff_show_value_with_unit(const __u8 *id, unsigned int reg,
+ const char *name, unsigned int mult,
+ const char *unit);
+void sff_show_ascii(const __u8 *id, unsigned int first_reg,
+ unsigned int last_reg, const char *name);
+void sff_show_lane_status(const char *name, unsigned int lane_cnt,
+ const char *yes, const char *no, unsigned int value);
+void sff_show_thresholds(struct sff_diags sd);
+
+void sff8024_show_oui(const __u8 *id, int id_offset);
+void sff8024_show_identifier(const __u8 *id, int id_offset);
+void sff8024_show_connector(const __u8 *id, int ctor_offset);
+void sff8024_show_encoding(const __u8 *id, int encoding_offset, int sff_type);
+void sff_show_revision_compliance(const __u8 *id, int rev_offset);
+
+#endif /* SFF_COMMON_H__ */
diff --git a/sfpdiag.c b/sfpdiag.c
new file mode 100644
index 0000000..1fa8b7b
--- /dev/null
+++ b/sfpdiag.c
@@ -0,0 +1,281 @@
+/*
+ * sfpdiag.c: Implements SFF-8472 optics diagnostics.
+ *
+ * Aurelien Guillaume <aurelien@iwi.me> (C) 2012
+ * This implementation is loosely based on DOM patches
+ * from Robert Olsson <robert@herjulf.se> (C) 2009
+ * and SFF-8472 specs (ftp://ftp.seagate.com/pub/sff/SFF-8472.PDF)
+ * by SFF Committee.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <arpa/inet.h>
+#include "internal.h"
+#include "sff-common.h"
+
+/* Offsets in decimal, for direct comparison with the SFF specs */
+
+/* A0-based EEPROM offsets for DOM support checks */
+#define SFF_A0_DOM 92
+#define SFF_A0_OPTIONS 93
+#define SFF_A0_COMP 94
+
+/* EEPROM bit values for various registers */
+#define SFF_A0_DOM_EXTCAL (1 << 4)
+#define SFF_A0_DOM_INTCAL (1 << 5)
+#define SFF_A0_DOM_IMPL (1 << 6)
+#define SFF_A0_DOM_PWRT (1 << 3)
+
+#define SFF_A0_OPTIONS_AW (1 << 7)
+
+/*
+ * See ethtool.c comments about SFF-8472, this is the offset
+ * at which the A2 page is in the EEPROM blob returned by the
+ * kernel.
+ */
+#define SFF_A2_BASE 0x100
+
+/* A2-based offsets for DOM */
+#define SFF_A2_TEMP 96
+#define SFF_A2_TEMP_HALRM 0
+#define SFF_A2_TEMP_LALRM 2
+#define SFF_A2_TEMP_HWARN 4
+#define SFF_A2_TEMP_LWARN 6
+
+#define SFF_A2_VCC 98
+#define SFF_A2_VCC_HALRM 8
+#define SFF_A2_VCC_LALRM 10
+#define SFF_A2_VCC_HWARN 12
+#define SFF_A2_VCC_LWARN 14
+
+#define SFF_A2_BIAS 100
+#define SFF_A2_BIAS_HALRM 16
+#define SFF_A2_BIAS_LALRM 18
+#define SFF_A2_BIAS_HWARN 20
+#define SFF_A2_BIAS_LWARN 22
+
+#define SFF_A2_TX_PWR 102
+#define SFF_A2_TX_PWR_HALRM 24
+#define SFF_A2_TX_PWR_LALRM 26
+#define SFF_A2_TX_PWR_HWARN 28
+#define SFF_A2_TX_PWR_LWARN 30
+
+#define SFF_A2_RX_PWR 104
+#define SFF_A2_RX_PWR_HALRM 32
+#define SFF_A2_RX_PWR_LALRM 34
+#define SFF_A2_RX_PWR_HWARN 36
+#define SFF_A2_RX_PWR_LWARN 38
+
+#define SFF_A2_ALRM_FLG 112
+#define SFF_A2_WARN_FLG 116
+
+/* 32-bit little-endian calibration constants */
+#define SFF_A2_CAL_RXPWR4 56
+#define SFF_A2_CAL_RXPWR3 60
+#define SFF_A2_CAL_RXPWR2 64
+#define SFF_A2_CAL_RXPWR1 68
+#define SFF_A2_CAL_RXPWR0 72
+
+/* 16-bit little endian calibration constants */
+#define SFF_A2_CAL_TXI_SLP 76
+#define SFF_A2_CAL_TXI_OFF 78
+#define SFF_A2_CAL_TXPWR_SLP 80
+#define SFF_A2_CAL_TXPWR_OFF 82
+#define SFF_A2_CAL_T_SLP 84
+#define SFF_A2_CAL_T_OFF 86
+#define SFF_A2_CAL_V_SLP 88
+#define SFF_A2_CAL_V_OFF 90
+
+static struct sff8472_aw_flags {
+ const char *str; /* Human-readable string, null at the end */
+ int offset; /* A2-relative address offset */
+ __u8 value; /* Alarm is on if (offset & value) != 0. */
+} sff8472_aw_flags[] = {
+ { "Laser bias current high alarm", SFF_A2_ALRM_FLG, (1 << 3) },
+ { "Laser bias current low alarm", SFF_A2_ALRM_FLG, (1 << 2) },
+ { "Laser bias current high warning", SFF_A2_WARN_FLG, (1 << 3) },
+ { "Laser bias current low warning", SFF_A2_WARN_FLG, (1 << 2) },
+
+ { "Laser output power high alarm", SFF_A2_ALRM_FLG, (1 << 1) },
+ { "Laser output power low alarm", SFF_A2_ALRM_FLG, (1 << 0) },
+ { "Laser output power high warning", SFF_A2_WARN_FLG, (1 << 1) },
+ { "Laser output power low warning", SFF_A2_WARN_FLG, (1 << 0) },
+
+ { "Module temperature high alarm", SFF_A2_ALRM_FLG, (1 << 7) },
+ { "Module temperature low alarm", SFF_A2_ALRM_FLG, (1 << 6) },
+ { "Module temperature high warning", SFF_A2_WARN_FLG, (1 << 7) },
+ { "Module temperature low warning", SFF_A2_WARN_FLG, (1 << 6) },
+
+ { "Module voltage high alarm", SFF_A2_ALRM_FLG, (1 << 5) },
+ { "Module voltage low alarm", SFF_A2_ALRM_FLG, (1 << 4) },
+ { "Module voltage high warning", SFF_A2_WARN_FLG, (1 << 5) },
+ { "Module voltage low warning", SFF_A2_WARN_FLG, (1 << 4) },
+
+ { "Laser rx power high alarm", SFF_A2_ALRM_FLG + 1, (1 << 7) },
+ { "Laser rx power low alarm", SFF_A2_ALRM_FLG + 1, (1 << 6) },
+ { "Laser rx power high warning", SFF_A2_WARN_FLG + 1, (1 << 7) },
+ { "Laser rx power low warning", SFF_A2_WARN_FLG + 1, (1 << 6) },
+
+ { NULL, 0, 0 },
+};
+
+/* Most common case: 16-bit unsigned integer in a certain unit */
+#define A2_OFFSET_TO_U16(offset) \
+ (id[SFF_A2_BASE + (offset)] << 8 | id[SFF_A2_BASE + (offset) + 1])
+
+/* Calibration slope is a number between 0.0 included and 256.0 excluded. */
+#define A2_OFFSET_TO_SLP(offset) \
+ (id[SFF_A2_BASE + (offset)] + id[SFF_A2_BASE + (offset) + 1] / 256.)
+
+/* Calibration offset is an integer from -32768 to 32767 */
+#define A2_OFFSET_TO_OFF(offset) \
+ ((__s16)A2_OFFSET_TO_U16(offset))
+
+/* RXPWR(x) are IEEE-754 floating point numbers in big-endian format */
+#define A2_OFFSET_TO_RXPWRx(offset) \
+ (befloattoh((__u32 *)(id + SFF_A2_BASE + (offset))))
+
+/*
+ * 2-byte internal temperature conversions:
+ * First byte is a signed 8-bit integer, which is the temp decimal part
+ * Second byte are 1/256th of degree, which are added to the dec part.
+ */
+#define A2_OFFSET_TO_TEMP(offset) ((__s16)A2_OFFSET_TO_U16(offset))
+
+static void sff8472_dom_parse(const __u8 *id, struct sff_diags *sd)
+{
+ sd->bias_cur[MCURR] = A2_OFFSET_TO_U16(SFF_A2_BIAS);
+ sd->bias_cur[HALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HALRM);
+ sd->bias_cur[LALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LALRM);
+ sd->bias_cur[HWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HWARN);
+ sd->bias_cur[LWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LWARN);
+
+ sd->sfp_voltage[MCURR] = A2_OFFSET_TO_U16(SFF_A2_VCC);
+ sd->sfp_voltage[HALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_HALRM);
+ sd->sfp_voltage[LALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_LALRM);
+ sd->sfp_voltage[HWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_HWARN);
+ sd->sfp_voltage[LWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_LWARN);
+
+ sd->tx_power[MCURR] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR);
+ sd->tx_power[HALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HALRM);
+ sd->tx_power[LALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LALRM);
+ sd->tx_power[HWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HWARN);
+ sd->tx_power[LWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LWARN);
+
+ sd->rx_power[MCURR] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR);
+ sd->rx_power[HALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HALRM);
+ sd->rx_power[LALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LALRM);
+ sd->rx_power[HWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HWARN);
+ sd->rx_power[LWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LWARN);
+
+ sd->sfp_temp[MCURR] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP);
+ sd->sfp_temp[HALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HALRM);
+ sd->sfp_temp[LALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LALRM);
+ sd->sfp_temp[HWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HWARN);
+ sd->sfp_temp[LWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LWARN);
+}
+
+/* Converts to a float from a big-endian 4-byte source buffer. */
+static float befloattoh(const __u32 *source)
+{
+ union {
+ __u32 src;
+ float dst;
+ } converter;
+
+ converter.src = ntohl(*source);
+ return converter.dst;
+}
+
+static void sff8472_calibration(const __u8 *id, struct sff_diags *sd)
+{
+ __u16 rx_reading;
+ unsigned int i;
+
+ /* Calibration should occur for all values (threshold and current) */
+ for (i = 0; i < ARRAY_SIZE(sd->bias_cur); ++i) {
+ /*
+ * Apply calibration formula 1 (Temp., Voltage, Bias, Tx Power)
+ */
+ sd->bias_cur[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXI_SLP);
+ sd->tx_power[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXPWR_SLP);
+ sd->sfp_voltage[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_V_SLP);
+ sd->sfp_temp[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_T_SLP);
+
+ sd->bias_cur[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXI_OFF);
+ sd->tx_power[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXPWR_OFF);
+ sd->sfp_voltage[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_V_OFF);
+ sd->sfp_temp[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_T_OFF);
+
+ /*
+ * Apply calibration formula 2 (Rx Power only)
+ */
+ rx_reading = sd->rx_power[i];
+ sd->rx_power[i] = A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR0);
+ sd->rx_power[i] += rx_reading *
+ A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR1);
+ sd->rx_power[i] += rx_reading *
+ A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR2);
+ sd->rx_power[i] += rx_reading *
+ A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR3);
+ }
+}
+
+static void sff8472_parse_eeprom(const __u8 *id, struct sff_diags *sd)
+{
+ sd->supports_dom = id[SFF_A0_DOM] & SFF_A0_DOM_IMPL;
+ sd->supports_alarms = id[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW;
+ sd->calibrated_ext = id[SFF_A0_DOM] & SFF_A0_DOM_EXTCAL;
+ sd->rx_power_type = id[SFF_A0_DOM] & SFF_A0_DOM_PWRT;
+
+ sff8472_dom_parse(id, sd);
+
+ /*
+ * If the SFP is externally calibrated, we need to read calibration data
+ * and compensate the already stored readings.
+ */
+ if (sd->calibrated_ext)
+ sff8472_calibration(id, sd);
+}
+
+void sff8472_show_all(const __u8 *id)
+{
+ struct sff_diags sd = {0};
+ char *rx_power_string = NULL;
+ int i;
+
+ sff8472_parse_eeprom(id, &sd);
+
+ if (!sd.supports_dom) {
+ printf("\t%-41s : No\n", "Optical diagnostics support");
+ return;
+ }
+ printf("\t%-41s : Yes\n", "Optical diagnostics support");
+
+ PRINT_BIAS("Laser bias current", sd.bias_cur[MCURR]);
+ PRINT_xX_PWR("Laser output power", sd.tx_power[MCURR]);
+
+ if (!sd.rx_power_type)
+ rx_power_string = "Receiver signal OMA";
+ else
+ rx_power_string = "Receiver signal average optical power";
+
+ PRINT_xX_PWR(rx_power_string, sd.rx_power[MCURR]);
+
+ PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]);
+ PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]);
+
+ printf("\t%-41s : %s\n", "Alarm/warning flags implemented",
+ (sd.supports_alarms ? "Yes" : "No"));
+ if (sd.supports_alarms) {
+
+ for (i = 0; sff8472_aw_flags[i].str; ++i) {
+ printf("\t%-41s : %s\n", sff8472_aw_flags[i].str,
+ id[SFF_A2_BASE + sff8472_aw_flags[i].offset]
+ & sff8472_aw_flags[i].value ? "On" : "Off");
+ }
+ sff_show_thresholds(sd);
+ }
+}
+
diff --git a/sfpid.c b/sfpid.c
new file mode 100644
index 0000000..1bc45c1
--- /dev/null
+++ b/sfpid.c
@@ -0,0 +1,505 @@
+/****************************************************************************
+ * Support for Solarflare Solarstorm network controllers and boards
+ * Copyright 2010 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "internal.h"
+#include "sff-common.h"
+#include "netlink/extapi.h"
+
+#define SFF8079_PAGE_SIZE 0x80
+#define SFF8079_I2C_ADDRESS_LOW 0x50
+#define SFF8079_I2C_ADDRESS_HIGH 0x51
+
+static void sff8079_show_identifier(const __u8 *id)
+{
+ sff8024_show_identifier(id, 0);
+}
+
+static void sff8079_show_ext_identifier(const __u8 *id)
+{
+ printf("\t%-41s : 0x%02x", "Extended identifier", id[1]);
+ if (id[1] == 0x00)
+ printf(" (GBIC not specified / not MOD_DEF compliant)\n");
+ else if (id[1] == 0x04)
+ printf(" (GBIC/SFP defined by 2-wire interface ID)\n");
+ else if (id[1] <= 0x07)
+ printf(" (GBIC compliant with MOD_DEF %u)\n", id[1]);
+ else
+ printf(" (unknown)\n");
+}
+
+static void sff8079_show_connector(const __u8 *id)
+{
+ sff8024_show_connector(id, 2);
+}
+
+static void sff8079_show_transceiver(const __u8 *id)
+{
+ static const char *pfx =
+ "\tTransceiver type :";
+
+ printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \
+ "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ "Transceiver codes",
+ id[3], id[4], id[5], id[6],
+ id[7], id[8], id[9], id[10], id[36]);
+ /* 10G Ethernet Compliance Codes */
+ if (id[3] & (1 << 7))
+ printf("%s 10G Ethernet: 10G Base-ER" \
+ " [SFF-8472 rev10.4 onwards]\n", pfx);
+ if (id[3] & (1 << 6))
+ printf("%s 10G Ethernet: 10G Base-LRM\n", pfx);
+ if (id[3] & (1 << 5))
+ printf("%s 10G Ethernet: 10G Base-LR\n", pfx);
+ if (id[3] & (1 << 4))
+ printf("%s 10G Ethernet: 10G Base-SR\n", pfx);
+ /* Infiniband Compliance Codes */
+ if (id[3] & (1 << 3))
+ printf("%s Infiniband: 1X SX\n", pfx);
+ if (id[3] & (1 << 2))
+ printf("%s Infiniband: 1X LX\n", pfx);
+ if (id[3] & (1 << 1))
+ printf("%s Infiniband: 1X Copper Active\n", pfx);
+ if (id[3] & (1 << 0))
+ printf("%s Infiniband: 1X Copper Passive\n", pfx);
+ /* ESCON Compliance Codes */
+ if (id[4] & (1 << 7))
+ printf("%s ESCON: ESCON MMF, 1310nm LED\n", pfx);
+ if (id[4] & (1 << 6))
+ printf("%s ESCON: ESCON SMF, 1310nm Laser\n", pfx);
+ /* SONET Compliance Codes */
+ if (id[4] & (1 << 5))
+ printf("%s SONET: OC-192, short reach\n", pfx);
+ if (id[4] & (1 << 4))
+ printf("%s SONET: SONET reach specifier bit 1\n", pfx);
+ if (id[4] & (1 << 3))
+ printf("%s SONET: SONET reach specifier bit 2\n", pfx);
+ if (id[4] & (1 << 2))
+ printf("%s SONET: OC-48, long reach\n", pfx);
+ if (id[4] & (1 << 1))
+ printf("%s SONET: OC-48, intermediate reach\n", pfx);
+ if (id[4] & (1 << 0))
+ printf("%s SONET: OC-48, short reach\n", pfx);
+ if (id[5] & (1 << 6))
+ printf("%s SONET: OC-12, single mode, long reach\n", pfx);
+ if (id[5] & (1 << 5))
+ printf("%s SONET: OC-12, single mode, inter. reach\n", pfx);
+ if (id[5] & (1 << 4))
+ printf("%s SONET: OC-12, short reach\n", pfx);
+ if (id[5] & (1 << 2))
+ printf("%s SONET: OC-3, single mode, long reach\n", pfx);
+ if (id[5] & (1 << 1))
+ printf("%s SONET: OC-3, single mode, inter. reach\n", pfx);
+ if (id[5] & (1 << 0))
+ printf("%s SONET: OC-3, short reach\n", pfx);
+ /* Ethernet Compliance Codes */
+ if (id[6] & (1 << 7))
+ printf("%s Ethernet: BASE-PX\n", pfx);
+ if (id[6] & (1 << 6))
+ printf("%s Ethernet: BASE-BX10\n", pfx);
+ if (id[6] & (1 << 5))
+ printf("%s Ethernet: 100BASE-FX\n", pfx);
+ if (id[6] & (1 << 4))
+ printf("%s Ethernet: 100BASE-LX/LX10\n", pfx);
+ if (id[6] & (1 << 3))
+ printf("%s Ethernet: 1000BASE-T\n", pfx);
+ if (id[6] & (1 << 2))
+ printf("%s Ethernet: 1000BASE-CX\n", pfx);
+ if (id[6] & (1 << 1))
+ printf("%s Ethernet: 1000BASE-LX\n", pfx);
+ if (id[6] & (1 << 0))
+ printf("%s Ethernet: 1000BASE-SX\n", pfx);
+ /* Fibre Channel link length */
+ if (id[7] & (1 << 7))
+ printf("%s FC: very long distance (V)\n", pfx);
+ if (id[7] & (1 << 6))
+ printf("%s FC: short distance (S)\n", pfx);
+ if (id[7] & (1 << 5))
+ printf("%s FC: intermediate distance (I)\n", pfx);
+ if (id[7] & (1 << 4))
+ printf("%s FC: long distance (L)\n", pfx);
+ if (id[7] & (1 << 3))
+ printf("%s FC: medium distance (M)\n", pfx);
+ /* Fibre Channel transmitter technology */
+ if (id[7] & (1 << 2))
+ printf("%s FC: Shortwave laser, linear Rx (SA)\n", pfx);
+ if (id[7] & (1 << 1))
+ printf("%s FC: Longwave laser (LC)\n", pfx);
+ if (id[7] & (1 << 0))
+ printf("%s FC: Electrical inter-enclosure (EL)\n", pfx);
+ if (id[8] & (1 << 7))
+ printf("%s FC: Electrical intra-enclosure (EL)\n", pfx);
+ if (id[8] & (1 << 6))
+ printf("%s FC: Shortwave laser w/o OFC (SN)\n", pfx);
+ if (id[8] & (1 << 5))
+ printf("%s FC: Shortwave laser with OFC (SL)\n", pfx);
+ if (id[8] & (1 << 4))
+ printf("%s FC: Longwave laser (LL)\n", pfx);
+ if (id[8] & (1 << 3))
+ printf("%s Active Cable\n", pfx);
+ if (id[8] & (1 << 2))
+ printf("%s Passive Cable\n", pfx);
+ if (id[8] & (1 << 1))
+ printf("%s FC: Copper FC-BaseT\n", pfx);
+ /* Fibre Channel transmission media */
+ if (id[9] & (1 << 7))
+ printf("%s FC: Twin Axial Pair (TW)\n", pfx);
+ if (id[9] & (1 << 6))
+ printf("%s FC: Twisted Pair (TP)\n", pfx);
+ if (id[9] & (1 << 5))
+ printf("%s FC: Miniature Coax (MI)\n", pfx);
+ if (id[9] & (1 << 4))
+ printf("%s FC: Video Coax (TV)\n", pfx);
+ if (id[9] & (1 << 3))
+ printf("%s FC: Multimode, 62.5um (M6)\n", pfx);
+ if (id[9] & (1 << 2))
+ printf("%s FC: Multimode, 50um (M5)\n", pfx);
+ if (id[9] & (1 << 0))
+ printf("%s FC: Single Mode (SM)\n", pfx);
+ /* Fibre Channel speed */
+ if (id[10] & (1 << 7))
+ printf("%s FC: 1200 MBytes/sec\n", pfx);
+ if (id[10] & (1 << 6))
+ printf("%s FC: 800 MBytes/sec\n", pfx);
+ if (id[10] & (1 << 4))
+ printf("%s FC: 400 MBytes/sec\n", pfx);
+ if (id[10] & (1 << 2))
+ printf("%s FC: 200 MBytes/sec\n", pfx);
+ if (id[10] & (1 << 0))
+ printf("%s FC: 100 MBytes/sec\n", pfx);
+ /* Extended Specification Compliance Codes from SFF-8024 */
+ if (id[36] == 0x1)
+ printf("%s Extended: 100G AOC or 25GAUI C2M AOC with worst BER of 5x10^(-5)\n", pfx);
+ if (id[36] == 0x2)
+ printf("%s Extended: 100G Base-SR4 or 25GBase-SR\n", pfx);
+ if (id[36] == 0x3)
+ printf("%s Extended: 100G Base-LR4 or 25GBase-LR\n", pfx);
+ if (id[36] == 0x4)
+ printf("%s Extended: 100G Base-ER4 or 25GBase-ER\n", pfx);
+ if (id[36] == 0x8)
+ printf("%s Extended: 100G ACC or 25GAUI C2M ACC with worst BER of 5x10^(-5)\n", pfx);
+ if (id[36] == 0xb)
+ printf("%s Extended: 100G Base-CR4 or 25G Base-CR CA-L\n", pfx);
+ if (id[36] == 0xc)
+ printf("%s Extended: 25G Base-CR CA-S\n", pfx);
+ if (id[36] == 0xd)
+ printf("%s Extended: 25G Base-CR CA-N\n", pfx);
+ if (id[36] == 0x16)
+ printf("%s Extended: 10Gbase-T with SFI electrical interface\n", pfx);
+ if (id[36] == 0x18)
+ printf("%s Extended: 100G AOC or 25GAUI C2M AOC with worst BER of 10^(-12)\n", pfx);
+ if (id[36] == 0x19)
+ printf("%s Extended: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)\n", pfx);
+ if (id[36] == 0x1a)
+ printf("%s Extended: 100GE-DWDM2 (DWDM transceiver using 2 wavelengths on a 1550 nm DWDM grid with a reach up to 80 km)\n",
+ pfx);
+ if (id[36] == 0x1b)
+ printf("%s Extended: 100G 1550nm WDM (4 wavelengths)\n", pfx);
+ if (id[36] == 0x1c)
+ printf("%s Extended: 10Gbase-T Short Reach\n", pfx);
+ if (id[36] == 0x1d)
+ printf("%s Extended: 5GBASE-T\n", pfx);
+ if (id[36] == 0x1e)
+ printf("%s Extended: 2.5GBASE-T\n", pfx);
+ if (id[36] == 0x1f)
+ printf("%s Extended: 40G SWDM4\n", pfx);
+ if (id[36] == 0x20)
+ printf("%s Extended: 100G SWDM4\n", pfx);
+ if (id[36] == 0x21)
+ printf("%s Extended: 100G PAM4 BiDi\n", pfx);
+ if (id[36] == 0x22)
+ printf("%s Extended: 4WDM-10 MSA (10km version of 100G CWDM4 with same RS(528,514) FEC in host system)\n",
+ pfx);
+ if (id[36] == 0x23)
+ printf("%s Extended: 4WDM-20 MSA (20km version of 100GBASE-LR4 with RS(528,514) FEC in host system)\n",
+ pfx);
+ if (id[36] == 0x24)
+ printf("%s Extended: 4WDM-40 MSA (40km reach with APD receiver and RS(528,514) FEC in host system)\n",
+ pfx);
+ if (id[36] == 0x25)
+ printf("%s Extended: 100GBASE-DR (clause 140), CAUI-4 (no FEC)\n", pfx);
+ if (id[36] == 0x26)
+ printf("%s Extended: 100G-FR or 100GBASE-FR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
+ if (id[36] == 0x27)
+ printf("%s Extended: 100G-LR or 100GBASE-LR1 (clause 140), CAUI-4 (no FEC)\n", pfx);
+ if (id[36] == 0x30)
+ printf("%s Extended: Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
+ pfx);
+ if (id[36] == 0x31)
+ printf("%s Extended: Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 10-6 or below\n",
+ pfx);
+ if (id[36] == 0x32)
+ printf("%s Extended: Active Copper Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
+ pfx);
+ if (id[36] == 0x33)
+ printf("%s Extended: Active Optical Cable with 50GAUI, 100GAUI-2 or 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC, 10-5 for AUI, or below\n",
+ pfx);
+ if (id[36] == 0x40)
+ printf("%s Extended: 50GBASE-CR, 100GBASE-CR2, or 200GBASE-CR4\n", pfx);
+ if (id[36] == 0x41)
+ printf("%s Extended: 50GBASE-SR, 100GBASE-SR2, or 200GBASE-SR4\n", pfx);
+ if (id[36] == 0x42)
+ printf("%s Extended: 50GBASE-FR or 200GBASE-DR4\n", pfx);
+ if (id[36] == 0x43)
+ printf("%s Extended: 200GBASE-FR4\n", pfx);
+ if (id[36] == 0x44)
+ printf("%s Extended: 200G 1550 nm PSM4\n", pfx);
+ if (id[36] == 0x45)
+ printf("%s Extended: 50GBASE-LR\n", pfx);
+ if (id[36] == 0x46)
+ printf("%s Extended: 200GBASE-LR4\n", pfx);
+ if (id[36] == 0x50)
+ printf("%s Extended: 64GFC EA\n", pfx);
+ if (id[36] == 0x51)
+ printf("%s Extended: 64GFC SW\n", pfx);
+ if (id[36] == 0x52)
+ printf("%s Extended: 64GFC LW\n", pfx);
+ if (id[36] == 0x53)
+ printf("%s Extended: 128GFC EA\n", pfx);
+ if (id[36] == 0x54)
+ printf("%s Extended: 128GFC SW\n", pfx);
+ if (id[36] == 0x55)
+ printf("%s Extended: 128GFC LW\n", pfx);
+}
+
+static void sff8079_show_encoding(const __u8 *id)
+{
+ sff8024_show_encoding(id, 11, ETH_MODULE_SFF_8472);
+}
+
+static void sff8079_show_rate_identifier(const __u8 *id)
+{
+ printf("\t%-41s : 0x%02x", "Rate identifier", id[13]);
+ switch (id[13]) {
+ case 0x00:
+ printf(" (unspecified)\n");
+ break;
+ case 0x01:
+ printf(" (4/2/1G Rate_Select & AS0/AS1)\n");
+ break;
+ case 0x02:
+ printf(" (8/4/2G Rx Rate_Select only)\n");
+ break;
+ case 0x03:
+ printf(" (8/4/2G Independent Rx & Tx Rate_Select)\n");
+ break;
+ case 0x04:
+ printf(" (8/4/2G Tx Rate_Select only)\n");
+ break;
+ default:
+ printf(" (reserved or unknown)\n");
+ break;
+ }
+}
+
+static void sff8079_show_oui(const __u8 *id)
+{
+ printf("\t%-41s : %02x:%02x:%02x\n", "Vendor OUI",
+ id[37], id[38], id[39]);
+}
+
+static void sff8079_show_wavelength_or_copper_compliance(const __u8 *id)
+{
+ if (id[8] & (1 << 2)) {
+ printf("\t%-41s : 0x%02x", "Passive Cu cmplnce.", id[60]);
+ switch (id[60]) {
+ case 0x00:
+ printf(" (unspecified)");
+ break;
+ case 0x01:
+ printf(" (SFF-8431 appendix E)");
+ break;
+ default:
+ printf(" (unknown)");
+ break;
+ }
+ printf(" [SFF-8472 rev10.4 only]\n");
+ } else if (id[8] & (1 << 3)) {
+ printf("\t%-41s : 0x%02x", "Active Cu cmplnce.", id[60]);
+ switch (id[60]) {
+ case 0x00:
+ printf(" (unspecified)");
+ break;
+ case 0x01:
+ printf(" (SFF-8431 appendix E)");
+ break;
+ case 0x04:
+ printf(" (SFF-8431 limiting)");
+ break;
+ default:
+ printf(" (unknown)");
+ break;
+ }
+ printf(" [SFF-8472 rev10.4 only]\n");
+ } else {
+ printf("\t%-41s : %unm\n", "Laser wavelength",
+ (id[60] << 8) | id[61]);
+ }
+}
+
+static void sff8079_show_value_with_unit(const __u8 *id, unsigned int reg,
+ const char *name, unsigned int mult,
+ const char *unit)
+{
+ unsigned int val = id[reg];
+
+ printf("\t%-41s : %u%s\n", name, val * mult, unit);
+}
+
+static void sff8079_show_ascii(const __u8 *id, unsigned int first_reg,
+ unsigned int last_reg, const char *name)
+{
+ unsigned int reg, val;
+
+ printf("\t%-41s : ", name);
+ while (first_reg <= last_reg && id[last_reg] == ' ')
+ last_reg--;
+ for (reg = first_reg; reg <= last_reg; reg++) {
+ val = id[reg];
+ putchar(((val >= 32) && (val <= 126)) ? val : '_');
+ }
+ printf("\n");
+}
+
+static void sff8079_show_options(const __u8 *id)
+{
+ static const char *pfx =
+ "\tOption :";
+
+ printf("\t%-41s : 0x%02x 0x%02x\n", "Option values", id[64], id[65]);
+ if (id[65] & (1 << 1))
+ printf("%s RX_LOS implemented\n", pfx);
+ if (id[65] & (1 << 2))
+ printf("%s RX_LOS implemented, inverted\n", pfx);
+ if (id[65] & (1 << 3))
+ printf("%s TX_FAULT implemented\n", pfx);
+ if (id[65] & (1 << 4))
+ printf("%s TX_DISABLE implemented\n", pfx);
+ if (id[65] & (1 << 5))
+ printf("%s RATE_SELECT implemented\n", pfx);
+ if (id[65] & (1 << 6))
+ printf("%s Tunable transmitter technology\n", pfx);
+ if (id[65] & (1 << 7))
+ printf("%s Receiver decision threshold implemented\n", pfx);
+ if (id[64] & (1 << 0))
+ printf("%s Linear receiver output implemented\n", pfx);
+ if (id[64] & (1 << 1))
+ printf("%s Power level 2 requirement\n", pfx);
+ if (id[64] & (1 << 2))
+ printf("%s Cooled transceiver implemented\n", pfx);
+ if (id[64] & (1 << 3))
+ printf("%s Retimer or CDR implemented\n", pfx);
+ if (id[64] & (1 << 4))
+ printf("%s Paging implemented\n", pfx);
+ if (id[64] & (1 << 5))
+ printf("%s Power level 3 requirement\n", pfx);
+}
+
+static void sff8079_show_all_common(const __u8 *id)
+{
+ sff8079_show_identifier(id);
+ if (((id[0] == 0x02) || (id[0] == 0x03)) && (id[1] == 0x04)) {
+ unsigned int br_nom, br_min, br_max;
+
+ if (id[12] == 0) {
+ br_nom = br_min = br_max = 0;
+ } else if (id[12] == 255) {
+ br_nom = id[66] * 250;
+ br_max = id[67];
+ br_min = id[67];
+ } else {
+ br_nom = id[12] * 100;
+ br_max = id[66];
+ br_min = id[67];
+ }
+ sff8079_show_ext_identifier(id);
+ sff8079_show_connector(id);
+ sff8079_show_transceiver(id);
+ sff8079_show_encoding(id);
+ printf("\t%-41s : %u%s\n", "BR, Nominal", br_nom, "MBd");
+ sff8079_show_rate_identifier(id);
+ sff8079_show_value_with_unit(id, 14,
+ "Length (SMF,km)", 1, "km");
+ sff8079_show_value_with_unit(id, 15, "Length (SMF)", 100, "m");
+ sff8079_show_value_with_unit(id, 16, "Length (50um)", 10, "m");
+ sff8079_show_value_with_unit(id, 17,
+ "Length (62.5um)", 10, "m");
+ sff8079_show_value_with_unit(id, 18, "Length (Copper)", 1, "m");
+ sff8079_show_value_with_unit(id, 19, "Length (OM3)", 10, "m");
+ sff8079_show_wavelength_or_copper_compliance(id);
+ sff8079_show_ascii(id, 20, 35, "Vendor name");
+ sff8079_show_oui(id);
+ sff8079_show_ascii(id, 40, 55, "Vendor PN");
+ sff8079_show_ascii(id, 56, 59, "Vendor rev");
+ sff8079_show_options(id);
+ printf("\t%-41s : %u%s\n", "BR margin, max", br_max, "%");
+ printf("\t%-41s : %u%s\n", "BR margin, min", br_min, "%");
+ sff8079_show_ascii(id, 68, 83, "Vendor SN");
+ sff8079_show_ascii(id, 84, 91, "Date code");
+ }
+}
+
+void sff8079_show_all_ioctl(const __u8 *id)
+{
+ sff8079_show_all_common(id);
+}
+
+static int sff8079_get_eeprom_page(struct cmd_context *ctx, u8 i2c_address,
+ __u8 *buf)
+{
+ struct ethtool_module_eeprom request = {
+ .length = SFF8079_PAGE_SIZE,
+ .i2c_address = i2c_address,
+ };
+ int ret;
+
+ ret = nl_get_eeprom_page(ctx, &request);
+ if (!ret)
+ memcpy(buf, request.data, SFF8079_PAGE_SIZE);
+
+ return ret;
+}
+
+int sff8079_show_all_nl(struct cmd_context *ctx)
+{
+ u8 *buf;
+ int ret;
+
+ /* The SFF-8472 parser expects a single buffer that contains the
+ * concatenation of the first 256 bytes from addresses A0h and A2h,
+ * respectively.
+ */
+ buf = calloc(1, ETH_MODULE_SFF_8472_LEN);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Read A0h page */
+ ret = sff8079_get_eeprom_page(ctx, SFF8079_I2C_ADDRESS_LOW, buf);
+ if (ret)
+ goto out;
+
+ sff8079_show_all_common(buf);
+
+ /* Finish if A2h page is not present */
+ if (!(buf[92] & (1 << 6)))
+ goto out;
+
+ /* Read A2h page */
+ ret = sff8079_get_eeprom_page(ctx, SFF8079_I2C_ADDRESS_HIGH,
+ buf + ETH_MODULE_SFF_8079_LEN);
+ if (ret)
+ goto out;
+
+ sff8472_show_all(buf);
+out:
+ free(buf);
+
+ return ret;
+}
diff --git a/shell-completion/bash/ethtool b/shell-completion/bash/ethtool
new file mode 100644
index 0000000..99c5f6f
--- /dev/null
+++ b/shell-completion/bash/ethtool
@@ -0,0 +1,1281 @@
+# bash completion for ethtool(8) -*- shell-script -*-
+# shellcheck shell=bash disable=SC2207
+
+# Complete a word representing a set of characters.
+# @param $@ chars Characters which may be present in completed set.
+_ethtool_compgen_letterset()
+{
+ local char
+ for char; do
+ case "$cur" in
+ *"$char"*)
+ # $cur already contains $char
+ ;;
+ *)
+ COMPREPLY+=( "$cur$char" )
+ ;;
+ esac
+ done
+}
+
+# Generate completions for words matched case-insensitively
+# @param $@ choices Completion choices.
+_ethtool_compgen_nocase()
+{
+ local reset
+ reset=$( shopt -p nocasematch )
+ shopt -s nocasematch
+
+ local choice
+ for choice; do
+ case "$choice" in
+ "$cur"*) COMPREPLY+=( "$choice" ) ;;
+ esac
+ done
+
+ $reset
+}
+
+# Gets names from a section of ethtool output.
+# @param $1 section_bre POSIX BRE matching section heading (without : at end).
+# @param $@ ethtool arguments
+_ethtool_get_names_in_section()
+{
+ local section_bre="$1"
+ shift
+
+ PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
+ ethtool "$@" 2>/dev/null |
+ command sed -n "
+# Line is section heading iff it ends with :
+# From requested section heading to next section heading
+/^$section_bre:$/,/:$/ {
+ # If line is section heading, ignore it
+ /:$/d
+ # Remove value and separator, if present
+ s/[[:space:]]*:.*//
+ # Remove leading space, if present
+ s/^[[:space:]]*//
+ # Print the line
+ p
+}"
+}
+
+# Complete an RSS Context ID
+_ethtool_context()
+{
+ COMPREPLY=(
+ $(PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
+ ethtool --show-nfc "${words[2]}" 2>/dev/null |
+ command sed -n 's/^[[:space:]]*RSS Context ID:[[:space:]]*\([0-9]*\)$/\1/p' |
+ sort -u) )
+}
+
+# Complete a network flow traffic type
+# Available OPTIONS:
+# --hash Complete only types suitable for rx hashing
+_ethtool_flow_type()
+{
+ local types='ah4 ah6 esp4 esp6 ether sctp4 sctp6 tcp4 tcp6 udp4 udp6'
+ if [ "${1-}" != --hash ]; then
+ types="$types ip4 ip6"
+ fi
+ COMPREPLY=( $( compgen -W "$types" -- "$cur" ) )
+}
+
+# Completion for ethtool --change
+_ethtool_change()
+{
+ local -A settings=(
+ [advertise]=notseen
+ [autoneg]=notseen
+ [duplex]=notseen
+ [mdix]=notseen
+ [msglvl]=notseen
+ [port]=notseen
+ [phyad]=notseen
+ [speed]=notseen
+ [wol]=notseen
+ [xcvr]=notseen
+ [lanes]=notseen
+ )
+
+ local -A msgtypes=(
+ [drv]=notseen
+ [hw]=notseen
+ [ifdown]=notseen
+ [ifup]=notseen
+ [intr]=notseen
+ [link]=notseen
+ [pktdata]=notseen
+ [probe]=notseen
+ [rx_err]=notseen
+ [rx_status]=notseen
+ [timer]=notseen
+ [tx_done]=notseen
+ [tx_err]=notseen
+ [tx_queued]=notseen
+ [wol]=notseen
+ )
+
+ # Mark seen settings and msgtypes, and whether in msglvl sub-command
+ local in_msglvl=
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ if [ "$in_msglvl" ] && [ "${msgtypes[$word]+set}" ]; then
+ msgtypes[$word]=seen
+ elif [ "${settings[$word]+set}" ]; then
+ settings[$word]=seen
+ if [ "$word" = msglvl ]; then
+ in_msglvl=1
+ else
+ in_msglvl=
+ fi
+ fi
+ done
+
+ if [ "$in_msglvl" ] && [ "${msgtypes[$prev]+set}" ]; then
+ # All msgtypes take an on/off argument
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ case "$prev" in
+ advertise)
+ # Hex number
+ return ;;
+ autoneg)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ duplex)
+ COMPREPLY=( $( compgen -W 'half full' -- "$cur" ) )
+ return ;;
+ mdix)
+ COMPREPLY=( $( compgen -W 'auto on off' -- "$cur" ) )
+ return ;;
+ msglvl)
+ # Unsigned integer or msgtype
+ COMPREPLY=( $( compgen -W "${!msgtypes[*]}" -- "$cur" ) )
+ return ;;
+ port)
+ COMPREPLY=( $( compgen -W 'aui bnc fibre mii tp' -- "$cur" ) )
+ return ;;
+ phyad)
+ # Integer
+ return ;;
+ sopass)
+ _mac_addresses
+ return ;;
+ speed)
+ # Number
+ return ;;
+ wol)
+ # $cur is a set of wol type characters.
+ _ethtool_compgen_letterset p u m b a g s f d
+ return ;;
+ xcvr)
+ COMPREPLY=( $( compgen -W 'internal external' -- "$cur" ) )
+ return ;;
+ lanes)
+ # Number
+ return ;;
+ esac
+
+ local -a comp_words=()
+
+ # Add settings not seen to completions
+ local setting
+ for setting in "${!settings[@]}"; do
+ if [ "${settings[$setting]}" = notseen ]; then
+ comp_words+=( "$setting" )
+ fi
+ done
+
+ # Add settings not seen to completions
+ if [ "$in_msglvl" ]; then
+ local msgtype
+ for msgtype in "${!msgtypes[@]}"; do
+ if [ "${msgtypes[$msgtype]}" = notseen ]; then
+ comp_words+=( "$msgtype" )
+ fi
+ done
+ fi
+
+ COMPREPLY=( $( compgen -W "${comp_words[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --change-eeprom
+_ethtool_change_eeprom()
+{
+ local -A settings=(
+ [length]=1
+ [magic]=1
+ [offset]=1
+ [value]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ # All settings take an unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --coalesce
+_ethtool_coalesce()
+{
+ local -A settings=(
+ [adaptive-rx]=1
+ [adaptive-tx]=1
+ [pkt-rate-high]=1
+ [pkt-rate-low]=1
+ [rx-frames]=1
+ [rx-frames-high]=1
+ [rx-frames-irq]=1
+ [rx-frames-low]=1
+ [rx-usecs]=1
+ [rx-usecs-high]=1
+ [rx-usecs-irq]=1
+ [rx-usecs-low]=1
+ [sample-interval]=1
+ [stats-block-usecs]=1
+ [tx-frames]=1
+ [tx-frames-high]=1
+ [tx-frames-irq]=1
+ [tx-frames-low]=1
+ [tx-usecs]=1
+ [tx-usecs-high]=1
+ [tx-usecs-irq]=1
+ [tx-usecs-low]=1
+ [tx-aggr-max-bytes]=1
+ [tx-aggr-max-frames]=1
+ [tx-aggr-time-usecs]=1
+ )
+
+ case "$prev" in
+ adaptive-rx|\
+ adaptive-tx)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ esac
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --config-nfc <devname> flow-type
+_ethtool_config_nfc_flow_type()
+{
+ if [ "$cword" -eq 4 ]; then
+ _ethtool_flow_type --spec
+ return
+ fi
+
+ case "$prev" in
+ context)
+ _ethtool_context
+ return ;;
+ dst|\
+ dst-mac|\
+ src)
+ # TODO: Complete only local for dst and remote for src
+ _mac_addresses
+ return ;;
+ dst-ip)
+ # Note: RX classification, so dst is usually local
+ case "${words[4]}" in
+ *4) _ip_addresses -4 return ;;
+ *6) _ip_addresses -6 return ;;
+ esac
+ return ;;
+ src-ip)
+ # Note: RX classification, so src is usually remote
+ # TODO: Remote IP addresses (ARP cache + /etc/hosts + ?)
+ return ;;
+ m|\
+ *-mask)
+ # MAC, IP, or integer bitmask
+ return ;;
+ esac
+
+ local -A settings=(
+ [action]=1
+ [context]=1
+ [loc]=1
+ [queue]=1
+ [vf]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Integer
+ return
+ fi
+
+ case "${words[4]}" in
+ ah4|\
+ esp4)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [spi]=1
+ [src-ip]=1
+ [tos]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ ah6|\
+ esp6)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [spi]=1
+ [src-ip]=1
+ [tclass]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ ether)
+ local -A fields=(
+ [dst]=1
+ [proto]=1
+ [src]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ ip4)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [dst-port]=1
+ [l4data]=1
+ [l4proto]=1
+ [spi]=1
+ [src-ip]=1
+ [src-port]=1
+ [tos]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ ip6)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [dst-port]=1
+ [l4data]=1
+ [l4proto]=1
+ [spi]=1
+ [src-ip]=1
+ [src-port]=1
+ [tclass]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ sctp4|\
+ tcp4|\
+ udp4)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [dst-port]=1
+ [src-ip]=1
+ [src-port]=1
+ [tos]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ sctp6|\
+ tcp6|\
+ udp6)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [dst-port]=1
+ [src-ip]=1
+ [src-port]=1
+ [tclass]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ *)
+ return ;;
+ esac
+
+ if [ "${fields[$prev]+set}" ]; then
+ # Integer
+ return
+ fi
+
+ # If the previous 2 words were a field+value, suggest a mask
+ local mask=
+ if [ "${fields[${words[$cword-2]}]+set}" ]; then
+ mask="m ${words[$cword-2]}-mask"
+ fi
+
+ # Remove fields and settings which have been seen
+ local word
+ for word in "${words[@]:5:${#words[@]}-6}"; do
+ unset "fields[$word]" "settings[$word]"
+ done
+
+ # Remove mutually-exclusive options
+ if ! [ "${settings[action]+set}" ]; then
+ unset 'settings[queue]' 'settings[vf]'
+ fi
+ if ! [ "${settings[queue]+set}" ]; then
+ unset 'settings[action]'
+ fi
+ if ! [ "${settings[vf]+set}" ]; then
+ unset 'settings[action]'
+ fi
+
+ COMPREPLY=( $( compgen -W "$mask ${!fields[*]} ${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --config-nfc
+_ethtool_config_nfc()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W 'delete flow-type rx-flow-hash' -- "$cur" ) )
+ return
+ fi
+
+ case "${words[3]}" in
+ delete)
+ # Unsigned integer
+ return ;;
+ flow-type)
+ _ethtool_config_nfc_flow_type
+ return ;;
+ rx-flow-hash)
+ case "$cword" in
+ 4)
+ _ethtool_flow_type --hash
+ return ;;
+ 5)
+ _ethtool_compgen_letterset m v t s d f n r
+ return ;;
+ 6)
+ COMPREPLY=( $( compgen -W context -- "$cur" ) )
+ return ;;
+ 7)
+ _ethtool_context
+ return ;;
+ esac
+ return ;;
+ esac
+}
+
+# Completion for ethtool --eeprom-dump
+_ethtool_eeprom_dump()
+{
+ local -A settings=(
+ [length]=1
+ [offset]=1
+ [raw]=1
+ )
+
+ if [ "$prev" = raw ]; then
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --features
+_ethtool_features()
+{
+ local -A abbreviations=(
+ [generic-receive-offload]=gro
+ [generic-segmentation-offload]=gso
+ [large-receive-offload]=lro
+ [ntuple-filters]=ntuple
+ [receive-hashing]=rxhash
+ [rx-checksumming]=rx
+ [rx-vlan-offload]=rxvlan
+ [scatter-gather]=sg
+ [tcp-segmentation-offload]=tso
+ [tx-checksumming]=tx
+ [tx-vlan-offload]=txvlan
+ [udp-fragmentation-offload]=ufo
+ )
+
+ local -A features=()
+ local feature status fixed
+ # shellcheck disable=SC2034
+ while read -r feature status fixed; do
+ if [ -z "$feature" ]; then
+ # Ignore blank line from empty expansion in here-document
+ continue
+ fi
+
+ if [ "$feature" = Features ]; then
+ # Ignore heading
+ continue
+ fi
+
+ if [ "$fixed" = '[fixed]' ]; then
+ # Fixed features can't be changed
+ continue
+ fi
+
+ feature=${feature%:}
+ if [ "${abbreviations[$feature]+set}" ]; then
+ features[${abbreviations[$feature]}]=1
+ else
+ features[$feature]=1
+ fi
+ done <<ETHTOOL_FEATURES
+$(PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
+ ethtool --show-features "${words[2]}" 2>/dev/null)
+ETHTOOL_FEATURES
+
+ if [ "${features[$prev]+set}" ]; then
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ # Remove features which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "features[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!features[*]}" -- "$cur" ) )
+}
+
+# Complete the current word as a kernel firmware file (for request_firmware)
+# See https://www.kernel.org/doc/html/latest/driver-api/firmware/core.html
+_ethtool_firmware()
+{
+ local -a firmware_paths=(
+ /lib/firmware/updates/
+ /lib/firmware/
+ )
+
+ local release
+ if release=$( uname -r 2>/dev/null ); then
+ firmware_paths+=(
+ "/lib/firmware/updates/$release/"
+ "/lib/firmware/$release/"
+ )
+ fi
+
+ local fw_path_para
+ if fw_path_para=$( cat /sys/module/firmware_class/parameters/path 2>/dev/null ) \
+ && [ -n "$fw_path_para" ]; then
+ firmware_paths+=( "$fw_path_para" )
+ fi
+
+ local -A firmware_files=()
+
+ local firmware_path
+ for firmware_path in "${firmware_paths[@]}"; do
+ local firmware_file
+ for firmware_file in "$firmware_path"*; do
+ if [ -f "$firmware_file" ]; then
+ firmware_files[${firmware_file##*/}]=1
+ fi
+ done
+ done
+
+ local IFS='
+'
+ COMPREPLY=( $( compgen -W "${!firmware_files[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --flash
+_ethtool_flash()
+{
+ if [ "$cword" -eq 3 ]; then
+ _ethtool_firmware
+ return
+ fi
+}
+
+# Completion for ethtool --get-dump
+_ethtool_get_dump()
+{
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W data -- "$cur" ) )
+ return ;;
+ 4)
+ # Output filename
+ local IFS='
+'
+ COMPREPLY=( $( compgen -f -- "$cur" ) )
+ return ;;
+ esac
+}
+
+# Completion for ethtool --get-phy-tunable
+_ethtool_get_phy_tunable()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W downshift -- "$cur" ) )
+ return
+ fi
+}
+
+# Completion for ethtool --module-info
+_ethtool_module_info()
+{
+ local -A settings=(
+ [hex]=1
+ [length]=1
+ [offset]=1
+ [raw]=1
+ )
+
+ case "$prev" in
+ hex|\
+ raw)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ esac
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --pause
+_ethtool_pause()
+{
+ local -A settings=(
+ [autoneg]=1
+ [rx]=1
+ [tx]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --per-queue
+_ethtool_per_queue()
+{
+ local -a subcommands=(
+ --coalesce
+ --show-coalesce
+ )
+
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W "queue_mask ${subcommands[*]}" -- "$cur" ) )
+ return
+ fi
+
+ local sc_start=3
+ if [ "${words[3]}" = queue_mask ] ; then
+ case "$cword" in
+ 4)
+ # Hex number
+ return ;;
+ 5)
+ COMPREPLY=( $( compgen -W "${subcommands[*]}" -- "$cur" ) )
+ return ;;
+ esac
+
+ sc_start=5
+ fi
+
+ case "${words[$sc_start]}" in
+ --coalesce)
+ # Remove --per-queue args to match normal --coalesce invocation
+ local words=(
+ "${words[0]}"
+ --coalesce
+ "${words[2]}"
+ "${words[@]:$sc_start+1:${#words[@]}-$sc_start-1}"
+ )
+ _ethtool_coalesce
+ return ;;
+ --show-coalesce)
+ # No args
+ return ;;
+ esac
+}
+
+# Completion for ethtool --register-dump
+_ethtool_register_dump()
+{
+ local -A settings=(
+ [file]=1
+ [hex]=1
+ [raw]=1
+ )
+
+ case "$prev" in
+ hex|\
+ raw)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ file)
+ local IFS='
+'
+ COMPREPLY=( $( compgen -f -- "$cur" ) )
+ return ;;
+ esac
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --reset
+_ethtool_reset()
+{
+ if [ "$prev" = flags ]; then
+ # Unsigned integer
+ return
+ fi
+
+ local -A flag_names=(
+ [ap]=1
+ [dma]=1
+ [filter]=1
+ [irq]=1
+ [mac]=1
+ [mgmt]=1
+ [offload]=1
+ [phy]=1
+ [ram]=1
+ )
+
+ local -A all_flag_names=()
+ local flag_name
+ for flag_name in "${!flag_names[@]}"; do
+ all_flag_names[$flag_name]=1
+ all_flag_names[$flag_name-shared]=1
+ done
+
+ # Remove all_flag_names which have been seen
+ local any_dedicated=
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ case "$word" in
+ all)
+ # Flags are always additive.
+ # Nothing to add after "all".
+ return ;;
+ dedicated)
+ any_dedicated=1
+ # "dedicated" sets all non-shared flags
+ for flag_name in "${!flag_names[@]}"; do
+ unset "all_flag_names[$flag_name]"
+ done
+ continue ;;
+ esac
+
+ if [ "${flag_names[$word]+set}" ]; then
+ any_dedicated=1
+ fi
+
+ unset "all_flag_names[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!all_flag_names[*]}" -- "$cur" ) )
+
+ # Although it is permitted to mix named and un-named flags or duplicate
+ # flags with "all" or "dedicated", it's not likely intentional.
+ # Reconsider if a real use-case (or good consistency argument) is found.
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY+=( all dedicated flags )
+ elif [ -z "$any_dedicated" ]; then
+ COMPREPLY+=( dedicated )
+ fi
+}
+
+# Completion for ethtool --rxfh
+_ethtool_rxfh()
+{
+ local -A settings=(
+ [context]=1
+ [default]=1
+ [delete]=1
+ [equal]=1
+ [hfunc]=1
+ [hkey]=1
+ [weight]=1
+ )
+
+ case "$prev" in
+ context)
+ _ethtool_context
+ # "new" to create a new context
+ COMPREPLY+=( new )
+ return ;;
+ equal)
+ # Positive integer
+ return ;;
+ hfunc)
+ # Complete available RSS hash functions
+ COMPREPLY=(
+ $(_ethtool_get_names_in_section 'RSS hash function' \
+ --show-rxfh "${words[2]}")
+ )
+ return ;;
+ hkey)
+ # Pairs of hex digits separated by :
+ return ;;
+ weight)
+ # Non-negative integer
+ return ;;
+ esac
+
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ # Remove settings which have been seen
+ unset "settings[$word]"
+
+ # Remove settings which are mutually-exclusive with seen settings
+ case "$word" in
+ context)
+ unset 'settings[default]'
+ ;;
+ default)
+ unset \
+ 'settings[context]' \
+ 'settings[delete]' \
+ 'settings[equal]' \
+ 'settings[weight]'
+ ;;
+ delete)
+ unset \
+ 'settings[default]' \
+ 'settings[equal]' \
+ 'settings[hkey]' \
+ 'settings[weight]'
+ ;;
+ equal)
+ unset \
+ 'settings[default]' \
+ 'settings[delete]' \
+ 'settings[weight]'
+ ;;
+ hkey)
+ unset 'settings[delete]'
+ ;;
+ weight)
+ unset \
+ 'settings[default]' \
+ 'settings[delete]' \
+ 'settings[equal]'
+ ;;
+ esac
+ done
+
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --set-channels
+_ethtool_set_channels()
+{
+ local -A settings=(
+ [combined]=1
+ [other]=1
+ [rx]=1
+ [tx]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --set-eee
+_ethtool_set_eee()
+{
+ local -A settings=(
+ [advertise]=1
+ [eee]=1
+ [tx-lpi]=1
+ [tx-timer]=1
+ )
+
+ case "$prev" in
+ advertise|\
+ tx-timer)
+ # Unsigned integer
+ return ;;
+ eee|\
+ tx-lpi)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ esac
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --set-fec
+_ethtool_set_fec()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W encoding -- "$cur" ) )
+ return
+ fi
+
+ local -A modes=(
+ [auto]=auto
+ [rs]=RS
+ [off]=off
+ [baser]=BaseR
+ )
+
+ # Remove modes which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ # ethtool recognizes modes case-insensitively
+ unset "modes[${word,,}]"
+ done
+
+ _ethtool_compgen_nocase "${modes[@]}"
+}
+
+# Completion for ethtool --set-phy-tunable
+_ethtool_set_phy_tunable()
+{
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W downshift -- "$cur" ) )
+ return ;;
+ 4)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ 5)
+ COMPREPLY=( $( compgen -W count -- "$cur" ) )
+ return ;;
+ esac
+}
+
+# Completion for ethtool --set-priv-flags
+_ethtool_set_priv_flags()
+{
+ if [ $(( cword % 2 )) -eq 0 ]; then
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ # Get available private flags
+ local -A flags=()
+ local flag
+ while IFS= read -r flag; do
+ # Ignore blank line from empty here-document
+ if [ -n "$flag" ]; then
+ flags[$flag]=1
+ fi
+ done <<ETHTOOL_PRIV_FLAGS
+$(_ethtool_get_names_in_section \
+ 'Private flags for [[:graph:]]*' --show-priv-flags "${words[2]}")
+ETHTOOL_PRIV_FLAGS
+
+ # Remove flags which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "flags[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!flags[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --set-ring
+_ethtool_set_ring()
+{
+ local -A settings=(
+ [rx-jumbo]=1
+ [rx-mini]=1
+ [rx]=1
+ [tx]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --show-nfc
+_ethtool_show_nfc()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W 'rule rx-flow-hash' -- "$cur" ) )
+ return
+ fi
+
+ case "${words[3]}" in
+ rule)
+ if [ "$cword" -eq 4 ]; then
+ COMPREPLY=(
+ $(PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
+ ethtool --show-nfc "${words[2]}" 2>/dev/null |
+ command sed -n 's/^Filter:[[:space:]]*\([0-9]*\)$/\1/p')
+ )
+ fi
+ return ;;
+ rx-flow-hash)
+ case "$cword" in
+ 4)
+ _ethtool_flow_type --hash
+ return ;;
+ 5)
+ COMPREPLY=( $( compgen -W context -- "$cur" ) )
+ return ;;
+ 6)
+ _ethtool_context
+ return ;;
+ esac
+ ;;
+ esac
+}
+
+# Completion for ethtool --show-rxfh
+_ethtool_show_rxfh()
+{
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W context -- "$cur" ) )
+ return ;;
+ 4)
+ _ethtool_context
+ return ;;
+ esac
+}
+
+# Completion for ethtool --test
+_ethtool_test()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W 'external_lb offline online' -- "$cur" ) )
+ return
+ fi
+}
+
+# Completion for ethtool --set-module
+_ethtool_set_module()
+{
+ local -A settings=(
+ [power-mode-policy]=1
+ )
+
+ case "$prev" in
+ power-mode-policy)
+ COMPREPLY=( $( compgen -W 'high auto' -- "$cur" ) )
+ return ;;
+ esac
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Complete any ethtool command
+_ethtool()
+{
+ local cur prev words cword
+ _init_completion || return
+
+ # Per "Contributing to bash-completion", complete non-duplicate long opts
+ local -A suggested_funcs=(
+ [--change-eeprom]=change_eeprom
+ [--change]=change
+ [--coalesce]=coalesce
+ [--config-nfc]=config_nfc
+ [--driver]=devname
+ [--dump-module-eeprom]=module_info
+ [--eeprom-dump]=eeprom_dump
+ [--features]=features
+ [--flash]=flash
+ [--get-dump]=get_dump
+ [--get-phy-tunable]=get_phy_tunable
+ [--identify]=devname
+ [--module-info]=module_info
+ [--negotiate]=devname
+ [--offload]=features
+ [--pause]=pause
+ [--per-queue]=per_queue
+ [--phy-statistics]=devname
+ [--register-dump]=register_dump
+ [--reset]=reset
+ [--set-channels]=set_channels
+ [--set-dump]=devname
+ [--set-eee]=set_eee
+ [--set-fec]=set_fec
+ [--set-phy-tunable]=set_phy_tunable
+ [--set-priv-flags]=set_priv_flags
+ [--set-ring]=set_ring
+ [--set-rxfh-indir]=rxfh
+ [--show-channels]=devname
+ [--show-coalesce]=devname
+ [--show-eee]=devname
+ [--show-features]=devname
+ [--show-fec]=devname
+ [--show-nfc]=show_nfc
+ [--show-offload]=devname
+ [--show-pause]=devname
+ [--show-permaddr]=devname
+ [--show-priv-flags]=devname
+ [--show-ring]=devname
+ [--show-rxfh]=show_rxfh
+ [--show-time-stamping]=devname
+ [--statistics]=devname
+ [--test]=test
+ [--set-module]=set_module
+ [--show-module]=devname
+ )
+ local -A other_funcs=(
+ [--config-ntuple]=config_nfc
+ [--rxfh]=rxfh
+ [--show-ntuple]=show_nfc
+ [--show-rxfh-indir]=devname
+ [-A]=pause
+ [-C]=coalesce
+ [-E]=change_eeprom
+ [-G]=set_ring
+ [-K]=features
+ [-L]=set_channels
+ [-N]=config_nfc
+ [-P]=devname
+ [-Q]=per_queue
+ [-S]=devname
+ [-T]=devname
+ [-U]=config_nfc
+ [-W]=devname
+ [-X]=rxfh
+ [-a]=devname
+ [-c]=devname
+ [-d]=register_dump
+ [-e]=eeprom_dump
+ [-f]=flash
+ [-g]=devname
+ [-i]=devname
+ [-k]=devname
+ [-l]=devname
+ [-m]=module_info
+ [-n]=show_nfc
+ [-p]=devname
+ [-r]=devname
+ [-s]=change
+ [-t]=test
+ [-u]=show_nfc
+ [-w]=get_dump
+ [-x]=devname
+ )
+
+ if [ "$cword" -le 1 ]; then
+ _available_interfaces
+ COMPREPLY+=(
+ $( compgen -W "--help --version ${!suggested_funcs[*]}" -- "$cur" )
+ )
+ return
+ fi
+
+ local func=${suggested_funcs[${words[1]}]-${other_funcs[${words[1]}]-}}
+ if [ "$func" ]; then
+ # All sub-commands have devname as their first argument
+ if [ "$cword" -eq 2 ]; then
+ _available_interfaces
+ return
+ fi
+
+ if [ "$func" != devname ]; then
+ "_ethtool_$func"
+ fi
+ fi
+} &&
+complete -F _ethtool ethtool
+
+# ex: filetype=sh sts=8 sw=8 ts=8 noet
diff --git a/smsc911x.c b/smsc911x.c
new file mode 100644
index 0000000..b643504
--- /dev/null
+++ b/smsc911x.c
@@ -0,0 +1,91 @@
+#include <stdio.h>
+#include <string.h>
+#include "internal.h"
+
+int smsc911x_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ unsigned int *smsc_reg = (unsigned int *)regs->data;
+
+ fprintf(stdout, "LAN911x Registers\n");
+ fprintf(stdout, "offset 0x50, ID_REV = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x54, INT_CFG = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x58, INT_STS = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x5C, INT_EN = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x60, RESERVED = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x64, BYTE_TEST = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x68, FIFO_INT = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x6C, RX_CFG = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x70, TX_CFG = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x74, HW_CFG = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x78, RX_DP_CTRL = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x7C, RX_FIFO_INF = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x80, TX_FIFO_INF = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x84, PMT_CTRL = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x88, GPIO_CFG = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x8C, GPT_CFG = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x90, GPT_CNT = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x94, FPGA_REV = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x98, ENDIAN = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0x9C, FREE_RUN = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0xA0, RX_DROP = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0xA4, MAC_CSR_CMD = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0xA8, MAC_CSR_DATA = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0xAC, AFC_CFG = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0xB0, E2P_CMD = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "offset 0xB4, E2P_DATA = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "MAC Registers\n");
+ fprintf(stdout, "index 1, MAC_CR = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index 2, ADDRH = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index 3, ADDRL = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index 4, HASHH = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index 5, HASHL = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index 6, MII_ACC = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index 7, MII_DATA = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index 8, FLOW = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index 9, VLAN1 = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index A, VLAN2 = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index B, WUFF = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "index C, WUCSR = 0x%08X\n",*smsc_reg++);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "PHY Registers\n");
+ fprintf(stdout, "index 0, Basic Control Reg = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 1, Basic Status Reg = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 2, PHY identifier 1 = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 3, PHY identifier 2 = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 4, Auto Negotiation Advertisement Reg = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 5, Auto Negotiation Link Partner Ability Reg = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 6, Auto Negotiation Expansion Register = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 7, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 8, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 9, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 10, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 11, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 12, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 13, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 14, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 15, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 16, Silicon Revision Reg = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 17, Mode Control/Status Reg = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 18, Special Modes = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 19, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 20, TSTCNTL = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 21, TSTREAD1 = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 22, TSTREAD2 = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 23, TSTWRITE = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 24, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 25, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 26, Reserved = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 27, Control/Status Indication = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 28, Special internal testability = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 29, Interrupt Source Register = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 30, Interrupt Mask Register = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "index 31, PHY Special Control/Status Register = 0x%04X\n",*smsc_reg++);
+ fprintf(stdout, "\n");
+
+ return 0;
+}
+
diff --git a/stmmac.c b/stmmac.c
new file mode 100644
index 0000000..5847120
--- /dev/null
+++ b/stmmac.c
@@ -0,0 +1,71 @@
+/****************************************************************************
+ * Support for the Synopsys MAC 10/100/1000 on-chip Ethernet controllers
+ *
+ * Copyright (C) 2007-2009 STMicroelectronics Ltd
+ *
+ * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "internal.h"
+
+#define MAC100_DMA_REG_NUM 9
+#define GMAC_REG_NUM 55
+#define GMAC_DMA_REG_NUM 23
+
+int st_mac100_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ int i;
+ unsigned int *stmmac_reg = (unsigned int *)regs->data;
+
+ fprintf(stdout, "ST MAC 10/100 Registers\n");
+ fprintf(stdout, "control reg 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "addr HI 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "addr LO 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "multicast hash HI 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "multicast hash LO 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "MII addr 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "MII data %08X\n", *stmmac_reg++);
+ fprintf(stdout, "flow control 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "VLAN1 tag 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "VLAN2 tag 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "mac wakeup frame 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "mac wakeup crtl 0x%08X\n", *stmmac_reg++);
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "DMA Registers\n");
+ for (i = 0; i < MAC100_DMA_REG_NUM; i++)
+ fprintf(stdout, "CSR%d 0x%08X\n", i, *stmmac_reg++);
+
+ fprintf(stdout, "DMA cur tx buf addr 0x%08X\n", *stmmac_reg++);
+ fprintf(stdout, "DMA cur rx buf addr 0x%08X\n", *stmmac_reg++);
+
+ fprintf(stdout, "\n");
+
+ return 0;
+}
+
+int st_gmac_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ int i;
+ unsigned int *stmmac_reg = (unsigned int *)regs->data;
+
+ fprintf(stdout, "ST GMAC Registers\n");
+ fprintf(stdout, "GMAC Registers\n");
+ for (i = 0; i < GMAC_REG_NUM; i++)
+ fprintf(stdout, "Reg%d 0x%08X\n", i, *stmmac_reg++);
+
+ fprintf(stdout, "\n");
+ fprintf(stdout, "DMA Registers\n");
+ for (i = 0; i < GMAC_DMA_REG_NUM; i++)
+ fprintf(stdout, "Reg%d 0x%08X\n", i, *stmmac_reg++);
+
+ return 0;
+}
diff --git a/test-cmdline.c b/test-cmdline.c
new file mode 100644
index 0000000..cb803ed
--- /dev/null
+++ b/test-cmdline.c
@@ -0,0 +1,340 @@
+/****************************************************************************
+ * Test cases for ethtool command-line parsing
+ * Copyright 2011 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#define TEST_NO_WRAPPERS
+#include "internal.h"
+
+#ifdef ETHTOOL_ENABLE_NETLINK
+#define IS_NL 1
+#else
+#define IS_NL 0
+#endif
+
+static struct test_case {
+ int rc;
+ const char *args;
+} test_cases[] = {
+ { 1, "" },
+ { 0, "devname" },
+ { 0, "15_char_devname" },
+ /* netlink interface allows names up to 127 characters */
+ { !IS_NL, "16_char_devname!" },
+ { !IS_NL, "127_char_devname0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde" },
+ { 1, "128_char_devname0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" },
+ /* Argument parsing for -s is specialised */
+ { 0, "-s devname" },
+ { 0, "--change devname speed 100 duplex half mdix auto" },
+ { 1, "-s devname speed foo" },
+ { 1, "--change devname speed" },
+ { 0, "-s devname duplex half" },
+ { 1, "--change devname duplex foo" },
+ { 1, "-s devname duplex" },
+ { 1, "--change devname mdix foo" },
+ { 1, "-s devname mdix" },
+ { 0, "--change devname port tp" },
+ { 1, "-s devname port foo" },
+ { 1, "--change devname port" },
+ { 0, "-s devname autoneg on" },
+ { 1, "--change devname autoneg foo" },
+ { 1, "-s devname autoneg" },
+ { 0, "--change devname advertise 0x1" },
+ { 0, "--change devname advertise 0xf" },
+ { 0, "--change devname advertise 0Xf" },
+ { 0, "--change devname advertise 1" },
+ { 0, "--change devname advertise f" },
+ { 0, "--change devname advertise 01" },
+ { 0, "--change devname advertise 0f" },
+ { 0, "--change devname advertise 0xfffffffffffffffffffffffffffffffff" },
+ { 0, "--change devname advertise fffffffffffffffffffffffffffffffff" },
+ { 0, "--change devname advertise 0x0000fffffffffffffffffffffffffffff" },
+ { 0, "--change devname advertise 0000fffffffffffffffffffffffffffff" },
+ { 1, "-s devname advertise" },
+ { 1, "-s devname advertise 0x" },
+ { 1, "-s devname advertise foo" },
+ { 1, "-s devname advertise 0xfoo" },
+ { 1, "--change devname advertise" },
+ { 0, "-s devname phyad 1" },
+ { 1, "--change devname phyad foo" },
+ { 1, "-s devname phyad" },
+ /* Deprecated 'xcvr' detected by netlink parser */
+ { IS_NL, "--change devname xcvr external" },
+ { 1, "-s devname xcvr foo" },
+ { 1, "--change devname xcvr" },
+ { 0, "-s devname wol p" },
+ { 1, "--change devname wol" },
+ { 0, "-s devname sopass 01:23:45:67:89:ab" },
+ { 1, "--change devname sopass 01:23:45:67:89:" },
+ { 1, "-s devname sopass 01:23:45:67:89" },
+ { 1, "--change devname sopass" },
+ { 0, "-s devname msglvl 1" },
+ { 1, "--change devname msglvl" },
+ { 0, "-s devname msglvl hw on rx_status off" },
+ { 1, "--change devname msglvl hw foo" },
+ { 1, "-s devname msglvl hw" },
+ { 0, "--change devname speed 100 duplex half port tp autoneg on advertise 0x1 phyad 1 wol p sopass 01:23:45:67:89:ab msglvl 1" },
+ /* Deprecated 'xcvr' detected by netlink parser */
+ { IS_NL, "--change devname speed 100 duplex half port tp autoneg on advertise 0x1 phyad 1 xcvr external wol p sopass 01:23:45:67:89:ab msglvl 1" },
+ { 1, "-s devname foo" },
+ { 1, "-s" },
+ { 0, "-a devname" },
+ { 0, "--show-pause devname" },
+ { 1, "-a" },
+ /* Many other sub-commands use parse_generic_cmdline() and
+ * don't need to be check in that much detail. */
+ { 0, "-A devname autoneg on" },
+ { 1, "--pause devname autoneg foo" },
+ { 1, "-A devname autoneg" },
+ { 0, "--pause devname rx off" },
+ { 0, "-A devname tx on rx on autoneg off" },
+ { 1, "--pause devname foo on" },
+ { 1, "-A" },
+ { 0, "-c devname" },
+ { 0, "--show-coalesce devname" },
+ { 0, "-C devname adaptive-rx on adaptive-tx off rx-usecs 1 rx-frames 2 rx-usecs-irq 3 rx-frames-irq 4 tx-usecs 5 tx-frames 6 tx-usecs-irq 7 tx-frames-irq 8 stats-block-usecs 9 pkt-rate-low 10" },
+ { 0, "--coalesce devname rx-usecs-low 11 rx-frames-low 12 tx-usecs-low 13 tx-frames-low 14 pkt-rate-high 15 rx-usecs-high 16 rx-frames-high 17 tx-usecs-high 18 tx-frames-high 19 sample-interval 20" },
+ { 1, "-C devname adaptive-rx foo" },
+ { 1, "--coalesce devname adaptive-rx" },
+ { 1, "-C devname foo on" },
+ { 1, "-C" },
+ { 0, "-g devname" },
+ { 0, "--show-ring devname" },
+ { 1, "-g" },
+ { 0, "-G devname rx 1 rx-mini 2 rx-jumbo 3 tx 4" },
+ { 0, "--set-ring devname rx 1 rx-mini 2 rx-jumbo 3 tx 4" },
+ { 1, "-G devname rx foo" },
+ { 1, "--set-ring devname rx" },
+ { 1, "-G devname foo 1" },
+ { 1, "-G" },
+ { 1, "-k" },
+ { 1, "-K" },
+ { 0, "-i devname" },
+ { 0, "--driver devname" },
+ { 1, "-i" },
+ { 0, "-d devname" },
+ { 0, "--register-dump devname raw on file foo" },
+ { 1, "-d devname raw foo" },
+ { 1, "--register-dump devname file" },
+ { 1, "-d devname foo" },
+ { 1, "-d" },
+ { 0, "-e devname" },
+ { 0, "--eeprom-dump devname raw on offset 1 length 2" },
+ { 1, "-e devname raw foo" },
+ { 1, "--eeprom-dump devname offset foo" },
+ { 1, "-e devname length" },
+ { 1, "--eeprom-dump devname foo" },
+ { 1, "-e" },
+ { 0, "-E devname" },
+ { 0, "--change-eeprom devname magic 0x87654321 offset 0 value 1" },
+ { 0, "-E devname magic 0x87654321 offset 0 length 2" },
+ { 1, "-E" },
+ { 0, "-r devname" },
+ { 0, "--negotiate devname" },
+ { 1, "-r" },
+ { 0, "-p devname" },
+ { 0, "--identify devname 1" },
+ { 1, "-p devname 1 foo" },
+ { 1, "--identify devname foo" },
+ { 1, "-p" },
+ /* Argument parsing for -t is specialised */
+ { 0, "-t devname" },
+ { 0, "--test devname online" },
+ { 1, "-t devname foo" },
+ { 1, "--test devname online foo" },
+ { 0, "-S devname" },
+ { 0, "--statistics devname" },
+ { 1, "-S" },
+ /* Argument parsing for -n/-u is specialised */
+ { 0, "-n devname rx-flow-hash tcp4" },
+ { 0, "-u devname rx-flow-hash sctp4" },
+ { 0, "--show-nfc devname rx-flow-hash udp6" },
+ { 0, "--show-ntuple devname rx-flow-hash esp6" },
+ { 1, "-n devname rx-flow-hash foo" },
+ { 1, "-u devname rx-flow-hash foo" },
+ { 1, "--show-nfc devname rx-flow-hash" },
+ { 1, "--show-ntuple devname rx-flow-hash" },
+ { 1, "-n" },
+ /* Argument parsing for -f is specialised */
+ { 1, "-f devname" },
+ { 0, "--flash devname filename" },
+ { 0, "-f devname filename 1" },
+ { 1, "-f devname filename 1 foo" },
+ { 1, "-f" },
+ /* Argument parsing for -N/-U is specialised */
+ { 0, "-N devname rx-flow-hash tcp4 mvtsdfn" },
+ { 0, "--config-ntuple devname rx-flow-hash tcp4 r" },
+ { 1, "-U devname rx-flow-hash tcp4" },
+ { 1, "--config-nfc devname rx-flow-hash foo" },
+ { 1, "-N devname rx-flow-hash" },
+ { 1, "--config-ntuple devname foo" },
+ { 0, "-U devname delete 1" },
+ { 1, "--config-nfc devname delete foo" },
+ { 1, "-N devname delete" },
+ { 0, "--config-ntuple devname flow-type ether src 01:23:45:67:89:ab m cd:ef:01:23:45:67 dst 89:ab:cd:ef:01:23 m 45:67:89:ab:cd:ef proto 0x0123 m 0x4567 vlan 0x89ab m 0xcdef action 0" },
+ { 0, "-U devname flow-type ether src 01:23:45:67:89:ab src-mask cd:ef:01:23:45:67 dst 89:ab:cd:ef:01:23 dst-mask 45:67:89:ab:cd:ef proto 0x0123 proto-mask 0x4567 vlan 0x89ab vlan-mask 0xcdef action 1" },
+ { 1, "--config-nfc devname flow-type ether src 01:23:45:67:89: action 3" },
+ { 1, "-N devname flow-type ether src 01:23:45:67:89 action 4" },
+ { 0, "--config-ntuple devname flow-type ip4 src-ip 0.123.45.67 m 89.0.123.45 dst-ip 67.89.0.123 m 45.67.89.0 tos 1 m 1 l4proto 0x23 m 0x45 l4data 0xfedcba98 m 76543210 vlan 0x89ab m 0xcdef action 6" },
+ { 0, "-U devname flow-type ip4 src-ip 0.123.45.67 src-ip-mask 89.0.123.45 dst-ip 67.89.0.123 dst-ip-mask 45.67.89.0 tos 1 tos-mask 1 l4proto 0x23 l4proto-mask 0x45 l4data 0xfedcba98 l4data-mask 76543210 vlan 0x89ab vlan-mask 0xcdef action 7" },
+ { 0, "--config-nfc devname flow-type tcp4 src-ip 0.123.45.67 m 89.0.123.45 dst-ip 67.89.0.123 m 45.67.89.0 tos 1 m 1 src-port 23456 m 7890 dst-port 12345 m 6789 vlan 0x89ab m 0xcdef action 8" },
+ { 0, "-N devname flow-type tcp4 src-ip 0.123.45.67 src-ip-mask 89.0.123.45 dst-ip 67.89.0.123 dst-ip-mask 45.67.89.0 tos 1 tos-mask 1 src-port 23456 src-port-mask 7890 dst-port 12345 dst-port-mask 6789 vlan 0x89ab vlan-mask 0xcdef action 9" },
+ { 0, "--config-ntuple devname flow-type ah4 src-ip 0.123.45.67 m 89.0.123.45 dst-ip 67.89.0.123 m 45.67.89.0 tos 1 m 1 spi 2 m 3 vlan 0x89ab m 0xcdef action 10" },
+ { 0, "-U devname flow-type ah4 src-ip 0.123.45.67 src-ip-mask 89.0.123.45 dst-ip 67.89.0.123 dst-ip-mask 45.67.89.0 tos 1 tos-mask 1 spi 2 spi-mask 3 vlan 0x89ab vlan-mask 0xcdef action 11" },
+ { 1, "--config-nfc devname flow-type tcp4 action foo" },
+ { 1, "-N devname flow-type foo" },
+ { 1, "--config-ntuple devname flow-type" },
+ { 1, "-U devname foo" },
+ { 1, "-N" },
+ { 1, "-U" },
+ { 0, "-T devname" },
+ { 0, "--show-time-stamping devname" },
+ { 1, "-T" },
+ { 0, "-x devname" },
+ { 0, "--show-rxfh-indir devname" },
+ { 0, "--show-rxfh devname" },
+ { 1, "-x" },
+ /* Argument parsing for -X is specialised */
+ { 0, "-X devname equal 2" },
+ { 0, "--set-rxfh-indir devname equal 256" },
+ { 1, "-X devname equal 0" },
+ { 1, "--set-rxfh-indir devname equal foo" },
+ { 1, "-X devname equal" },
+ { 1, "-X devname start" },
+ { 1, "-X devname start 3" },
+ { 0, "-X devname start 4 equal 2" },
+ { 0, "--set-rxfh-indir devname weight 1 2 3 4" },
+ { 0, "--set-rxfh-indir devname start 4 weight 1 2 3 4" },
+ { 0, "--rxfh devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" },
+ { 0, "-X devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" },
+#if 0
+ /* XXX These won't fail as expected because we don't parse the
+ * hash key until after the first send_ioctl(). That needs to
+ * be changed before we enable them.
+ */
+ { 1, "--rxfh devname hkey foo" },
+ { 1, "-X devname hkey foo" },
+#endif
+ { 0, "--rxfh devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee weight 1 2 3 4" },
+ { 0, "-X devname weight 1 2 3 4 hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" },
+ { 0, "--rxfh devname hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee equal 2" },
+ { 0, "-X devname equal 2 hkey 48:15:6e:bb:d8:bd:6f:b1:a4:c6:7a:c4:76:1c:29:98:da:e1:ae:6c:2e:12:2f:c0:b9:be:61:3d:00:54:35:9e:09:05:c7:d7:93:72:4a:ee" },
+ { 1, "--rxfh devname weight 1 2 3 4 equal 8" },
+ { 1, "-X devname weight 1 2 3 4 equal 8" },
+ { 1, "-X devname foo" },
+ { 1, "-X" },
+ { 0, "-P devname" },
+ { 0, "--show-permaddr devname" },
+ { 1, "-P" },
+ { 0, "-w devname" },
+ { 0, "--get-dump devname data filename" },
+ { 0, "-w devname data filename" },
+ { 1, "--get-dump devname data" },
+ { 1, "-w devname foo" },
+ { 1, "-w" },
+ { 0, "-W devname 1" },
+ { 0, "--set-dump devname 2" },
+ { 1, "-W devname 1 foo" },
+ { 1, "-W devname foo" },
+ { 1, "-W" },
+ { 0, "-l devname" },
+ { 0, "--show-channels devname" },
+ { 1, "-l" },
+ { 0, "-L devname rx 1 tx 2 other 3 combined 4" },
+ { 0, "--set-channels devname rx 1 tx 2 other 3 combined 4" },
+ { 1, "-L devname rx foo" },
+ { 1, "--set-channels devname rx" },
+ { 0, "-L devname" },
+ { 1, "-L" },
+ { 0, "--show-priv-flags devname" },
+ { 1, "--show-priv-flags devname foo" },
+ { 1, "--show-priv-flags" },
+ { 1, "-m" },
+ { 0, "-m devname" },
+ { 1, "--dump-module-eeprom" },
+ { 0, "--dump-module-eeprom devname" },
+ { 1, "--module-info" },
+ { 0, "--module-info devname" },
+ { 0, "-m devname raw on" },
+ { 0, "-m devname raw off" },
+ { 0, "-m devname hex on" },
+ { 0, "-m devname hex off" },
+ { 1, "-m devname hex on raw on" },
+ { 0, "-m devname offset 4 length 6" },
+ { 1, "--show-eee" },
+ { 0, "--show-eee devname" },
+ { 1, "--show-eee devname foo" },
+ { 1, "--set-eee" },
+ { 1, "--set-eee devname" },
+ { 1, "--set-eee devname foo" },
+ { 0, "--set-eee devname eee on" },
+ { 0, "--set-eee devname eee off" },
+ { 1, "--set-eee devname eee foo" },
+ { 0, "--set-eee devname tx-lpi on" },
+ { 0, "--set-eee devname tx-lpi off" },
+ { 1, "--set-eee devname tx-lpi foo" },
+ { 0, "--set-eee devname tx-timer 42 advertise 0x4321" },
+ { 1, "--set-eee devname tx-timer foo" },
+ { 1, "--set-eee devname advertise foo" },
+ { 1, "--set-fec devname" },
+ { 0, "--set-fec devname encoding auto" },
+ { 0, "--set-fec devname encoding off" },
+ { 0, "--set-fec devname encoding baser rs" },
+ { 0, "--set-fec devname encoding auto auto" },
+ /* encoding names are validated by kernel with netlink */
+ { !IS_NL, "--set-fec devname encoding foo" },
+ { !IS_NL, "--set-fec devname encoding auto foo" },
+ { !IS_NL, "--set-fec devname encoding none" },
+ { 1, "--set-fec devname auto" },
+ /* can't test --set-priv-flags yet */
+ { 0, "-h" },
+ { 0, "--help" },
+ { 0, "--version" },
+ { 1, "--foo" },
+ { 1, "-foo" },
+ { 1, "-0" },
+};
+
+int send_ioctl(struct cmd_context *ctx __maybe_unused, void *cmd __maybe_unused)
+{
+ /* If we get this far then parsing succeeded */
+ test_exit(0);
+}
+
+#ifdef ETHTOOL_ENABLE_NETLINK
+struct nl_socket;
+struct nl_msg_buff;
+
+ssize_t nlsock_sendmsg(struct nl_socket *nlsk __maybe_unused,
+ struct nl_msg_buff *altbuff __maybe_unused)
+{
+ /* If we get this far then parsing succeeded */
+ test_exit(0);
+}
+#endif
+
+int main(void)
+{
+ struct test_case *tc;
+ int test_rc;
+ int rc = 0;
+
+ for (tc = test_cases; tc < test_cases + ARRAY_SIZE(test_cases); tc++) {
+ if (getenv("ETHTOOL_TEST_VERBOSE"))
+ printf("I: Test command line: ethtool %s\n", tc->args);
+ test_rc = test_cmdline(tc->args);
+ if (test_rc != tc->rc) {
+ fprintf(stderr, "E: ethtool %s returns %d\n",
+ tc->args, test_rc);
+ rc = 1;
+ }
+ }
+
+ return rc;
+}
diff --git a/test-common.c b/test-common.c
new file mode 100644
index 0000000..1dab0ce
--- /dev/null
+++ b/test-common.c
@@ -0,0 +1,385 @@
+/****************************************************************************
+ * Common test functions for ethtool
+ * Copyright 2011 Solarflare Communications Inc.
+ *
+ * Partly derived from kernel <linux/list.h>.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#define TEST_NO_WRAPPERS
+#include "internal.h"
+
+/* List utilities */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+static void init_list_head(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static void list_add(struct list_head *new, struct list_head *head)
+{
+ head->next->prev = new;
+ new->next = head->next;
+ new->prev = head;
+ head->next = new;
+}
+
+static void list_del(struct list_head *entry)
+{
+ entry->next->prev = entry->prev;
+ entry->prev->next = entry->next;
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/* Free memory at end of test */
+
+static struct list_head malloc_list = LIST_HEAD_INIT(malloc_list);
+
+void *test_malloc(size_t size)
+{
+ struct list_head *block = malloc(sizeof(*block) + size);
+
+ if (!block)
+ return NULL;
+ list_add(block, &malloc_list);
+ return block + 1;
+}
+
+void *test_calloc(size_t nmemb, size_t size)
+{
+ void *ptr = test_malloc(nmemb * size);
+
+ if (ptr)
+ memset(ptr, 0, nmemb * size);
+ return ptr;
+}
+
+char *test_strdup(const char *s)
+{
+ size_t size = strlen(s) + 1;
+ char *dup = test_malloc(size);
+
+ if (dup)
+ memcpy(dup, s, size);
+ return dup;
+}
+
+void test_free(void *ptr)
+{
+ struct list_head *block;
+
+ if (!ptr)
+ return;
+ block = (struct list_head *)ptr - 1;
+ list_del(block);
+ free(block);
+}
+
+void *test_realloc(void *ptr, size_t size)
+{
+ struct list_head *block = NULL;
+
+ if (ptr) {
+ block = (struct list_head *)ptr - 1;
+ list_del(block);
+ }
+ block = realloc(block, sizeof(*block) + size);
+ if (!block)
+ return NULL;
+ list_add(block, &malloc_list);
+ return block + 1;
+}
+
+static void test_free_all(void)
+{
+ struct list_head *block, *next;
+
+ list_for_each_safe(block, next, &malloc_list)
+ free(block);
+ init_list_head(&malloc_list);
+}
+
+/* Close files at end of test */
+
+struct file_node {
+ struct list_head link;
+ FILE *fh;
+ int fd;
+};
+
+static struct list_head file_list = LIST_HEAD_INIT(file_list);
+
+int test_open(const char *pathname, int flag, ...)
+{
+ struct file_node *node;
+ mode_t mode;
+
+ if (flag & O_CREAT) {
+ va_list ap;
+ va_start(ap, flag);
+ mode = va_arg(ap, mode_t);
+ va_end(ap);
+ } else {
+ mode = 0;
+ }
+
+ node = malloc(sizeof(*node));
+ if (!node)
+ return -1;
+
+ node->fd = open(pathname, flag, mode);
+ if (node->fd < 0) {
+ free(node);
+ return -1;
+ }
+
+ node->fh = NULL;
+ list_add(&node->link, &file_list);
+ return node->fd;
+}
+
+int test_socket(int domain, int type, int protocol)
+{
+ struct file_node *node;
+
+ node = malloc(sizeof(*node));
+ if (!node)
+ return -1;
+
+ node->fd = socket(domain, type, protocol);
+ if (node->fd < 0) {
+ free(node);
+ return -1;
+ }
+
+ node->fh = NULL;
+ list_add(&node->link, &file_list);
+ return node->fd;
+}
+
+int test_close(int fd)
+{
+ struct list_head *head, *next;
+
+ if (fd >= 0) {
+ list_for_each_safe(head, next, &file_list) {
+ if (((struct file_node *)head)->fd == fd) {
+ list_del(head);
+ free(head);
+ break;
+ }
+ }
+ }
+
+ return close(fd);
+}
+
+FILE *test_fopen(const char *path, const char *mode)
+{
+ struct file_node *node;
+
+ node = malloc(sizeof(*node));
+ if (!node)
+ return NULL;
+
+ node->fh = fopen(path, mode);
+ if (!node->fh) {
+ free(node);
+ return NULL;
+ }
+
+ node->fd = -1;
+ list_add(&node->link, &file_list);
+ return node->fh;
+}
+
+int test_fclose(FILE *fh)
+{
+ struct list_head *head, *next;
+
+ assert(fh);
+
+ list_for_each_safe(head, next, &file_list) {
+ if (((struct file_node *)head)->fh == fh) {
+ list_del(head);
+ free(head);
+ break;
+ }
+ }
+
+ return fclose(fh);
+}
+
+static void test_close_all(void)
+{
+ struct list_head *head, *next;
+ struct file_node *node;
+
+ list_for_each_safe(head, next, &file_list) {
+ node = (struct file_node *)head;
+ if (node->fh)
+ fclose(node->fh);
+ else
+ close(node->fd);
+ free(node);
+ }
+ init_list_head(&file_list);
+}
+
+/* Wrap test main function */
+
+static jmp_buf test_return;
+static FILE *orig_stderr;
+
+void test_exit(int rc)
+{
+ longjmp(test_return, rc + 1);
+}
+
+int test_ioctl(const struct cmd_expect *expect, void *cmd)
+{
+ int rc;
+
+ if (!expect->cmd || *(u32 *)cmd != *(const u32 *)expect->cmd) {
+ /* We have no idea how long this command structure is */
+ fprintf(orig_stderr, "Unexpected ioctl: cmd=%#10x\n",
+ *(u32 *)cmd);
+ return TEST_IOCTL_MISMATCH;
+ }
+
+ if (memcmp(cmd, expect->cmd, expect->cmd_len)) {
+ fprintf(orig_stderr, "Expected ioctl structure:\n");
+ dump_hex(orig_stderr, expect->cmd, expect->cmd_len, 0);
+ fprintf(orig_stderr, "Actual ioctl structure:\n");
+ dump_hex(orig_stderr, cmd, expect->cmd_len, 0);
+ return TEST_IOCTL_MISMATCH;
+ }
+
+ if (expect->resp)
+ memcpy(cmd, expect->resp, expect->resp_len);
+ rc = expect->rc;
+
+ /* Convert kernel return code according to libc convention */
+ if (rc >= 0) {
+ return rc;
+ } else {
+ errno = -rc;
+ return -1;
+ }
+}
+
+int test_cmdline(const char *args)
+{
+ int volatile orig_stdout_fd = -1;
+ int volatile orig_stderr_fd = -1;
+ char **volatile argv;
+ int volatile argc;
+ int dev_null = -1;
+ const char *arg;
+ size_t len;
+ int rc, i;
+
+ /* Convert line to argv */
+ argc = 1;
+ arg = args;
+ for (;;) {
+ len = strcspn(arg, " ");
+ if (len == 0)
+ break;
+ argc++;
+ if (arg[len] == 0)
+ break;
+ arg += len + 1;
+ }
+ argv = test_calloc(argc + 1, sizeof(argv[0]));
+ argv[0] = test_strdup("ethtool");
+ arg = args;
+ for (i = 1; i < argc; i++) {
+ len = strcspn(arg, " ");
+ argv[i] = test_malloc(len + 1);
+ memcpy(argv[i], arg, len);
+ argv[i][len] = 0;
+ arg += len + 1;
+ }
+
+ dev_null = open("/dev/null", O_RDWR);
+ if (dev_null < 0) {
+ perror("open /dev/null");
+ rc = -1;
+ goto out;
+ }
+
+ fflush(NULL);
+ dup2(dev_null, STDIN_FILENO);
+ if (getenv("TEST_TEST_VERBOSE")) {
+ orig_stderr = stderr;
+ } else {
+ orig_stdout_fd = dup(STDOUT_FILENO);
+ if (orig_stdout_fd < 0) {
+ perror("dup stdout");
+ rc = -1;
+ goto out;
+ }
+ dup2(dev_null, STDOUT_FILENO);
+ orig_stderr_fd = dup(STDERR_FILENO);
+ if (orig_stderr_fd < 0) {
+ perror("dup stderr");
+ rc = -1;
+ goto out;
+ }
+ orig_stderr = fdopen(orig_stderr_fd, "w");
+ if (orig_stderr == NULL) {
+ perror("fdopen orig_stderr_fd");
+ rc = -1;
+ goto out;
+ }
+ dup2(dev_null, STDERR_FILENO);
+ }
+
+ rc = setjmp(test_return);
+ rc = rc ? rc - 1 : test_main(argc, argv);
+
+out:
+ fflush(NULL);
+ if (orig_stderr_fd >= 0) {
+ dup2(orig_stderr_fd, STDERR_FILENO);
+ if (orig_stderr)
+ fclose(orig_stderr);
+ else
+ close(orig_stderr_fd);
+ }
+ orig_stderr = NULL;
+ if (orig_stdout_fd >= 0) {
+ dup2(orig_stdout_fd, STDOUT_FILENO);
+ close(orig_stdout_fd);
+ }
+ if (dev_null >= 0)
+ close(dev_null);
+
+ test_free_all();
+ test_close_all();
+ return rc;
+}
diff --git a/test-features.c b/test-features.c
new file mode 100644
index 0000000..b9f80f0
--- /dev/null
+++ b/test-features.c
@@ -0,0 +1,555 @@
+/****************************************************************************
+ * Test cases for ethtool features
+ * Copyright 2012 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define TEST_NO_WRAPPERS
+#include "internal.h"
+
+static const struct {
+ struct ethtool_sset_info cmd;
+ u32 data[1];
+}
+cmd_gssetinfo = { { ETHTOOL_GSSET_INFO, 0, 1ULL << ETH_SS_FEATURES }, { 34 } };
+
+static const struct ethtool_value
+cmd_grxcsum_off = { ETHTOOL_GRXCSUM, 0 },
+cmd_grxcsum_on = { ETHTOOL_GRXCSUM, 1 },
+cmd_srxcsum_off = { ETHTOOL_SRXCSUM, 0 },
+cmd_srxcsum_on = { ETHTOOL_SRXCSUM, 1 },
+cmd_gtxcsum_off = { ETHTOOL_GTXCSUM, 0 },
+cmd_gtxcsum_on = { ETHTOOL_GTXCSUM, 1 },
+cmd_stxcsum_off = { ETHTOOL_STXCSUM, 0 },
+cmd_stxcsum_on = { ETHTOOL_STXCSUM, 1 },
+cmd_gsg_off = { ETHTOOL_GSG, 0 },
+cmd_gsg_on = { ETHTOOL_GSG, 1 },
+cmd_ssg_off = { ETHTOOL_SSG, 0 },
+cmd_ssg_on = { ETHTOOL_SSG, 1 },
+cmd_gtso_off = { ETHTOOL_GTSO, 0 },
+cmd_gtso_on = { ETHTOOL_GTSO, 1 },
+cmd_stso_off = { ETHTOOL_STSO, 0 },
+cmd_stso_on = { ETHTOOL_STSO, 1 },
+cmd_gufo_off = { ETHTOOL_GUFO, 0 },
+cmd_gufo_on = { ETHTOOL_GUFO, 1 },
+cmd_sufo_off = { ETHTOOL_SUFO, 0 },
+cmd_sufo_on = { ETHTOOL_SUFO, 1 },
+cmd_ggso_off = { ETHTOOL_GGSO, 0 },
+cmd_ggso_on = { ETHTOOL_GGSO, 1 },
+cmd_sgso_off = { ETHTOOL_SGSO, 0 },
+cmd_sgso_on = { ETHTOOL_SGSO, 1 },
+cmd_ggro_off = { ETHTOOL_GGRO, 0 },
+cmd_ggro_on = { ETHTOOL_GGRO, 1 },
+cmd_sgro_off = { ETHTOOL_SGRO, 0 },
+cmd_sgro_on = { ETHTOOL_SGRO, 1 },
+cmd_gflags_off = { ETHTOOL_GFLAGS, 0 },
+cmd_gflags_on = { ETHTOOL_GFLAGS,
+ ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN |
+ ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH },
+cmd_sflags_off = { ETHTOOL_SFLAGS, 0 },
+cmd_sflags_ntuple = { ETHTOOL_GFLAGS, ETH_FLAG_NTUPLE },
+cmd_sflags_on = { ETHTOOL_SFLAGS,
+ ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN |
+ ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH },
+cmd_sflags_not_rxhash = { ETHTOOL_SFLAGS,
+ ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN |
+ ETH_FLAG_NTUPLE };
+
+static const struct cmd_expect cmd_expect_get_strings_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_get_features_off_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_get_features_off_old_some_unsup[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, -EOPNOTSUPP },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4, -EOPNOTSUPP },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_get_features_off_old_some_priv[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EPERM },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4, -EPERM },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_off_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
+ { &cmd_grxcsum_on, 4, 0, &cmd_grxcsum_on, sizeof(cmd_grxcsum_on) },
+ { &cmd_gtxcsum_on, 4, 0, &cmd_gtxcsum_on, sizeof(cmd_gtxcsum_on) },
+ { &cmd_gsg_on, 4, 0, &cmd_gsg_on, sizeof(cmd_gsg_on) },
+ { &cmd_gtso_on, 4, 0, &cmd_gtso_on, sizeof(cmd_gtso_on) },
+ { &cmd_gufo_on, 4, 0, &cmd_gufo_on, sizeof(cmd_gufo_on) },
+ { &cmd_ggso_on, 4, 0, &cmd_ggso_on, sizeof(cmd_ggso_on) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_on, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_on) },
+ { &cmd_srxcsum_off, sizeof(cmd_srxcsum_off), 0, 0, 0 },
+ { &cmd_stxcsum_off, sizeof(cmd_stxcsum_off), 0, 0, 0 },
+ { &cmd_ssg_off, sizeof(cmd_ssg_off), 0, 0, 0 },
+ { &cmd_stso_off, sizeof(cmd_stso_off), 0, 0, 0 },
+ { &cmd_sufo_off, sizeof(cmd_sufo_off), 0, 0, 0 },
+ { &cmd_sgso_off, sizeof(cmd_sgso_off), 0, 0, 0 },
+ { &cmd_sgro_off, sizeof(cmd_sgro_off), 0, 0, 0 },
+ { &cmd_sflags_off, sizeof(cmd_sflags_off), 0, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_sflags_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_on_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_srxcsum_on, sizeof(cmd_srxcsum_on), 0, 0, 0 },
+ { &cmd_stxcsum_on, sizeof(cmd_stxcsum_on), 0, 0, 0 },
+ { &cmd_ssg_on, sizeof(cmd_ssg_on), 0, 0, 0 },
+ { &cmd_stso_on, sizeof(cmd_stso_on), 0, 0, 0 },
+ { &cmd_sufo_on, sizeof(cmd_sufo_on), 0, 0, 0 },
+ { &cmd_sgso_on, sizeof(cmd_sgso_on), 0, 0, 0 },
+ { &cmd_sgro_on, sizeof(cmd_sgro_on), 0, 0, 0 },
+ { &cmd_sflags_on, sizeof(cmd_sflags_on), 0, 0, 0 },
+ { &cmd_grxcsum_on, 4, 0, &cmd_grxcsum_on, sizeof(cmd_grxcsum_on) },
+ { &cmd_gtxcsum_on, 4, 0, &cmd_gtxcsum_on, sizeof(cmd_gtxcsum_on) },
+ { &cmd_gsg_on, 4, 0, &cmd_gsg_on, sizeof(cmd_gsg_on) },
+ { &cmd_gtso_on, 4, 0, &cmd_gtso_on, sizeof(cmd_gtso_on) },
+ { &cmd_gufo_on, 4, 0, &cmd_gufo_on, sizeof(cmd_gufo_on) },
+ { &cmd_ggso_on, 4, 0, &cmd_ggso_on, sizeof(cmd_ggso_on) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_on, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_on) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_unsup_on_old[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd), -EINVAL },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_stxcsum_on, sizeof(cmd_stxcsum_on), -EOPNOTSUPP },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct {
+ struct ethtool_gstrings cmd;
+ u8 data[34][ETH_GSTRING_LEN];
+}
+cmd_gstrings = {
+ { ETHTOOL_GSTRINGS, ETH_SS_FEATURES, 34 },
+ {
+ "tx-scatter-gather",
+ "tx-checksum-ipv4",
+ "",
+ "tx-checksum-ip-generic",
+ "tx-checksum-ipv6",
+ "highdma",
+ "tx-scatter-gather-fraglist",
+ "tx-vlan-hw-insert",
+ "rx-vlan-hw-parse",
+ "rx-vlan-filter",
+ "vlan-challenged",
+ "tx-generic-segmentation",
+ "tx-lockless",
+ "netns-local",
+ "rx-gro",
+ "rx-lro",
+ "tx-tcp-segmentation",
+ "tx-udp-fragmentation",
+ "tx-gso-robust",
+ "tx-tcp-ecn-segmentation",
+ "tx-tcp6-segmentation",
+ "tx-fcoe-segmentation",
+ "",
+ "",
+ "tx-checksum-fcoe-crc",
+ "tx-checksum-sctp",
+ "fcoe-mtu",
+ "rx-ntuple-filter",
+ "rx-hashing",
+ "rx-checksum",
+ "tx-nocache-copy",
+ "loopback",
+ "rx-fcs",
+ "rx-all",
+ }
+};
+
+static const struct {
+ struct ethtool_gfeatures cmd;
+ struct ethtool_get_features_block data[2];
+}
+ /* available requested active never_changed */
+/* minimal: only GRO and GSO are available (and GSO won't work) */
+cmd_gfeatures_min_off = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0x00004800, 0x00000000, 0x00000000, 0x00003400},
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000}}
+},
+cmd_gfeatures_min_on = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0x00004800, 0x00004800, 0x00004000, 0x00003400},
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000}}
+},
+/* maximal: everything that isn't never-changed is available */
+cmd_gfeatures_max_off = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0xffffcbff, 0x00000000, 0x00000000, 0x00003400 },
+ { 0x00000003, 0x00000000, 0x00000000, 0x00000000 }}
+},
+cmd_gfeatures_max_on = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0xffffcbff, 0xffffcbff, 0xffffcbff, 0x00003400 },
+ { 0x00000003, 0x00000003, 0x00000003, 0x00000000 }}
+},
+/* IPv4: GRO, GSO, SG and some IPv4-specific offloads are available */
+cmd_gfeatures_ipv4_off = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0x00014803, 0x00000000, 0x00000000, 0x00003400 },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }}
+},
+cmd_gfeatures_ipv4_on = { { ETHTOOL_GFEATURES, 2 },
+ {{ 0x00014803, 0x00014803, 0x00014803, 0x00003400 },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }}
+};
+
+static const struct {
+ struct ethtool_sfeatures cmd;
+ struct ethtool_set_features_block data[2];
+}
+ /* valid requested */
+cmd_sfeatures_min_on = { { ETHTOOL_SFEATURES, 2 },
+ {{ 0x00004800, 0x00004800 },
+ { 0x00000000, 0x00000000 }} },
+cmd_sfeatures_min_off = { { ETHTOOL_SFEATURES, 2 },
+ {{ 0x00004800, 0x00000000 },
+ { 0x00000000, 0x00000000 }} },
+cmd_sfeatures_noop = { { ETHTOOL_SFEATURES, 2 },
+ {{ 0x00000000, 0x00000000 },
+ { 0x00000000, 0x00000000 }} },
+cmd_sfeatures_ipv4_on = { { ETHTOOL_SFEATURES, 2 },
+ {{ 0x00014803, 0x00014803 },
+ { 0x00000000, 0x00000000 }} };
+
+static const struct cmd_expect cmd_expect_get_strings[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_get_features_min_off[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_get_features_max_on[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_on, 4, 0, &cmd_grxcsum_on, sizeof(cmd_grxcsum_on) },
+ { &cmd_gtxcsum_on, 4, 0, &cmd_gtxcsum_on, sizeof(cmd_gtxcsum_on) },
+ { &cmd_gsg_on, 4, 0, &cmd_gsg_on, sizeof(cmd_gsg_on) },
+ { &cmd_gtso_on, 4, 0, &cmd_gtso_on, sizeof(cmd_gtso_on) },
+ { &cmd_gufo_on, 4, 0, &cmd_gufo_on, sizeof(cmd_gufo_on) },
+ { &cmd_ggso_on, 4, 0, &cmd_ggso_on, sizeof(cmd_ggso_on) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_on, 4, 0, &cmd_gflags_on, sizeof(cmd_sflags_on) },
+ { &cmd_gfeatures_max_on, sizeof(cmd_gfeatures_max_on.cmd),
+ 0, &cmd_gfeatures_max_on, sizeof(cmd_gfeatures_max_on) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_min_off_min_on[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { &cmd_sfeatures_min_on, sizeof(cmd_sfeatures_min_on),
+ ETHTOOL_F_WISH, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_on, sizeof(cmd_gfeatures_min_on.cmd),
+ 0, &cmd_gfeatures_min_on, sizeof(cmd_gfeatures_min_on) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_min_off_min_off[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { &cmd_sfeatures_min_off, sizeof(cmd_sfeatures_min_off), 0, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_min_on_min_off[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_on, sizeof(cmd_gfeatures_min_on.cmd),
+ 0, &cmd_gfeatures_min_on, sizeof(cmd_gfeatures_min_on) },
+ { &cmd_sfeatures_min_off, sizeof(cmd_sfeatures_min_off), 0, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_min_off_unsup_on[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { &cmd_sfeatures_noop, sizeof(cmd_sfeatures_noop), 0, 0, 0 },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off.cmd),
+ 0, &cmd_gfeatures_min_off, sizeof(cmd_gfeatures_min_off) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static const struct cmd_expect cmd_expect_set_features_ipv4_off_many_on[] = {
+ { &cmd_gssetinfo, sizeof(cmd_gssetinfo.cmd),
+ 0, &cmd_gssetinfo, sizeof(cmd_gssetinfo) },
+ { &cmd_gstrings, sizeof(cmd_gstrings.cmd),
+ 0, &cmd_gstrings, sizeof(cmd_gstrings) },
+ { &cmd_grxcsum_off, 4, 0, &cmd_grxcsum_off, sizeof(cmd_grxcsum_off) },
+ { &cmd_gtxcsum_off, 4, 0, &cmd_gtxcsum_off, sizeof(cmd_gtxcsum_off) },
+ { &cmd_gsg_off, 4, 0, &cmd_gsg_off, sizeof(cmd_gsg_off) },
+ { &cmd_gtso_off, 4, 0, &cmd_gtso_off, sizeof(cmd_gtso_off) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_off, 4, 0, &cmd_ggso_off, sizeof(cmd_ggso_off) },
+ { &cmd_ggro_off, 4,0, &cmd_ggro_off, sizeof(cmd_ggro_off) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_ipv4_off, sizeof(cmd_gfeatures_ipv4_off.cmd),
+ 0, &cmd_gfeatures_ipv4_off, sizeof(cmd_gfeatures_ipv4_off) },
+ { &cmd_sfeatures_ipv4_on, sizeof(cmd_sfeatures_ipv4_on), 0, 0, 0 },
+ { &cmd_grxcsum_on, 4, 0, &cmd_grxcsum_on, sizeof(cmd_grxcsum_on) },
+ { &cmd_gtxcsum_on, 4, 0, &cmd_gtxcsum_on, sizeof(cmd_gtxcsum_on) },
+ { &cmd_gsg_on, 4, 0, &cmd_gsg_on, sizeof(cmd_gsg_on) },
+ { &cmd_gtso_on, 4, 0, &cmd_gtso_on, sizeof(cmd_gtso_on) },
+ { &cmd_gufo_off, 4, 0, &cmd_gufo_off, sizeof(cmd_gufo_off) },
+ { &cmd_ggso_on, 4, 0, &cmd_ggso_on, sizeof(cmd_ggso_on) },
+ { &cmd_ggro_on, 4,0, &cmd_ggro_on, sizeof(cmd_ggro_on) },
+ { &cmd_gflags_off, 4, 0, &cmd_gflags_off, sizeof(cmd_gflags_off) },
+ { &cmd_gfeatures_ipv4_on, sizeof(cmd_gfeatures_ipv4_on.cmd),
+ 0, &cmd_gfeatures_ipv4_on, sizeof(cmd_gfeatures_ipv4_on) },
+ { 0, 0, 0, 0, 0 }
+};
+
+static struct test_case {
+ int rc;
+ const char *args;
+ const struct cmd_expect *expect;
+} const test_cases[] = {
+ { 0, "-k devname", cmd_expect_get_features_off_old },
+ { 0, "-k dev_unsup", cmd_expect_get_features_off_old_some_unsup },
+ { 0, "-k dev_priv", cmd_expect_get_features_off_old_some_priv },
+ { 0, "-K devname rx off tx off sg off tso off ufo off gso off lro off rxvlan off txvlan off ntuple off rxhash off gro off",
+ cmd_expect_set_features_off_old },
+ { 0, "-K devname rx on tx on sg on tso on ufo on gso on lro on rxvlan on txvlan on ntuple on rxhash on gro on",
+ cmd_expect_set_features_on_old },
+ { 1, "-K devname tx on sg on", cmd_expect_set_features_unsup_on_old },
+ { 0, "--show-offload devname", cmd_expect_get_features_min_off },
+ { 0, "--show-features devname", cmd_expect_get_features_max_on },
+ { 0, "-K devname rx on tx on sg on tso on ufo on gso on gro on",
+ cmd_expect_set_features_min_off_min_on },
+ { 0, "-K devname rx off tx off sg off tso off ufo off gso off gro off",
+ cmd_expect_set_features_min_off_min_off },
+ { 0, "-K devname rx off tx off sg off tso off ufo off gso off gro off",
+ cmd_expect_set_features_min_on_min_off },
+ { 1, "-K devname tx on sg on",
+ cmd_expect_set_features_min_off_unsup_on },
+ { 0, "--features devname rx on tx on sg on tso on gso on gro on",
+ cmd_expect_set_features_ipv4_off_many_on },
+ { 1, "-K devname rx foo", cmd_expect_get_strings_old },
+ { 1, "-K devname rx foo", cmd_expect_get_strings },
+ { 1, "--offload devname rx", cmd_expect_get_strings_old },
+ { 1, "--features devname rx", cmd_expect_get_strings },
+ { 1, "--features devname foo on", cmd_expect_get_strings_old },
+ { 1, "--offload devname foo on", cmd_expect_get_strings },
+};
+
+static int expect_matched;
+static const struct cmd_expect *expect_next;
+
+int send_ioctl(struct cmd_context *ctx, void *cmd)
+{
+ int rc = test_ioctl(expect_next, cmd);
+
+ if (rc == TEST_IOCTL_MISMATCH) {
+ expect_matched = 0;
+ test_exit(0);
+ }
+ expect_next++;
+ return rc;
+}
+
+#ifdef ETHTOOL_ENABLE_NETLINK
+struct nl_socket;
+struct nl_msg_buff;
+
+ssize_t nlsock_sendmsg(struct nl_socket *nlsk, struct nl_msg_buff *altbuff)
+{
+ /* Should not be called with test-features */
+ exit(1);
+}
+#endif
+
+int main(void)
+{
+ const struct test_case *tc;
+ int test_rc;
+ int rc = 0;
+
+ for (tc = test_cases; tc < test_cases + ARRAY_SIZE(test_cases); tc++) {
+ if (getenv("ETHTOOL_TEST_VERBOSE"))
+ printf("I: Test command line: ethtool %s\n", tc->args);
+ expect_matched = 1;
+ expect_next = tc->expect;
+ test_rc = test_cmdline(tc->args);
+
+ /* If we found a mismatch, or there is still another
+ * expected ioctl to match, the test failed.
+ */
+ if (!expect_matched || expect_next->cmd) {
+ fprintf(stderr,
+ "E: ethtool %s deviated from the expected "
+ "ioctl sequence after %zu calls\n",
+ tc->args, expect_next - tc->expect);
+ rc = 1;
+ } else if (test_rc != tc->rc) {
+ fprintf(stderr, "E: ethtool %s returns %d\n",
+ tc->args, test_rc);
+ rc = 1;
+ }
+ }
+
+ return rc;
+}
diff --git a/tg3.c b/tg3.c
new file mode 100644
index 0000000..ebdef2d
--- /dev/null
+++ b/tg3.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <string.h>
+#include "internal.h"
+
+#define TG3_MAGIC 0x669955aa
+
+int tg3_dump_eeprom(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_eeprom *ee)
+{
+ unsigned int i;
+
+ if (ee->magic != TG3_MAGIC) {
+ fprintf(stderr, "Magic number 0x%08x does not match 0x%08x\n",
+ ee->magic, TG3_MAGIC);
+ return -1;
+ }
+
+ fprintf(stdout, "Address \tData\n");
+ fprintf(stdout, "----------\t----\n");
+ for (i = 0; i < ee->len; i++)
+ fprintf(stdout, "0x%08x\t0x%02x\n", i + ee->offset, ee->data[i]);
+
+ return 0;
+}
+
+int tg3_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ unsigned int i;
+ u32 reg;
+
+ fprintf(stdout, "Offset\tValue\n");
+ fprintf(stdout, "------\t----------\n");
+ for (i = 0; i < regs->len; i += sizeof(reg)) {
+ memcpy(&reg, &regs->data[i], sizeof(reg));
+ if (reg)
+ fprintf(stdout, "0x%04x\t0x%08x\n", i, reg);
+
+ }
+ fprintf(stdout, "\n");
+ return 0;
+}
diff --git a/tse.c b/tse.c
new file mode 100644
index 0000000..fb00d21
--- /dev/null
+++ b/tse.c
@@ -0,0 +1,112 @@
+/****************************************************************************
+ * Support for the Altera Triple Speed Ethernet 10/100/1000 Controller
+ *
+ * Copyright (C) 2014 Altera Corporation
+ *
+ * Author: Vince Bridgers <vbridgers2013@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "internal.h"
+
+#define ALTERA_VERSION_MASK 0xffff
+#define ALTERA_ETHTOOL_V1 1
+
+int
+bitset(u32 val, int bit)
+{
+ if (val & (1 << bit))
+ return 1;
+ return 0;
+}
+
+int altera_tse_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ int i;
+ u32 *tsereg = (unsigned int *)regs->data;
+ u32 version = regs->version;
+
+ if ((version & ALTERA_VERSION_MASK) > ALTERA_ETHTOOL_V1)
+ return -1;
+
+ /*
+ * Version 1: Initial TSE driver release. No feature information
+ * available, 32-bits of version is equal to 1.
+ *
+ * Version 2: Lower 16-bits of version is 2, upper 16 bits are:
+ * Bit 16 - SGMDA or MSGDMA Registers
+ * Bit 17 - PCS Present
+ * Bit 18 - Supplementary MAC Address Filter Registers Present
+ * Bit 19 - Multicast Hash Filter Present
+ * Bit 20 - IEEE 1588 Feature Present
+ */
+ fprintf(stdout, "Altera TSE 10/100/1000 Registers, Version %d\n",
+ version);
+ fprintf(stdout, "---------------------------------------------\n");
+ fprintf(stdout, "Revision 0x%08X\n", tsereg[0]);
+ fprintf(stdout, " Core Version %d.%d\n",
+ (tsereg[0] & 0xffff) >> 8,
+ tsereg[0] & 0xff);
+ fprintf(stdout, " CustVersion 0x%08X\n", tsereg[0] >> 16);
+ fprintf(stdout, "Scratch 0x%08X\n", tsereg[1]);
+ fprintf(stdout, "Command/Config 0x%08X\n", tsereg[2]);
+ fprintf(stdout, " (0)TX_EN %d\n", bitset(tsereg[2], 0));
+ fprintf(stdout, " (1)RX_EN %d\n", bitset(tsereg[2], 1));
+ fprintf(stdout, " (2)XON_GEN %d\n", bitset(tsereg[2], 2));
+ fprintf(stdout, " (3)ETH_SPEED %d\n", bitset(tsereg[2], 3));
+ fprintf(stdout, " (4)PROMIS_EN %d\n", bitset(tsereg[2], 4));
+ fprintf(stdout, " (5)PAD_EN %d\n", bitset(tsereg[2], 5));
+ fprintf(stdout, " (6)CRC_FWD %d\n", bitset(tsereg[2], 6));
+ fprintf(stdout, " (7)PAUSE_FWD %d\n", bitset(tsereg[2], 7));
+ fprintf(stdout, " (8)PAUSE_IGN %d\n", bitset(tsereg[2], 8));
+ fprintf(stdout, " (9)TXADDR_INS %d\n", bitset(tsereg[2], 9));
+ fprintf(stdout, " (10)HD_EN %d\n", bitset(tsereg[2], 10));
+ fprintf(stdout, " (11)EXCESS_COL %d\n", bitset(tsereg[2], 11));
+ fprintf(stdout, " (12)LATE_COL %d\n", bitset(tsereg[2], 12));
+ fprintf(stdout, " (13)SW_RESET %d\n", bitset(tsereg[2], 13));
+ fprintf(stdout, " (14)MHASH_SEL %d\n", bitset(tsereg[2], 14));
+ fprintf(stdout, " (15)LOOP_EN %d\n", bitset(tsereg[2], 15));
+ fprintf(stdout, " (16-18)TX_ADDR_SEL %d\n",
+ (tsereg[2] & 0x30000) >> 16);
+ fprintf(stdout, " (19)MAGIC_EN %d\n", bitset(tsereg[2], 19));
+ fprintf(stdout, " (20)SLEEP %d\n", bitset(tsereg[2], 20));
+ fprintf(stdout, " (21)WAKEUP %d\n", bitset(tsereg[2], 21));
+ fprintf(stdout, " (22)XOFF_GEN %d\n", bitset(tsereg[2], 22));
+ fprintf(stdout, " (23)CTRL_FRAME_EN %d\n", bitset(tsereg[2], 23));
+ fprintf(stdout, " (24)NO_LEN_CHECK %d\n", bitset(tsereg[2], 24));
+ fprintf(stdout, " (25)ENA_10 %d\n", bitset(tsereg[2], 25));
+ fprintf(stdout, " (26)RX_ERR_DISC %d\n", bitset(tsereg[2], 26));
+ fprintf(stdout, " (31)CTRL_RESET %d\n", bitset(tsereg[2], 31));
+ fprintf(stdout, "mac_0 0x%08X\n", tsereg[3]);
+ fprintf(stdout, "mac_1 0x%08X\n", tsereg[4]);
+ fprintf(stdout, "frm_length 0x%08X\n", tsereg[5]);
+ fprintf(stdout, "pause_quant 0x%08X\n", tsereg[6]);
+ fprintf(stdout, "rx_section_empty 0x%08X\n", tsereg[7]);
+ fprintf(stdout, "rx_section_full 0x%08X\n", tsereg[8]);
+ fprintf(stdout, "tx_section_empty 0x%08X\n", tsereg[9]);
+ fprintf(stdout, "tx_section_full 0x%08X\n", tsereg[0xa]);
+ fprintf(stdout, "rx_almost_empty 0x%08X\n", tsereg[0xb]);
+ fprintf(stdout, "rx_almost_full 0x%08X\n", tsereg[0xc]);
+ fprintf(stdout, "tx_almost_empty 0x%08X\n", tsereg[0xd]);
+ fprintf(stdout, "tx_almost_full 0x%08X\n", tsereg[0xe]);
+ fprintf(stdout, "mdio_addr0 0x%08X\n", tsereg[0xf]);
+ fprintf(stdout, "mdio_addr1 0x%08X\n", tsereg[0x10]);
+ fprintf(stdout, "holdoff_quant 0x%08X\n", tsereg[0x11]);
+
+ fprintf(stdout, "tx_ipg_length 0x%08X\n", tsereg[0x17]);
+ fprintf(stdout, "Transmit Command 0x%08X\n", tsereg[0x3a]);
+ fprintf(stdout, "Receive Command 0x%08X\n", tsereg[0x3b]);
+
+ for (i = 0; i < 64; i++)
+ fprintf(stdout, "Multicast Hash[%02d] 0x%08X\n",
+ i,
+ tsereg[0x40 + i]);
+ return 0;
+}
+
diff --git a/uapi/linux/const.h b/uapi/linux/const.h
new file mode 100644
index 0000000..1eb84b5
--- /dev/null
+++ b/uapi/linux/const.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* const.h: Macros for dealing with constants. */
+
+#ifndef _LINUX_CONST_H
+#define _LINUX_CONST_H
+
+/* Some constant macros are used in both assembler and
+ * C code. Therefore we cannot annotate them always with
+ * 'UL' and other type specifiers unilaterally. We
+ * use the following macros to deal with this.
+ *
+ * Similarly, _AT() will cast an expression with a type in C, but
+ * leave it unchanged in asm.
+ */
+
+#ifdef __ASSEMBLY__
+#define _AC(X,Y) X
+#define _AT(T,X) X
+#else
+#define __AC(X,Y) (X##Y)
+#define _AC(X,Y) __AC(X,Y)
+#define _AT(T,X) ((T)(X))
+#endif
+
+#define _UL(x) (_AC(x, UL))
+#define _ULL(x) (_AC(x, ULL))
+
+#define _BITUL(x) (_UL(1) << (x))
+#define _BITULL(x) (_ULL(1) << (x))
+
+#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1)
+#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
+
+#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+#endif /* _LINUX_CONST_H */
diff --git a/uapi/linux/ethtool.h b/uapi/linux/ethtool.h
new file mode 100644
index 0000000..1d0731b
--- /dev/null
+++ b/uapi/linux/ethtool.h
@@ -0,0 +1,2206 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * ethtool.h: Defines for Linux ethtool.
+ *
+ * Copyright (C) 1998 David S. Miller (davem@redhat.com)
+ * Copyright 2001 Jeff Garzik <jgarzik@pobox.com>
+ * Portions Copyright 2001 Sun Microsystems (thockin@sun.com)
+ * Portions Copyright 2002 Intel (eli.kupermann@intel.com,
+ * christopher.leech@intel.com,
+ * scott.feldman@intel.com)
+ * Portions Copyright (C) Sun Microsystems 2008
+ */
+
+#ifndef _LINUX_ETHTOOL_H
+#define _LINUX_ETHTOOL_H
+
+#include <linux/const.h>
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#include <limits.h> /* for INT_MAX */
+
+/* All structures exposed to userland should be defined such that they
+ * have the same layout for 32-bit and 64-bit userland.
+ */
+
+/* Note on reserved space.
+ * Reserved fields must not be accessed directly by user space because
+ * they may be replaced by a different field in the future. They must
+ * be initialized to zero before making the request, e.g. via memset
+ * of the entire structure or implicitly by not being set in a structure
+ * initializer.
+ */
+
+/**
+ * struct ethtool_cmd - DEPRECATED, link control and status
+ * This structure is DEPRECATED, please use struct ethtool_link_settings.
+ * @cmd: Command number = %ETHTOOL_GSET or %ETHTOOL_SSET
+ * @supported: Bitmask of %SUPPORTED_* flags for the link modes,
+ * physical connectors and other link features for which the
+ * interface supports autonegotiation or auto-detection.
+ * Read-only.
+ * @advertising: Bitmask of %ADVERTISED_* flags for the link modes,
+ * physical connectors and other link features that are
+ * advertised through autonegotiation or enabled for
+ * auto-detection.
+ * @speed: Low bits of the speed, 1Mb units, 0 to INT_MAX or SPEED_UNKNOWN
+ * @duplex: Duplex mode; one of %DUPLEX_*
+ * @port: Physical connector type; one of %PORT_*
+ * @phy_address: MDIO address of PHY (transceiver); 0 or 255 if not
+ * applicable. For clause 45 PHYs this is the PRTAD.
+ * @transceiver: Historically used to distinguish different possible
+ * PHY types, but not in a consistent way. Deprecated.
+ * @autoneg: Enable/disable autonegotiation and auto-detection;
+ * either %AUTONEG_DISABLE or %AUTONEG_ENABLE
+ * @mdio_support: Bitmask of %ETH_MDIO_SUPPORTS_* flags for the MDIO
+ * protocols supported by the interface; 0 if unknown.
+ * Read-only.
+ * @maxtxpkt: Historically used to report TX IRQ coalescing; now
+ * obsoleted by &struct ethtool_coalesce. Read-only; deprecated.
+ * @maxrxpkt: Historically used to report RX IRQ coalescing; now
+ * obsoleted by &struct ethtool_coalesce. Read-only; deprecated.
+ * @speed_hi: High bits of the speed, 1Mb units, 0 to INT_MAX or SPEED_UNKNOWN
+ * @eth_tp_mdix: Ethernet twisted-pair MDI(-X) status; one of
+ * %ETH_TP_MDI_*. If the status is unknown or not applicable, the
+ * value will be %ETH_TP_MDI_INVALID. Read-only.
+ * @eth_tp_mdix_ctrl: Ethernet twisted pair MDI(-X) control; one of
+ * %ETH_TP_MDI_*. If MDI(-X) control is not implemented, reads
+ * yield %ETH_TP_MDI_INVALID and writes may be ignored or rejected.
+ * When written successfully, the link should be renegotiated if
+ * necessary.
+ * @lp_advertising: Bitmask of %ADVERTISED_* flags for the link modes
+ * and other link features that the link partner advertised
+ * through autonegotiation; 0 if unknown or not applicable.
+ * Read-only.
+ * @reserved: Reserved for future use; see the note on reserved space.
+ *
+ * The link speed in Mbps is split between @speed and @speed_hi. Use
+ * the ethtool_cmd_speed() and ethtool_cmd_speed_set() functions to
+ * access it.
+ *
+ * If autonegotiation is disabled, the speed and @duplex represent the
+ * fixed link mode and are writable if the driver supports multiple
+ * link modes. If it is enabled then they are read-only; if the link
+ * is up they represent the negotiated link mode; if the link is down,
+ * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and
+ * @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
+ *
+ * Some hardware interfaces may have multiple PHYs and/or physical
+ * connectors fitted or do not allow the driver to detect which are
+ * fitted. For these interfaces @port and/or @phy_address may be
+ * writable, possibly dependent on @autoneg being %AUTONEG_DISABLE.
+ * Otherwise, attempts to write different values may be ignored or
+ * rejected.
+ *
+ * Users should assume that all fields not marked read-only are
+ * writable and subject to validation by the driver. They should use
+ * %ETHTOOL_GSET to get the current values before making specific
+ * changes and then applying them with %ETHTOOL_SSET.
+ *
+ * Deprecated fields should be ignored by both users and drivers.
+ */
+struct ethtool_cmd {
+ __u32 cmd;
+ __u32 supported;
+ __u32 advertising;
+ __u16 speed;
+ __u8 duplex;
+ __u8 port;
+ __u8 phy_address;
+ __u8 transceiver;
+ __u8 autoneg;
+ __u8 mdio_support;
+ __u32 maxtxpkt;
+ __u32 maxrxpkt;
+ __u16 speed_hi;
+ __u8 eth_tp_mdix;
+ __u8 eth_tp_mdix_ctrl;
+ __u32 lp_advertising;
+ __u32 reserved[2];
+};
+
+static __inline__ void ethtool_cmd_speed_set(struct ethtool_cmd *ep,
+ __u32 speed)
+{
+ ep->speed = (__u16)(speed & 0xFFFF);
+ ep->speed_hi = (__u16)(speed >> 16);
+}
+
+static __inline__ __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep)
+{
+ return (ep->speed_hi << 16) | ep->speed;
+}
+
+/* Device supports clause 22 register access to PHY or peripherals
+ * using the interface defined in <linux/mii.h>. This should not be
+ * set if there are known to be no such peripherals present or if
+ * the driver only emulates clause 22 registers for compatibility.
+ */
+#define ETH_MDIO_SUPPORTS_C22 1
+
+/* Device supports clause 45 register access to PHY or peripherals
+ * using the interface defined in <linux/mii.h> and <linux/mdio.h>.
+ * This should not be set if there are known to be no such peripherals
+ * present.
+ */
+#define ETH_MDIO_SUPPORTS_C45 2
+
+#define ETHTOOL_FWVERS_LEN 32
+#define ETHTOOL_BUSINFO_LEN 32
+#define ETHTOOL_EROMVERS_LEN 32
+
+/**
+ * struct ethtool_drvinfo - general driver and device information
+ * @cmd: Command number = %ETHTOOL_GDRVINFO
+ * @driver: Driver short name. This should normally match the name
+ * in its bus driver structure (e.g. pci_driver::name). Must
+ * not be an empty string.
+ * @version: Driver version string; may be an empty string
+ * @fw_version: Firmware version string; driver defined; may be an
+ * empty string
+ * @erom_version: Expansion ROM version string; driver defined; may be
+ * an empty string
+ * @bus_info: Device bus address. This should match the dev_name()
+ * string for the underlying bus device, if there is one. May be
+ * an empty string.
+ * @reserved2: Reserved for future use; see the note on reserved space.
+ * @n_priv_flags: Number of flags valid for %ETHTOOL_GPFLAGS and
+ * %ETHTOOL_SPFLAGS commands; also the number of strings in the
+ * %ETH_SS_PRIV_FLAGS set
+ * @n_stats: Number of u64 statistics returned by the %ETHTOOL_GSTATS
+ * command; also the number of strings in the %ETH_SS_STATS set
+ * @testinfo_len: Number of results returned by the %ETHTOOL_TEST
+ * command; also the number of strings in the %ETH_SS_TEST set
+ * @eedump_len: Size of EEPROM accessible through the %ETHTOOL_GEEPROM
+ * and %ETHTOOL_SEEPROM commands, in bytes
+ * @regdump_len: Size of register dump returned by the %ETHTOOL_GREGS
+ * command, in bytes
+ *
+ * Users can use the %ETHTOOL_GSSET_INFO command to get the number of
+ * strings in any string set (from Linux 2.6.34).
+ */
+struct ethtool_drvinfo {
+ __u32 cmd;
+ char driver[32];
+ char version[32];
+ char fw_version[ETHTOOL_FWVERS_LEN];
+ char bus_info[ETHTOOL_BUSINFO_LEN];
+ char erom_version[ETHTOOL_EROMVERS_LEN];
+ char reserved2[12];
+ __u32 n_priv_flags;
+ __u32 n_stats;
+ __u32 testinfo_len;
+ __u32 eedump_len;
+ __u32 regdump_len;
+};
+
+#define SOPASS_MAX 6
+
+/**
+ * struct ethtool_wolinfo - Wake-On-Lan configuration
+ * @cmd: Command number = %ETHTOOL_GWOL or %ETHTOOL_SWOL
+ * @supported: Bitmask of %WAKE_* flags for supported Wake-On-Lan modes.
+ * Read-only.
+ * @wolopts: Bitmask of %WAKE_* flags for enabled Wake-On-Lan modes.
+ * @sopass: SecureOn(tm) password; meaningful only if %WAKE_MAGICSECURE
+ * is set in @wolopts.
+ */
+struct ethtool_wolinfo {
+ __u32 cmd;
+ __u32 supported;
+ __u32 wolopts;
+ __u8 sopass[SOPASS_MAX];
+};
+
+/* for passing single values */
+struct ethtool_value {
+ __u32 cmd;
+ __u32 data;
+};
+
+#define PFC_STORM_PREVENTION_AUTO 0xffff
+#define PFC_STORM_PREVENTION_DISABLE 0
+
+enum tunable_id {
+ ETHTOOL_ID_UNSPEC,
+ ETHTOOL_RX_COPYBREAK,
+ ETHTOOL_TX_COPYBREAK,
+ ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */
+ ETHTOOL_TX_COPYBREAK_BUF_SIZE,
+ /*
+ * Add your fresh new tunable attribute above and remember to update
+ * tunable_strings[] in net/ethtool/common.c
+ */
+ __ETHTOOL_TUNABLE_COUNT,
+};
+
+enum tunable_type_id {
+ ETHTOOL_TUNABLE_UNSPEC,
+ ETHTOOL_TUNABLE_U8,
+ ETHTOOL_TUNABLE_U16,
+ ETHTOOL_TUNABLE_U32,
+ ETHTOOL_TUNABLE_U64,
+ ETHTOOL_TUNABLE_STRING,
+ ETHTOOL_TUNABLE_S8,
+ ETHTOOL_TUNABLE_S16,
+ ETHTOOL_TUNABLE_S32,
+ ETHTOOL_TUNABLE_S64,
+};
+
+struct ethtool_tunable {
+ __u32 cmd;
+ __u32 id;
+ __u32 type_id;
+ __u32 len;
+ void *data[];
+};
+
+#define DOWNSHIFT_DEV_DEFAULT_COUNT 0xff
+#define DOWNSHIFT_DEV_DISABLE 0
+
+/* Time in msecs after which link is reported as down
+ * 0 = lowest time supported by the PHY
+ * 0xff = off, link down detection according to standard
+ */
+#define ETHTOOL_PHY_FAST_LINK_DOWN_ON 0
+#define ETHTOOL_PHY_FAST_LINK_DOWN_OFF 0xff
+
+/* Energy Detect Power Down (EDPD) is a feature supported by some PHYs, where
+ * the PHY's RX & TX blocks are put into a low-power mode when there is no
+ * link detected (typically cable is un-plugged). For RX, only a minimal
+ * link-detection is available, and for TX the PHY wakes up to send link pulses
+ * to avoid any lock-ups in case the peer PHY may also be running in EDPD mode.
+ *
+ * Some PHYs may support configuration of the wake-up interval for TX pulses,
+ * and some PHYs may support only disabling TX pulses entirely. For the latter
+ * a special value is required (ETHTOOL_PHY_EDPD_NO_TX) so that this can be
+ * configured from userspace (should the user want it).
+ *
+ * The interval units for TX wake-up are in milliseconds, since this should
+ * cover a reasonable range of intervals:
+ * - from 1 millisecond, which does not sound like much of a power-saver
+ * - to ~65 seconds which is quite a lot to wait for a link to come up when
+ * plugging a cable
+ */
+#define ETHTOOL_PHY_EDPD_DFLT_TX_MSECS 0xffff
+#define ETHTOOL_PHY_EDPD_NO_TX 0xfffe
+#define ETHTOOL_PHY_EDPD_DISABLE 0
+
+enum phy_tunable_id {
+ ETHTOOL_PHY_ID_UNSPEC,
+ ETHTOOL_PHY_DOWNSHIFT,
+ ETHTOOL_PHY_FAST_LINK_DOWN,
+ ETHTOOL_PHY_EDPD,
+ /*
+ * Add your fresh new phy tunable attribute above and remember to update
+ * phy_tunable_strings[] in net/ethtool/common.c
+ */
+ __ETHTOOL_PHY_TUNABLE_COUNT,
+};
+
+/**
+ * struct ethtool_regs - hardware register dump
+ * @cmd: Command number = %ETHTOOL_GREGS
+ * @version: Dump format version. This is driver-specific and may
+ * distinguish different chips/revisions. Drivers must use new
+ * version numbers whenever the dump format changes in an
+ * incompatible way.
+ * @len: On entry, the real length of @data. On return, the number of
+ * bytes used.
+ * @data: Buffer for the register dump
+ *
+ * Users should use %ETHTOOL_GDRVINFO to find the maximum length of
+ * a register dump for the interface. They must allocate the buffer
+ * immediately following this structure.
+ */
+struct ethtool_regs {
+ __u32 cmd;
+ __u32 version;
+ __u32 len;
+ __u8 data[];
+};
+
+/**
+ * struct ethtool_eeprom - EEPROM dump
+ * @cmd: Command number = %ETHTOOL_GEEPROM, %ETHTOOL_GMODULEEEPROM or
+ * %ETHTOOL_SEEPROM
+ * @magic: A 'magic cookie' value to guard against accidental changes.
+ * The value passed in to %ETHTOOL_SEEPROM must match the value
+ * returned by %ETHTOOL_GEEPROM for the same device. This is
+ * unused when @cmd is %ETHTOOL_GMODULEEEPROM.
+ * @offset: Offset within the EEPROM to begin reading/writing, in bytes
+ * @len: On entry, number of bytes to read/write. On successful
+ * return, number of bytes actually read/written. In case of
+ * error, this may indicate at what point the error occurred.
+ * @data: Buffer to read/write from
+ *
+ * Users may use %ETHTOOL_GDRVINFO or %ETHTOOL_GMODULEINFO to find
+ * the length of an on-board or module EEPROM, respectively. They
+ * must allocate the buffer immediately following this structure.
+ */
+struct ethtool_eeprom {
+ __u32 cmd;
+ __u32 magic;
+ __u32 offset;
+ __u32 len;
+ __u8 data[];
+};
+
+/**
+ * struct ethtool_eee - Energy Efficient Ethernet information
+ * @cmd: ETHTOOL_{G,S}EEE
+ * @supported: Mask of %SUPPORTED_* flags for the speed/duplex combinations
+ * for which there is EEE support.
+ * @advertised: Mask of %ADVERTISED_* flags for the speed/duplex combinations
+ * advertised as eee capable.
+ * @lp_advertised: Mask of %ADVERTISED_* flags for the speed/duplex
+ * combinations advertised by the link partner as eee capable.
+ * @eee_active: Result of the eee auto negotiation.
+ * @eee_enabled: EEE configured mode (enabled/disabled).
+ * @tx_lpi_enabled: Whether the interface should assert its tx lpi, given
+ * that eee was negotiated.
+ * @tx_lpi_timer: Time in microseconds the interface delays prior to asserting
+ * its tx lpi (after reaching 'idle' state). Effective only when eee
+ * was negotiated and tx_lpi_enabled was set.
+ * @reserved: Reserved for future use; see the note on reserved space.
+ */
+struct ethtool_eee {
+ __u32 cmd;
+ __u32 supported;
+ __u32 advertised;
+ __u32 lp_advertised;
+ __u32 eee_active;
+ __u32 eee_enabled;
+ __u32 tx_lpi_enabled;
+ __u32 tx_lpi_timer;
+ __u32 reserved[2];
+};
+
+/**
+ * struct ethtool_modinfo - plugin module eeprom information
+ * @cmd: %ETHTOOL_GMODULEINFO
+ * @type: Standard the module information conforms to %ETH_MODULE_SFF_xxxx
+ * @eeprom_len: Length of the eeprom
+ * @reserved: Reserved for future use; see the note on reserved space.
+ *
+ * This structure is used to return the information to
+ * properly size memory for a subsequent call to %ETHTOOL_GMODULEEEPROM.
+ * The type code indicates the eeprom data format
+ */
+struct ethtool_modinfo {
+ __u32 cmd;
+ __u32 type;
+ __u32 eeprom_len;
+ __u32 reserved[8];
+};
+
+/**
+ * struct ethtool_coalesce - coalescing parameters for IRQs and stats updates
+ * @cmd: ETHTOOL_{G,S}COALESCE
+ * @rx_coalesce_usecs: How many usecs to delay an RX interrupt after
+ * a packet arrives.
+ * @rx_max_coalesced_frames: Maximum number of packets to receive
+ * before an RX interrupt.
+ * @rx_coalesce_usecs_irq: Same as @rx_coalesce_usecs, except that
+ * this value applies while an IRQ is being serviced by the host.
+ * @rx_max_coalesced_frames_irq: Same as @rx_max_coalesced_frames,
+ * except that this value applies while an IRQ is being serviced
+ * by the host.
+ * @tx_coalesce_usecs: How many usecs to delay a TX interrupt after
+ * a packet is sent.
+ * @tx_max_coalesced_frames: Maximum number of packets to be sent
+ * before a TX interrupt.
+ * @tx_coalesce_usecs_irq: Same as @tx_coalesce_usecs, except that
+ * this value applies while an IRQ is being serviced by the host.
+ * @tx_max_coalesced_frames_irq: Same as @tx_max_coalesced_frames,
+ * except that this value applies while an IRQ is being serviced
+ * by the host.
+ * @stats_block_coalesce_usecs: How many usecs to delay in-memory
+ * statistics block updates. Some drivers do not have an
+ * in-memory statistic block, and in such cases this value is
+ * ignored. This value must not be zero.
+ * @use_adaptive_rx_coalesce: Enable adaptive RX coalescing.
+ * @use_adaptive_tx_coalesce: Enable adaptive TX coalescing.
+ * @pkt_rate_low: Threshold for low packet rate (packets per second).
+ * @rx_coalesce_usecs_low: How many usecs to delay an RX interrupt after
+ * a packet arrives, when the packet rate is below @pkt_rate_low.
+ * @rx_max_coalesced_frames_low: Maximum number of packets to be received
+ * before an RX interrupt, when the packet rate is below @pkt_rate_low.
+ * @tx_coalesce_usecs_low: How many usecs to delay a TX interrupt after
+ * a packet is sent, when the packet rate is below @pkt_rate_low.
+ * @tx_max_coalesced_frames_low: Maximum nuumber of packets to be sent before
+ * a TX interrupt, when the packet rate is below @pkt_rate_low.
+ * @pkt_rate_high: Threshold for high packet rate (packets per second).
+ * @rx_coalesce_usecs_high: How many usecs to delay an RX interrupt after
+ * a packet arrives, when the packet rate is above @pkt_rate_high.
+ * @rx_max_coalesced_frames_high: Maximum number of packets to be received
+ * before an RX interrupt, when the packet rate is above @pkt_rate_high.
+ * @tx_coalesce_usecs_high: How many usecs to delay a TX interrupt after
+ * a packet is sent, when the packet rate is above @pkt_rate_high.
+ * @tx_max_coalesced_frames_high: Maximum number of packets to be sent before
+ * a TX interrupt, when the packet rate is above @pkt_rate_high.
+ * @rate_sample_interval: How often to do adaptive coalescing packet rate
+ * sampling, measured in seconds. Must not be zero.
+ *
+ * Each pair of (usecs, max_frames) fields specifies that interrupts
+ * should be coalesced until
+ * (usecs > 0 && time_since_first_completion >= usecs) ||
+ * (max_frames > 0 && completed_frames >= max_frames)
+ *
+ * It is illegal to set both usecs and max_frames to zero as this
+ * would cause interrupts to never be generated. To disable
+ * coalescing, set usecs = 0 and max_frames = 1.
+ *
+ * Some implementations ignore the value of max_frames and use the
+ * condition time_since_first_completion >= usecs
+ *
+ * This is deprecated. Drivers for hardware that does not support
+ * counting completions should validate that max_frames == !rx_usecs.
+ *
+ * Adaptive RX/TX coalescing is an algorithm implemented by some
+ * drivers to improve latency under low packet rates and improve
+ * throughput under high packet rates. Some drivers only implement
+ * one of RX or TX adaptive coalescing. Anything not implemented by
+ * the driver causes these values to be silently ignored.
+ *
+ * When the packet rate is below @pkt_rate_high but above
+ * @pkt_rate_low (both measured in packets per second) the
+ * normal {rx,tx}_* coalescing parameters are used.
+ */
+struct ethtool_coalesce {
+ __u32 cmd;
+ __u32 rx_coalesce_usecs;
+ __u32 rx_max_coalesced_frames;
+ __u32 rx_coalesce_usecs_irq;
+ __u32 rx_max_coalesced_frames_irq;
+ __u32 tx_coalesce_usecs;
+ __u32 tx_max_coalesced_frames;
+ __u32 tx_coalesce_usecs_irq;
+ __u32 tx_max_coalesced_frames_irq;
+ __u32 stats_block_coalesce_usecs;
+ __u32 use_adaptive_rx_coalesce;
+ __u32 use_adaptive_tx_coalesce;
+ __u32 pkt_rate_low;
+ __u32 rx_coalesce_usecs_low;
+ __u32 rx_max_coalesced_frames_low;
+ __u32 tx_coalesce_usecs_low;
+ __u32 tx_max_coalesced_frames_low;
+ __u32 pkt_rate_high;
+ __u32 rx_coalesce_usecs_high;
+ __u32 rx_max_coalesced_frames_high;
+ __u32 tx_coalesce_usecs_high;
+ __u32 tx_max_coalesced_frames_high;
+ __u32 rate_sample_interval;
+};
+
+/**
+ * struct ethtool_ringparam - RX/TX ring parameters
+ * @cmd: Command number = %ETHTOOL_GRINGPARAM or %ETHTOOL_SRINGPARAM
+ * @rx_max_pending: Maximum supported number of pending entries per
+ * RX ring. Read-only.
+ * @rx_mini_max_pending: Maximum supported number of pending entries
+ * per RX mini ring. Read-only.
+ * @rx_jumbo_max_pending: Maximum supported number of pending entries
+ * per RX jumbo ring. Read-only.
+ * @tx_max_pending: Maximum supported number of pending entries per
+ * TX ring. Read-only.
+ * @rx_pending: Current maximum number of pending entries per RX ring
+ * @rx_mini_pending: Current maximum number of pending entries per RX
+ * mini ring
+ * @rx_jumbo_pending: Current maximum number of pending entries per RX
+ * jumbo ring
+ * @tx_pending: Current maximum supported number of pending entries
+ * per TX ring
+ *
+ * If the interface does not have separate RX mini and/or jumbo rings,
+ * @rx_mini_max_pending and/or @rx_jumbo_max_pending will be 0.
+ *
+ * There may also be driver-dependent minimum values for the number
+ * of entries per ring.
+ */
+struct ethtool_ringparam {
+ __u32 cmd;
+ __u32 rx_max_pending;
+ __u32 rx_mini_max_pending;
+ __u32 rx_jumbo_max_pending;
+ __u32 tx_max_pending;
+ __u32 rx_pending;
+ __u32 rx_mini_pending;
+ __u32 rx_jumbo_pending;
+ __u32 tx_pending;
+};
+
+/**
+ * struct ethtool_channels - configuring number of network channel
+ * @cmd: ETHTOOL_{G,S}CHANNELS
+ * @max_rx: Read only. Maximum number of receive channel the driver support.
+ * @max_tx: Read only. Maximum number of transmit channel the driver support.
+ * @max_other: Read only. Maximum number of other channel the driver support.
+ * @max_combined: Read only. Maximum number of combined channel the driver
+ * support. Set of queues RX, TX or other.
+ * @rx_count: Valid values are in the range 1 to the max_rx.
+ * @tx_count: Valid values are in the range 1 to the max_tx.
+ * @other_count: Valid values are in the range 1 to the max_other.
+ * @combined_count: Valid values are in the range 1 to the max_combined.
+ *
+ * This can be used to configure RX, TX and other channels.
+ */
+
+struct ethtool_channels {
+ __u32 cmd;
+ __u32 max_rx;
+ __u32 max_tx;
+ __u32 max_other;
+ __u32 max_combined;
+ __u32 rx_count;
+ __u32 tx_count;
+ __u32 other_count;
+ __u32 combined_count;
+};
+
+/**
+ * struct ethtool_pauseparam - Ethernet pause (flow control) parameters
+ * @cmd: Command number = %ETHTOOL_GPAUSEPARAM or %ETHTOOL_SPAUSEPARAM
+ * @autoneg: Flag to enable autonegotiation of pause frame use
+ * @rx_pause: Flag to enable reception of pause frames
+ * @tx_pause: Flag to enable transmission of pause frames
+ *
+ * Drivers should reject a non-zero setting of @autoneg when
+ * autoneogotiation is disabled (or not supported) for the link.
+ *
+ * If the link is autonegotiated, drivers should use
+ * mii_advertise_flowctrl() or similar code to set the advertised
+ * pause frame capabilities based on the @rx_pause and @tx_pause flags,
+ * even if @autoneg is zero. They should also allow the advertised
+ * pause frame capabilities to be controlled directly through the
+ * advertising field of &struct ethtool_cmd.
+ *
+ * If @autoneg is non-zero, the MAC is configured to send and/or
+ * receive pause frames according to the result of autonegotiation.
+ * Otherwise, it is configured directly based on the @rx_pause and
+ * @tx_pause flags.
+ */
+struct ethtool_pauseparam {
+ __u32 cmd;
+ __u32 autoneg;
+ __u32 rx_pause;
+ __u32 tx_pause;
+};
+
+/* Link extended state */
+enum ethtool_link_ext_state {
+ ETHTOOL_LINK_EXT_STATE_AUTONEG,
+ ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
+ ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
+ ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY,
+ ETHTOOL_LINK_EXT_STATE_NO_CABLE,
+ ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
+ ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE,
+ ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE,
+ ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED,
+ ETHTOOL_LINK_EXT_STATE_OVERHEAT,
+ ETHTOOL_LINK_EXT_STATE_MODULE,
+};
+
+/* More information in addition to ETHTOOL_LINK_EXT_STATE_AUTONEG. */
+enum ethtool_link_ext_substate_autoneg {
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE,
+ ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD,
+};
+
+/* More information in addition to ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE.
+ */
+enum ethtool_link_ext_substate_link_training {
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY,
+ ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT,
+};
+
+/* More information in addition to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH.
+ */
+enum ethtool_link_ext_substate_link_logical_mismatch {
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED,
+ ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED,
+};
+
+/* More information in addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY.
+ */
+enum ethtool_link_ext_substate_bad_signal_integrity {
+ ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE,
+ ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_REFERENCE_CLOCK_LOST,
+ ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_ALOS,
+};
+
+/* More information in addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE. */
+enum ethtool_link_ext_substate_cable_issue {
+ ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1,
+ ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE,
+};
+
+/* More information in addition to ETHTOOL_LINK_EXT_STATE_MODULE. */
+enum ethtool_link_ext_substate_module {
+ ETHTOOL_LINK_EXT_SUBSTATE_MODULE_CMIS_NOT_READY = 1,
+};
+
+#define ETH_GSTRING_LEN 32
+
+/**
+ * enum ethtool_stringset - string set ID
+ * @ETH_SS_TEST: Self-test result names, for use with %ETHTOOL_TEST
+ * @ETH_SS_STATS: Statistic names, for use with %ETHTOOL_GSTATS
+ * @ETH_SS_PRIV_FLAGS: Driver private flag names, for use with
+ * %ETHTOOL_GPFLAGS and %ETHTOOL_SPFLAGS
+ * @ETH_SS_NTUPLE_FILTERS: Previously used with %ETHTOOL_GRXNTUPLE;
+ * now deprecated
+ * @ETH_SS_FEATURES: Device feature names
+ * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names
+ * @ETH_SS_TUNABLES: tunable names
+ * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS
+ * @ETH_SS_PHY_TUNABLES: PHY tunable names
+ * @ETH_SS_LINK_MODES: link mode names
+ * @ETH_SS_MSG_CLASSES: debug message class names
+ * @ETH_SS_WOL_MODES: wake-on-lan modes
+ * @ETH_SS_SOF_TIMESTAMPING: SOF_TIMESTAMPING_* flags
+ * @ETH_SS_TS_TX_TYPES: timestamping Tx types
+ * @ETH_SS_TS_RX_FILTERS: timestamping Rx filters
+ * @ETH_SS_UDP_TUNNEL_TYPES: UDP tunnel types
+ * @ETH_SS_STATS_STD: standardized stats
+ * @ETH_SS_STATS_ETH_PHY: names of IEEE 802.3 PHY statistics
+ * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics
+ * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics
+ * @ETH_SS_STATS_RMON: names of RMON statistics
+ *
+ * @ETH_SS_COUNT: number of defined string sets
+ */
+enum ethtool_stringset {
+ ETH_SS_TEST = 0,
+ ETH_SS_STATS,
+ ETH_SS_PRIV_FLAGS,
+ ETH_SS_NTUPLE_FILTERS,
+ ETH_SS_FEATURES,
+ ETH_SS_RSS_HASH_FUNCS,
+ ETH_SS_TUNABLES,
+ ETH_SS_PHY_STATS,
+ ETH_SS_PHY_TUNABLES,
+ ETH_SS_LINK_MODES,
+ ETH_SS_MSG_CLASSES,
+ ETH_SS_WOL_MODES,
+ ETH_SS_SOF_TIMESTAMPING,
+ ETH_SS_TS_TX_TYPES,
+ ETH_SS_TS_RX_FILTERS,
+ ETH_SS_UDP_TUNNEL_TYPES,
+ ETH_SS_STATS_STD,
+ ETH_SS_STATS_ETH_PHY,
+ ETH_SS_STATS_ETH_MAC,
+ ETH_SS_STATS_ETH_CTRL,
+ ETH_SS_STATS_RMON,
+
+ /* add new constants above here */
+ ETH_SS_COUNT
+};
+
+/**
+ * enum ethtool_mac_stats_src - source of ethtool MAC statistics
+ * @ETHTOOL_MAC_STATS_SRC_AGGREGATE:
+ * if device supports a MAC merge layer, this retrieves the aggregate
+ * statistics of the eMAC and pMAC. Otherwise, it retrieves just the
+ * statistics of the single (express) MAC.
+ * @ETHTOOL_MAC_STATS_SRC_EMAC:
+ * if device supports a MM layer, this retrieves the eMAC statistics.
+ * Otherwise, it retrieves the statistics of the single (express) MAC.
+ * @ETHTOOL_MAC_STATS_SRC_PMAC:
+ * if device supports a MM layer, this retrieves the pMAC statistics.
+ */
+enum ethtool_mac_stats_src {
+ ETHTOOL_MAC_STATS_SRC_AGGREGATE,
+ ETHTOOL_MAC_STATS_SRC_EMAC,
+ ETHTOOL_MAC_STATS_SRC_PMAC,
+};
+
+/**
+ * enum ethtool_module_power_mode_policy - plug-in module power mode policy
+ * @ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH: Module is always in high power mode.
+ * @ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO: Module is transitioned by the host
+ * to high power mode when the first port using it is put administratively
+ * up and to low power mode when the last port using it is put
+ * administratively down.
+ */
+enum ethtool_module_power_mode_policy {
+ ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH = 1,
+ ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO,
+};
+
+/**
+ * enum ethtool_module_power_mode - plug-in module power mode
+ * @ETHTOOL_MODULE_POWER_MODE_LOW: Module is in low power mode.
+ * @ETHTOOL_MODULE_POWER_MODE_HIGH: Module is in high power mode.
+ */
+enum ethtool_module_power_mode {
+ ETHTOOL_MODULE_POWER_MODE_LOW = 1,
+ ETHTOOL_MODULE_POWER_MODE_HIGH,
+};
+
+/**
+ * enum ethtool_podl_pse_admin_state - operational state of the PoDL PSE
+ * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState
+ * @ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN: state of PoDL PSE functions are
+ * unknown
+ * @ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: PoDL PSE functions are disabled
+ * @ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: PoDL PSE functions are enabled
+ */
+enum ethtool_podl_pse_admin_state {
+ ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN = 1,
+ ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED,
+ ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED,
+};
+
+/**
+ * enum ethtool_podl_pse_pw_d_status - power detection status of the PoDL PSE.
+ * IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus:
+ * @ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN: PoDL PSE
+ * @ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED: "The enumeration “disabled” is
+ * asserted true when the PoDL PSE state diagram variable mr_pse_enable is
+ * false"
+ * @ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING: "The enumeration “searching” is
+ * asserted true when either of the PSE state diagram variables
+ * pi_detecting or pi_classifying is true."
+ * @ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING: "The enumeration “deliveringPower”
+ * is asserted true when the PoDL PSE state diagram variable pi_powered is
+ * true."
+ * @ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP: "The enumeration “sleep” is asserted
+ * true when the PoDL PSE state diagram variable pi_sleeping is true."
+ * @ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE: "The enumeration “idle” is asserted true
+ * when the logical combination of the PoDL PSE state diagram variables
+ * pi_prebiased*!pi_sleeping is true."
+ * @ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR: "The enumeration “error” is asserted
+ * true when the PoDL PSE state diagram variable overload_held is true."
+ */
+enum ethtool_podl_pse_pw_d_status {
+ ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN = 1,
+ ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED,
+ ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING,
+ ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING,
+ ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP,
+ ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE,
+ ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR,
+};
+
+/**
+ * enum ethtool_mm_verify_status - status of MAC Merge Verify function
+ * @ETHTOOL_MM_VERIFY_STATUS_UNKNOWN:
+ * verification status is unknown
+ * @ETHTOOL_MM_VERIFY_STATUS_INITIAL:
+ * the 802.3 Verify State diagram is in the state INIT_VERIFICATION
+ * @ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
+ * the Verify State diagram is in the state VERIFICATION_IDLE,
+ * SEND_VERIFY or WAIT_FOR_RESPONSE
+ * @ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
+ * indicates that the Verify State diagram is in the state VERIFIED
+ * @ETHTOOL_MM_VERIFY_STATUS_FAILED:
+ * the Verify State diagram is in the state VERIFY_FAIL
+ * @ETHTOOL_MM_VERIFY_STATUS_DISABLED:
+ * verification of preemption operation is disabled
+ */
+enum ethtool_mm_verify_status {
+ ETHTOOL_MM_VERIFY_STATUS_UNKNOWN,
+ ETHTOOL_MM_VERIFY_STATUS_INITIAL,
+ ETHTOOL_MM_VERIFY_STATUS_VERIFYING,
+ ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED,
+ ETHTOOL_MM_VERIFY_STATUS_FAILED,
+ ETHTOOL_MM_VERIFY_STATUS_DISABLED,
+};
+
+/**
+ * struct ethtool_gstrings - string set for data tagging
+ * @cmd: Command number = %ETHTOOL_GSTRINGS
+ * @string_set: String set ID; one of &enum ethtool_stringset
+ * @len: On return, the number of strings in the string set
+ * @data: Buffer for strings. Each string is null-padded to a size of
+ * %ETH_GSTRING_LEN.
+ *
+ * Users must use %ETHTOOL_GSSET_INFO to find the number of strings in
+ * the string set. They must allocate a buffer of the appropriate
+ * size immediately following this structure.
+ */
+struct ethtool_gstrings {
+ __u32 cmd;
+ __u32 string_set;
+ __u32 len;
+ __u8 data[];
+};
+
+/**
+ * struct ethtool_sset_info - string set information
+ * @cmd: Command number = %ETHTOOL_GSSET_INFO
+ * @reserved: Reserved for future use; see the note on reserved space.
+ * @sset_mask: On entry, a bitmask of string sets to query, with bits
+ * numbered according to &enum ethtool_stringset. On return, a
+ * bitmask of those string sets queried that are supported.
+ * @data: Buffer for string set sizes. On return, this contains the
+ * size of each string set that was queried and supported, in
+ * order of ID.
+ *
+ * Example: The user passes in @sset_mask = 0x7 (sets 0, 1, 2) and on
+ * return @sset_mask == 0x6 (sets 1, 2). Then @data[0] contains the
+ * size of set 1 and @data[1] contains the size of set 2.
+ *
+ * Users must allocate a buffer of the appropriate size (4 * number of
+ * sets queried) immediately following this structure.
+ */
+struct ethtool_sset_info {
+ __u32 cmd;
+ __u32 reserved;
+ __u64 sset_mask;
+ __u32 data[];
+};
+
+/**
+ * enum ethtool_test_flags - flags definition of ethtool_test
+ * @ETH_TEST_FL_OFFLINE: if set perform online and offline tests, otherwise
+ * only online tests.
+ * @ETH_TEST_FL_FAILED: Driver set this flag if test fails.
+ * @ETH_TEST_FL_EXTERNAL_LB: Application request to perform external loopback
+ * test.
+ * @ETH_TEST_FL_EXTERNAL_LB_DONE: Driver performed the external loopback test
+ */
+
+enum ethtool_test_flags {
+ ETH_TEST_FL_OFFLINE = (1 << 0),
+ ETH_TEST_FL_FAILED = (1 << 1),
+ ETH_TEST_FL_EXTERNAL_LB = (1 << 2),
+ ETH_TEST_FL_EXTERNAL_LB_DONE = (1 << 3),
+};
+
+/**
+ * struct ethtool_test - device self-test invocation
+ * @cmd: Command number = %ETHTOOL_TEST
+ * @flags: A bitmask of flags from &enum ethtool_test_flags. Some
+ * flags may be set by the user on entry; others may be set by
+ * the driver on return.
+ * @reserved: Reserved for future use; see the note on reserved space.
+ * @len: On return, the number of test results
+ * @data: Array of test results
+ *
+ * Users must use %ETHTOOL_GSSET_INFO or %ETHTOOL_GDRVINFO to find the
+ * number of test results that will be returned. They must allocate a
+ * buffer of the appropriate size (8 * number of results) immediately
+ * following this structure.
+ */
+struct ethtool_test {
+ __u32 cmd;
+ __u32 flags;
+ __u32 reserved;
+ __u32 len;
+ __u64 data[];
+};
+
+/**
+ * struct ethtool_stats - device-specific statistics
+ * @cmd: Command number = %ETHTOOL_GSTATS
+ * @n_stats: On return, the number of statistics
+ * @data: Array of statistics
+ *
+ * Users must use %ETHTOOL_GSSET_INFO or %ETHTOOL_GDRVINFO to find the
+ * number of statistics that will be returned. They must allocate a
+ * buffer of the appropriate size (8 * number of statistics)
+ * immediately following this structure.
+ */
+struct ethtool_stats {
+ __u32 cmd;
+ __u32 n_stats;
+ __u64 data[];
+};
+
+/**
+ * struct ethtool_perm_addr - permanent hardware address
+ * @cmd: Command number = %ETHTOOL_GPERMADDR
+ * @size: On entry, the size of the buffer. On return, the size of the
+ * address. The command fails if the buffer is too small.
+ * @data: Buffer for the address
+ *
+ * Users must allocate the buffer immediately following this structure.
+ * A buffer size of %MAX_ADDR_LEN should be sufficient for any address
+ * type.
+ */
+struct ethtool_perm_addr {
+ __u32 cmd;
+ __u32 size;
+ __u8 data[];
+};
+
+/* boolean flags controlling per-interface behavior characteristics.
+ * When reading, the flag indicates whether or not a certain behavior
+ * is enabled/present. When writing, the flag indicates whether
+ * or not the driver should turn on (set) or off (clear) a behavior.
+ *
+ * Some behaviors may read-only (unconditionally absent or present).
+ * If such is the case, return EINVAL in the set-flags operation if the
+ * flag differs from the read-only value.
+ */
+enum ethtool_flags {
+ ETH_FLAG_TXVLAN = (1 << 7), /* TX VLAN offload enabled */
+ ETH_FLAG_RXVLAN = (1 << 8), /* RX VLAN offload enabled */
+ ETH_FLAG_LRO = (1 << 15), /* LRO is enabled */
+ ETH_FLAG_NTUPLE = (1 << 27), /* N-tuple filters enabled */
+ ETH_FLAG_RXHASH = (1 << 28),
+};
+
+/* The following structures are for supporting RX network flow
+ * classification and RX n-tuple configuration. Note, all multibyte
+ * fields, e.g., ip4src, ip4dst, psrc, pdst, spi, etc. are expected to
+ * be in network byte order.
+ */
+
+/**
+ * struct ethtool_tcpip4_spec - flow specification for TCP/IPv4 etc.
+ * @ip4src: Source host
+ * @ip4dst: Destination host
+ * @psrc: Source port
+ * @pdst: Destination port
+ * @tos: Type-of-service
+ *
+ * This can be used to specify a TCP/IPv4, UDP/IPv4 or SCTP/IPv4 flow.
+ */
+struct ethtool_tcpip4_spec {
+ __be32 ip4src;
+ __be32 ip4dst;
+ __be16 psrc;
+ __be16 pdst;
+ __u8 tos;
+};
+
+/**
+ * struct ethtool_ah_espip4_spec - flow specification for IPsec/IPv4
+ * @ip4src: Source host
+ * @ip4dst: Destination host
+ * @spi: Security parameters index
+ * @tos: Type-of-service
+ *
+ * This can be used to specify an IPsec transport or tunnel over IPv4.
+ */
+struct ethtool_ah_espip4_spec {
+ __be32 ip4src;
+ __be32 ip4dst;
+ __be32 spi;
+ __u8 tos;
+};
+
+#define ETH_RX_NFC_IP4 1
+
+/**
+ * struct ethtool_usrip4_spec - general flow specification for IPv4
+ * @ip4src: Source host
+ * @ip4dst: Destination host
+ * @l4_4_bytes: First 4 bytes of transport (layer 4) header
+ * @tos: Type-of-service
+ * @ip_ver: Value must be %ETH_RX_NFC_IP4; mask must be 0
+ * @proto: Transport protocol number; mask must be 0
+ */
+struct ethtool_usrip4_spec {
+ __be32 ip4src;
+ __be32 ip4dst;
+ __be32 l4_4_bytes;
+ __u8 tos;
+ __u8 ip_ver;
+ __u8 proto;
+};
+
+/**
+ * struct ethtool_tcpip6_spec - flow specification for TCP/IPv6 etc.
+ * @ip6src: Source host
+ * @ip6dst: Destination host
+ * @psrc: Source port
+ * @pdst: Destination port
+ * @tclass: Traffic Class
+ *
+ * This can be used to specify a TCP/IPv6, UDP/IPv6 or SCTP/IPv6 flow.
+ */
+struct ethtool_tcpip6_spec {
+ __be32 ip6src[4];
+ __be32 ip6dst[4];
+ __be16 psrc;
+ __be16 pdst;
+ __u8 tclass;
+};
+
+/**
+ * struct ethtool_ah_espip6_spec - flow specification for IPsec/IPv6
+ * @ip6src: Source host
+ * @ip6dst: Destination host
+ * @spi: Security parameters index
+ * @tclass: Traffic Class
+ *
+ * This can be used to specify an IPsec transport or tunnel over IPv6.
+ */
+struct ethtool_ah_espip6_spec {
+ __be32 ip6src[4];
+ __be32 ip6dst[4];
+ __be32 spi;
+ __u8 tclass;
+};
+
+/**
+ * struct ethtool_usrip6_spec - general flow specification for IPv6
+ * @ip6src: Source host
+ * @ip6dst: Destination host
+ * @l4_4_bytes: First 4 bytes of transport (layer 4) header
+ * @tclass: Traffic Class
+ * @l4_proto: Transport protocol number (nexthdr after any Extension Headers)
+ */
+struct ethtool_usrip6_spec {
+ __be32 ip6src[4];
+ __be32 ip6dst[4];
+ __be32 l4_4_bytes;
+ __u8 tclass;
+ __u8 l4_proto;
+};
+
+union ethtool_flow_union {
+ struct ethtool_tcpip4_spec tcp_ip4_spec;
+ struct ethtool_tcpip4_spec udp_ip4_spec;
+ struct ethtool_tcpip4_spec sctp_ip4_spec;
+ struct ethtool_ah_espip4_spec ah_ip4_spec;
+ struct ethtool_ah_espip4_spec esp_ip4_spec;
+ struct ethtool_usrip4_spec usr_ip4_spec;
+ struct ethtool_tcpip6_spec tcp_ip6_spec;
+ struct ethtool_tcpip6_spec udp_ip6_spec;
+ struct ethtool_tcpip6_spec sctp_ip6_spec;
+ struct ethtool_ah_espip6_spec ah_ip6_spec;
+ struct ethtool_ah_espip6_spec esp_ip6_spec;
+ struct ethtool_usrip6_spec usr_ip6_spec;
+ struct ethhdr ether_spec;
+ __u8 hdata[52];
+};
+
+/**
+ * struct ethtool_flow_ext - additional RX flow fields
+ * @h_dest: destination MAC address
+ * @vlan_etype: VLAN EtherType
+ * @vlan_tci: VLAN tag control information
+ * @data: user defined data
+ * @padding: Reserved for future use; see the note on reserved space.
+ *
+ * Note, @vlan_etype, @vlan_tci, and @data are only valid if %FLOW_EXT
+ * is set in &struct ethtool_rx_flow_spec @flow_type.
+ * @h_dest is valid if %FLOW_MAC_EXT is set.
+ */
+struct ethtool_flow_ext {
+ __u8 padding[2];
+ unsigned char h_dest[ETH_ALEN];
+ __be16 vlan_etype;
+ __be16 vlan_tci;
+ __be32 data[2];
+};
+
+/**
+ * struct ethtool_rx_flow_spec - classification rule for RX flows
+ * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW
+ * @h_u: Flow fields to match (dependent on @flow_type)
+ * @h_ext: Additional fields to match
+ * @m_u: Masks for flow field bits to be matched
+ * @m_ext: Masks for additional field bits to be matched
+ * Note, all additional fields must be ignored unless @flow_type
+ * includes the %FLOW_EXT or %FLOW_MAC_EXT flag
+ * (see &struct ethtool_flow_ext description).
+ * @ring_cookie: RX ring/queue index to deliver to, or %RX_CLS_FLOW_DISC
+ * if packets should be discarded, or %RX_CLS_FLOW_WAKE if the
+ * packets should be used for Wake-on-LAN with %WAKE_FILTER
+ * @location: Location of rule in the table. Locations must be
+ * numbered such that a flow matching multiple rules will be
+ * classified according to the first (lowest numbered) rule.
+ */
+struct ethtool_rx_flow_spec {
+ __u32 flow_type;
+ union ethtool_flow_union h_u;
+ struct ethtool_flow_ext h_ext;
+ union ethtool_flow_union m_u;
+ struct ethtool_flow_ext m_ext;
+ __u64 ring_cookie;
+ __u32 location;
+};
+
+/* How rings are laid out when accessing virtual functions or
+ * offloaded queues is device specific. To allow users to do flow
+ * steering and specify these queues the ring cookie is partitioned
+ * into a 32bit queue index with an 8 bit virtual function id.
+ * This also leaves the 3bytes for further specifiers. It is possible
+ * future devices may support more than 256 virtual functions if
+ * devices start supporting PCIe w/ARI. However at the moment I
+ * do not know of any devices that support this so I do not reserve
+ * space for this at this time. If a future patch consumes the next
+ * byte it should be aware of this possibility.
+ */
+#define ETHTOOL_RX_FLOW_SPEC_RING 0x00000000FFFFFFFFLL
+#define ETHTOOL_RX_FLOW_SPEC_RING_VF 0x000000FF00000000LL
+#define ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF 32
+static __inline__ __u64 ethtool_get_flow_spec_ring(__u64 ring_cookie)
+{
+ return ETHTOOL_RX_FLOW_SPEC_RING & ring_cookie;
+}
+
+static __inline__ __u64 ethtool_get_flow_spec_ring_vf(__u64 ring_cookie)
+{
+ return (ETHTOOL_RX_FLOW_SPEC_RING_VF & ring_cookie) >>
+ ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF;
+}
+
+/**
+ * struct ethtool_rxnfc - command to get or set RX flow classification rules
+ * @cmd: Specific command number - %ETHTOOL_GRXFH, %ETHTOOL_SRXFH,
+ * %ETHTOOL_GRXRINGS, %ETHTOOL_GRXCLSRLCNT, %ETHTOOL_GRXCLSRULE,
+ * %ETHTOOL_GRXCLSRLALL, %ETHTOOL_SRXCLSRLDEL or %ETHTOOL_SRXCLSRLINS
+ * @flow_type: Type of flow to be affected, e.g. %TCP_V4_FLOW
+ * @data: Command-dependent value
+ * @fs: Flow classification rule
+ * @rss_context: RSS context to be affected
+ * @rule_cnt: Number of rules to be affected
+ * @rule_locs: Array of used rule locations
+ *
+ * For %ETHTOOL_GRXFH and %ETHTOOL_SRXFH, @data is a bitmask indicating
+ * the fields included in the flow hash, e.g. %RXH_IP_SRC. The following
+ * structure fields must not be used, except that if @flow_type includes
+ * the %FLOW_RSS flag, then @rss_context determines which RSS context to
+ * act on.
+ *
+ * For %ETHTOOL_GRXRINGS, @data is set to the number of RX rings/queues
+ * on return.
+ *
+ * For %ETHTOOL_GRXCLSRLCNT, @rule_cnt is set to the number of defined
+ * rules on return. If @data is non-zero on return then it is the
+ * size of the rule table, plus the flag %RX_CLS_LOC_SPECIAL if the
+ * driver supports any special location values. If that flag is not
+ * set in @data then special location values should not be used.
+ *
+ * For %ETHTOOL_GRXCLSRULE, @fs.@location specifies the location of an
+ * existing rule on entry and @fs contains the rule on return; if
+ * @fs.@flow_type includes the %FLOW_RSS flag, then @rss_context is
+ * filled with the RSS context ID associated with the rule.
+ *
+ * For %ETHTOOL_GRXCLSRLALL, @rule_cnt specifies the array size of the
+ * user buffer for @rule_locs on entry. On return, @data is the size
+ * of the rule table, @rule_cnt is the number of defined rules, and
+ * @rule_locs contains the locations of the defined rules. Drivers
+ * must use the second parameter to get_rxnfc() instead of @rule_locs.
+ *
+ * For %ETHTOOL_SRXCLSRLINS, @fs specifies the rule to add or update.
+ * @fs.@location either specifies the location to use or is a special
+ * location value with %RX_CLS_LOC_SPECIAL flag set. On return,
+ * @fs.@location is the actual rule location. If @fs.@flow_type
+ * includes the %FLOW_RSS flag, @rss_context is the RSS context ID to
+ * use for flow spreading traffic which matches this rule. The value
+ * from the rxfh indirection table will be added to @fs.@ring_cookie
+ * to choose which ring to deliver to.
+ *
+ * For %ETHTOOL_SRXCLSRLDEL, @fs.@location specifies the location of an
+ * existing rule on entry.
+ *
+ * A driver supporting the special location values for
+ * %ETHTOOL_SRXCLSRLINS may add the rule at any suitable unused
+ * location, and may remove a rule at a later location (lower
+ * priority) that matches exactly the same set of flows. The special
+ * values are %RX_CLS_LOC_ANY, selecting any location;
+ * %RX_CLS_LOC_FIRST, selecting the first suitable location (maximum
+ * priority); and %RX_CLS_LOC_LAST, selecting the last suitable
+ * location (minimum priority). Additional special values may be
+ * defined in future and drivers must return -%EINVAL for any
+ * unrecognised value.
+ */
+struct ethtool_rxnfc {
+ __u32 cmd;
+ __u32 flow_type;
+ __u64 data;
+ struct ethtool_rx_flow_spec fs;
+ union {
+ __u32 rule_cnt;
+ __u32 rss_context;
+ };
+ __u32 rule_locs[];
+};
+
+
+/**
+ * struct ethtool_rxfh_indir - command to get or set RX flow hash indirection
+ * @cmd: Specific command number - %ETHTOOL_GRXFHINDIR or %ETHTOOL_SRXFHINDIR
+ * @size: On entry, the array size of the user buffer, which may be zero.
+ * On return from %ETHTOOL_GRXFHINDIR, the array size of the hardware
+ * indirection table.
+ * @ring_index: RX ring/queue index for each hash value
+ *
+ * For %ETHTOOL_GRXFHINDIR, a @size of zero means that only the size
+ * should be returned. For %ETHTOOL_SRXFHINDIR, a @size of zero means
+ * the table should be reset to default values. This last feature
+ * is not supported by the original implementations.
+ */
+struct ethtool_rxfh_indir {
+ __u32 cmd;
+ __u32 size;
+ __u32 ring_index[];
+};
+
+/**
+ * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key.
+ * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH
+ * @rss_context: RSS context identifier. Context 0 is the default for normal
+ * traffic; other contexts can be referenced as the destination for RX flow
+ * classification rules. %ETH_RXFH_CONTEXT_ALLOC is used with command
+ * %ETHTOOL_SRSSH to allocate a new RSS context; on return this field will
+ * contain the ID of the newly allocated context.
+ * @indir_size: On entry, the array size of the user buffer for the
+ * indirection table, which may be zero, or (for %ETHTOOL_SRSSH),
+ * %ETH_RXFH_INDIR_NO_CHANGE. On return from %ETHTOOL_GRSSH,
+ * the array size of the hardware indirection table.
+ * @key_size: On entry, the array size of the user buffer for the hash key,
+ * which may be zero. On return from %ETHTOOL_GRSSH, the size of the
+ * hardware hash key.
+ * @hfunc: Defines the current RSS hash function used by HW (or to be set to).
+ * Valid values are one of the %ETH_RSS_HASH_*.
+ * @rsvd8: Reserved for future use; see the note on reserved space.
+ * @rsvd32: Reserved for future use; see the note on reserved space.
+ * @rss_config: RX ring/queue index for each hash value i.e., indirection table
+ * of @indir_size __u32 elements, followed by hash key of @key_size
+ * bytes.
+ *
+ * For %ETHTOOL_GRSSH, a @indir_size and key_size of zero means that only the
+ * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of
+ * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested
+ * and a @indir_size of zero means the indir table should be reset to default
+ * values (if @rss_context == 0) or that the RSS context should be deleted.
+ * An hfunc of zero means that hash function setting is not requested.
+ */
+struct ethtool_rxfh {
+ __u32 cmd;
+ __u32 rss_context;
+ __u32 indir_size;
+ __u32 key_size;
+ __u8 hfunc;
+ __u8 rsvd8[3];
+ __u32 rsvd32;
+ __u32 rss_config[];
+};
+#define ETH_RXFH_CONTEXT_ALLOC 0xffffffff
+#define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff
+
+/**
+ * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter
+ * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW
+ * @h_u: Flow field values to match (dependent on @flow_type)
+ * @m_u: Masks for flow field value bits to be ignored
+ * @vlan_tag: VLAN tag to match
+ * @vlan_tag_mask: Mask for VLAN tag bits to be ignored
+ * @data: Driver-dependent data to match
+ * @data_mask: Mask for driver-dependent data bits to be ignored
+ * @action: RX ring/queue index to deliver to (non-negative) or other action
+ * (negative, e.g. %ETHTOOL_RXNTUPLE_ACTION_DROP)
+ *
+ * For flow types %TCP_V4_FLOW, %UDP_V4_FLOW and %SCTP_V4_FLOW, where
+ * a field value and mask are both zero this is treated as if all mask
+ * bits are set i.e. the field is ignored.
+ */
+struct ethtool_rx_ntuple_flow_spec {
+ __u32 flow_type;
+ union {
+ struct ethtool_tcpip4_spec tcp_ip4_spec;
+ struct ethtool_tcpip4_spec udp_ip4_spec;
+ struct ethtool_tcpip4_spec sctp_ip4_spec;
+ struct ethtool_ah_espip4_spec ah_ip4_spec;
+ struct ethtool_ah_espip4_spec esp_ip4_spec;
+ struct ethtool_usrip4_spec usr_ip4_spec;
+ struct ethhdr ether_spec;
+ __u8 hdata[72];
+ } h_u, m_u;
+
+ __u16 vlan_tag;
+ __u16 vlan_tag_mask;
+ __u64 data;
+ __u64 data_mask;
+
+ __s32 action;
+#define ETHTOOL_RXNTUPLE_ACTION_DROP (-1) /* drop packet */
+#define ETHTOOL_RXNTUPLE_ACTION_CLEAR (-2) /* clear filter */
+};
+
+/**
+ * struct ethtool_rx_ntuple - command to set or clear RX flow filter
+ * @cmd: Command number - %ETHTOOL_SRXNTUPLE
+ * @fs: Flow filter specification
+ */
+struct ethtool_rx_ntuple {
+ __u32 cmd;
+ struct ethtool_rx_ntuple_flow_spec fs;
+};
+
+#define ETHTOOL_FLASH_MAX_FILENAME 128
+enum ethtool_flash_op_type {
+ ETHTOOL_FLASH_ALL_REGIONS = 0,
+};
+
+/* for passing firmware flashing related parameters */
+struct ethtool_flash {
+ __u32 cmd;
+ __u32 region;
+ char data[ETHTOOL_FLASH_MAX_FILENAME];
+};
+
+/**
+ * struct ethtool_dump - used for retrieving, setting device dump
+ * @cmd: Command number - %ETHTOOL_GET_DUMP_FLAG, %ETHTOOL_GET_DUMP_DATA, or
+ * %ETHTOOL_SET_DUMP
+ * @version: FW version of the dump, filled in by driver
+ * @flag: driver dependent flag for dump setting, filled in by driver during
+ * get and filled in by ethtool for set operation.
+ * flag must be initialized by macro ETH_FW_DUMP_DISABLE value when
+ * firmware dump is disabled.
+ * @len: length of dump data, used as the length of the user buffer on entry to
+ * %ETHTOOL_GET_DUMP_DATA and this is returned as dump length by driver
+ * for %ETHTOOL_GET_DUMP_FLAG command
+ * @data: data collected for get dump data operation
+ */
+struct ethtool_dump {
+ __u32 cmd;
+ __u32 version;
+ __u32 flag;
+ __u32 len;
+ __u8 data[];
+};
+
+#define ETH_FW_DUMP_DISABLE 0
+
+/* for returning and changing feature sets */
+
+/**
+ * struct ethtool_get_features_block - block with state of 32 features
+ * @available: mask of changeable features
+ * @requested: mask of features requested to be enabled if possible
+ * @active: mask of currently enabled features
+ * @never_changed: mask of features not changeable for any device
+ */
+struct ethtool_get_features_block {
+ __u32 available;
+ __u32 requested;
+ __u32 active;
+ __u32 never_changed;
+};
+
+/**
+ * struct ethtool_gfeatures - command to get state of device's features
+ * @cmd: command number = %ETHTOOL_GFEATURES
+ * @size: On entry, the number of elements in the features[] array;
+ * on return, the number of elements in features[] needed to hold
+ * all features
+ * @features: state of features
+ */
+struct ethtool_gfeatures {
+ __u32 cmd;
+ __u32 size;
+ struct ethtool_get_features_block features[];
+};
+
+/**
+ * struct ethtool_set_features_block - block with request for 32 features
+ * @valid: mask of features to be changed
+ * @requested: values of features to be changed
+ */
+struct ethtool_set_features_block {
+ __u32 valid;
+ __u32 requested;
+};
+
+/**
+ * struct ethtool_sfeatures - command to request change in device's features
+ * @cmd: command number = %ETHTOOL_SFEATURES
+ * @size: array size of the features[] array
+ * @features: feature change masks
+ */
+struct ethtool_sfeatures {
+ __u32 cmd;
+ __u32 size;
+ struct ethtool_set_features_block features[];
+};
+
+/**
+ * struct ethtool_ts_info - holds a device's timestamping and PHC association
+ * @cmd: command number = %ETHTOOL_GET_TS_INFO
+ * @so_timestamping: bit mask of the sum of the supported SO_TIMESTAMPING flags
+ * @phc_index: device index of the associated PHC, or -1 if there is none
+ * @tx_types: bit mask of the supported hwtstamp_tx_types enumeration values
+ * @tx_reserved: Reserved for future use; see the note on reserved space.
+ * @rx_filters: bit mask of the supported hwtstamp_rx_filters enumeration values
+ * @rx_reserved: Reserved for future use; see the note on reserved space.
+ *
+ * The bits in the 'tx_types' and 'rx_filters' fields correspond to
+ * the 'hwtstamp_tx_types' and 'hwtstamp_rx_filters' enumeration values,
+ * respectively. For example, if the device supports HWTSTAMP_TX_ON,
+ * then (1 << HWTSTAMP_TX_ON) in 'tx_types' will be set.
+ *
+ * Drivers should only report the filters they actually support without
+ * upscaling in the SIOCSHWTSTAMP ioctl. If the SIOCSHWSTAMP request for
+ * HWTSTAMP_FILTER_V1_SYNC is supported by HWTSTAMP_FILTER_V1_EVENT, then the
+ * driver should only report HWTSTAMP_FILTER_V1_EVENT in this op.
+ */
+struct ethtool_ts_info {
+ __u32 cmd;
+ __u32 so_timestamping;
+ __s32 phc_index;
+ __u32 tx_types;
+ __u32 tx_reserved[3];
+ __u32 rx_filters;
+ __u32 rx_reserved[3];
+};
+
+/*
+ * %ETHTOOL_SFEATURES changes features present in features[].valid to the
+ * values of corresponding bits in features[].requested. Bits in .requested
+ * not set in .valid or not changeable are ignored.
+ *
+ * Returns %EINVAL when .valid contains undefined or never-changeable bits
+ * or size is not equal to required number of features words (32-bit blocks).
+ * Returns >= 0 if request was completed; bits set in the value mean:
+ * %ETHTOOL_F_UNSUPPORTED - there were bits set in .valid that are not
+ * changeable (not present in %ETHTOOL_GFEATURES' features[].available)
+ * those bits were ignored.
+ * %ETHTOOL_F_WISH - some or all changes requested were recorded but the
+ * resulting state of bits masked by .valid is not equal to .requested.
+ * Probably there are other device-specific constraints on some features
+ * in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered
+ * here as though ignored bits were cleared.
+ * %ETHTOOL_F_COMPAT - some or all changes requested were made by calling
+ * compatibility functions. Requested offload state cannot be properly
+ * managed by kernel.
+ *
+ * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of
+ * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands
+ * for ETH_SS_FEATURES string set. First entry in the table corresponds to least
+ * significant bit in features[0] fields. Empty strings mark undefined features.
+ */
+enum ethtool_sfeatures_retval_bits {
+ ETHTOOL_F_UNSUPPORTED__BIT,
+ ETHTOOL_F_WISH__BIT,
+ ETHTOOL_F_COMPAT__BIT,
+};
+
+#define ETHTOOL_F_UNSUPPORTED (1 << ETHTOOL_F_UNSUPPORTED__BIT)
+#define ETHTOOL_F_WISH (1 << ETHTOOL_F_WISH__BIT)
+#define ETHTOOL_F_COMPAT (1 << ETHTOOL_F_COMPAT__BIT)
+
+#define MAX_NUM_QUEUE 4096
+
+/**
+ * struct ethtool_per_queue_op - apply sub command to the queues in mask.
+ * @cmd: ETHTOOL_PERQUEUE
+ * @sub_command: the sub command which apply to each queues
+ * @queue_mask: Bitmap of the queues which sub command apply to
+ * @data: A complete command structure following for each of the queues addressed
+ */
+struct ethtool_per_queue_op {
+ __u32 cmd;
+ __u32 sub_command;
+ __u32 queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)];
+ char data[];
+};
+
+/**
+ * struct ethtool_fecparam - Ethernet Forward Error Correction parameters
+ * @cmd: Command number = %ETHTOOL_GFECPARAM or %ETHTOOL_SFECPARAM
+ * @active_fec: FEC mode which is active on the port, single bit set, GET only.
+ * @fec: Bitmask of configured FEC modes.
+ * @reserved: Reserved for future extensions, ignore on GET, write 0 for SET.
+ *
+ * Note that @reserved was never validated on input and ethtool user space
+ * left it uninitialized when calling SET. Hence going forward it can only be
+ * used to return a value to userspace with GET.
+ *
+ * FEC modes supported by the device can be read via %ETHTOOL_GLINKSETTINGS.
+ * FEC settings are configured by link autonegotiation whenever it's enabled.
+ * With autoneg on %ETHTOOL_GFECPARAM can be used to read the current mode.
+ *
+ * When autoneg is disabled %ETHTOOL_SFECPARAM controls the FEC settings.
+ * It is recommended that drivers only accept a single bit set in @fec.
+ * When multiple bits are set in @fec drivers may pick mode in an implementation
+ * dependent way. Drivers should reject mixing %ETHTOOL_FEC_AUTO_BIT with other
+ * FEC modes, because it's unclear whether in this case other modes constrain
+ * AUTO or are independent choices.
+ * Drivers must reject SET requests if they support none of the requested modes.
+ *
+ * If device does not support FEC drivers may use %ETHTOOL_FEC_NONE instead
+ * of returning %EOPNOTSUPP from %ETHTOOL_GFECPARAM.
+ *
+ * See enum ethtool_fec_config_bits for definition of valid bits for both
+ * @fec and @active_fec.
+ */
+struct ethtool_fecparam {
+ __u32 cmd;
+ /* bitmask of FEC modes */
+ __u32 active_fec;
+ __u32 fec;
+ __u32 reserved;
+};
+
+/**
+ * enum ethtool_fec_config_bits - flags definition of ethtool_fec_configuration
+ * @ETHTOOL_FEC_NONE_BIT: FEC mode configuration is not supported. Should not
+ * be used together with other bits. GET only.
+ * @ETHTOOL_FEC_AUTO_BIT: Select default/best FEC mode automatically, usually
+ * based link mode and SFP parameters read from module's
+ * EEPROM. This bit does _not_ mean autonegotiation.
+ * @ETHTOOL_FEC_OFF_BIT: No FEC Mode
+ * @ETHTOOL_FEC_RS_BIT: Reed-Solomon FEC Mode
+ * @ETHTOOL_FEC_BASER_BIT: Base-R/Reed-Solomon FEC Mode
+ * @ETHTOOL_FEC_LLRS_BIT: Low Latency Reed Solomon FEC Mode (25G/50G Ethernet
+ * Consortium)
+ */
+enum ethtool_fec_config_bits {
+ ETHTOOL_FEC_NONE_BIT,
+ ETHTOOL_FEC_AUTO_BIT,
+ ETHTOOL_FEC_OFF_BIT,
+ ETHTOOL_FEC_RS_BIT,
+ ETHTOOL_FEC_BASER_BIT,
+ ETHTOOL_FEC_LLRS_BIT,
+};
+
+#define ETHTOOL_FEC_NONE (1 << ETHTOOL_FEC_NONE_BIT)
+#define ETHTOOL_FEC_AUTO (1 << ETHTOOL_FEC_AUTO_BIT)
+#define ETHTOOL_FEC_OFF (1 << ETHTOOL_FEC_OFF_BIT)
+#define ETHTOOL_FEC_RS (1 << ETHTOOL_FEC_RS_BIT)
+#define ETHTOOL_FEC_BASER (1 << ETHTOOL_FEC_BASER_BIT)
+#define ETHTOOL_FEC_LLRS (1 << ETHTOOL_FEC_LLRS_BIT)
+
+/* CMDs currently supported */
+#define ETHTOOL_GSET 0x00000001 /* DEPRECATED, Get settings.
+ * Please use ETHTOOL_GLINKSETTINGS
+ */
+#define ETHTOOL_SSET 0x00000002 /* DEPRECATED, Set settings.
+ * Please use ETHTOOL_SLINKSETTINGS
+ */
+#define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */
+#define ETHTOOL_GREGS 0x00000004 /* Get NIC registers. */
+#define ETHTOOL_GWOL 0x00000005 /* Get wake-on-lan options. */
+#define ETHTOOL_SWOL 0x00000006 /* Set wake-on-lan options. */
+#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */
+#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level. */
+#define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation. */
+/* Get link status for host, i.e. whether the interface *and* the
+ * physical port (if there is one) are up (ethtool_value). */
+#define ETHTOOL_GLINK 0x0000000a
+#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */
+#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data. */
+#define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */
+#define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config. */
+#define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */
+#define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters. */
+#define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */
+#define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters. */
+#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */
+#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */
+#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */
+#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */
+#define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable
+ * (ethtool_value) */
+#define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable
+ * (ethtool_value). */
+#define ETHTOOL_TEST 0x0000001a /* execute NIC self-test. */
+#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */
+#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */
+#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */
+#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */
+#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */
+#define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */
+#define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (ethtool_value) */
+#define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (ethtool_value) */
+#define ETHTOOL_GGSO 0x00000023 /* Get GSO enable (ethtool_value) */
+#define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (ethtool_value) */
+#define ETHTOOL_GFLAGS 0x00000025 /* Get flags bitmap(ethtool_value) */
+#define ETHTOOL_SFLAGS 0x00000026 /* Set flags bitmap(ethtool_value) */
+#define ETHTOOL_GPFLAGS 0x00000027 /* Get driver-private flags bitmap */
+#define ETHTOOL_SPFLAGS 0x00000028 /* Set driver-private flags bitmap */
+
+#define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */
+#define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */
+#define ETHTOOL_GGRO 0x0000002b /* Get GRO enable (ethtool_value) */
+#define ETHTOOL_SGRO 0x0000002c /* Set GRO enable (ethtool_value) */
+#define ETHTOOL_GRXRINGS 0x0000002d /* Get RX rings available for LB */
+#define ETHTOOL_GRXCLSRLCNT 0x0000002e /* Get RX class rule count */
+#define ETHTOOL_GRXCLSRULE 0x0000002f /* Get RX classification rule */
+#define ETHTOOL_GRXCLSRLALL 0x00000030 /* Get all RX classification rule */
+#define ETHTOOL_SRXCLSRLDEL 0x00000031 /* Delete RX classification rule */
+#define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */
+#define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */
+#define ETHTOOL_RESET 0x00000034 /* Reset hardware */
+#define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */
+#define ETHTOOL_GRXNTUPLE 0x00000036 /* deprecated */
+#define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */
+#define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */
+#define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */
+
+#define ETHTOOL_GFEATURES 0x0000003a /* Get device offload settings */
+#define ETHTOOL_SFEATURES 0x0000003b /* Change device offload settings */
+#define ETHTOOL_GCHANNELS 0x0000003c /* Get no of channels */
+#define ETHTOOL_SCHANNELS 0x0000003d /* Set no of channels */
+#define ETHTOOL_SET_DUMP 0x0000003e /* Set dump settings */
+#define ETHTOOL_GET_DUMP_FLAG 0x0000003f /* Get dump settings */
+#define ETHTOOL_GET_DUMP_DATA 0x00000040 /* Get dump data */
+#define ETHTOOL_GET_TS_INFO 0x00000041 /* Get time stamping and PHC info */
+#define ETHTOOL_GMODULEINFO 0x00000042 /* Get plug-in module information */
+#define ETHTOOL_GMODULEEEPROM 0x00000043 /* Get plug-in module eeprom */
+#define ETHTOOL_GEEE 0x00000044 /* Get EEE settings */
+#define ETHTOOL_SEEE 0x00000045 /* Set EEE settings */
+
+#define ETHTOOL_GRSSH 0x00000046 /* Get RX flow hash configuration */
+#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */
+#define ETHTOOL_GTUNABLE 0x00000048 /* Get tunable configuration */
+#define ETHTOOL_STUNABLE 0x00000049 /* Set tunable configuration */
+#define ETHTOOL_GPHYSTATS 0x0000004a /* get PHY-specific statistics */
+
+#define ETHTOOL_PERQUEUE 0x0000004b /* Set per queue options */
+
+#define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */
+#define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */
+#define ETHTOOL_PHY_GTUNABLE 0x0000004e /* Get PHY tunable configuration */
+#define ETHTOOL_PHY_STUNABLE 0x0000004f /* Set PHY tunable configuration */
+#define ETHTOOL_GFECPARAM 0x00000050 /* Get FEC settings */
+#define ETHTOOL_SFECPARAM 0x00000051 /* Set FEC settings */
+
+/* compatibility with older code */
+#define SPARC_ETH_GSET ETHTOOL_GSET
+#define SPARC_ETH_SSET ETHTOOL_SSET
+
+/* Link mode bit indices */
+enum ethtool_link_mode_bit_indices {
+ ETHTOOL_LINK_MODE_10baseT_Half_BIT = 0,
+ ETHTOOL_LINK_MODE_10baseT_Full_BIT = 1,
+ ETHTOOL_LINK_MODE_100baseT_Half_BIT = 2,
+ ETHTOOL_LINK_MODE_100baseT_Full_BIT = 3,
+ ETHTOOL_LINK_MODE_1000baseT_Half_BIT = 4,
+ ETHTOOL_LINK_MODE_1000baseT_Full_BIT = 5,
+ ETHTOOL_LINK_MODE_Autoneg_BIT = 6,
+ ETHTOOL_LINK_MODE_TP_BIT = 7,
+ ETHTOOL_LINK_MODE_AUI_BIT = 8,
+ ETHTOOL_LINK_MODE_MII_BIT = 9,
+ ETHTOOL_LINK_MODE_FIBRE_BIT = 10,
+ ETHTOOL_LINK_MODE_BNC_BIT = 11,
+ ETHTOOL_LINK_MODE_10000baseT_Full_BIT = 12,
+ ETHTOOL_LINK_MODE_Pause_BIT = 13,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT = 14,
+ ETHTOOL_LINK_MODE_2500baseX_Full_BIT = 15,
+ ETHTOOL_LINK_MODE_Backplane_BIT = 16,
+ ETHTOOL_LINK_MODE_1000baseKX_Full_BIT = 17,
+ ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT = 18,
+ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT = 19,
+ ETHTOOL_LINK_MODE_10000baseR_FEC_BIT = 20,
+ ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT = 21,
+ ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT = 22,
+ ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT = 23,
+ ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT = 24,
+ ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT = 25,
+ ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT = 26,
+ ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT = 27,
+ ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT = 28,
+ ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT = 29,
+ ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT = 30,
+ ETHTOOL_LINK_MODE_25000baseCR_Full_BIT = 31,
+
+ /* Last allowed bit for __ETHTOOL_LINK_MODE_LEGACY_MASK is bit
+ * 31. Please do NOT define any SUPPORTED_* or ADVERTISED_*
+ * macro for bits > 31. The only way to use indices > 31 is to
+ * use the new ETHTOOL_GLINKSETTINGS/ETHTOOL_SLINKSETTINGS API.
+ */
+
+ ETHTOOL_LINK_MODE_25000baseKR_Full_BIT = 32,
+ ETHTOOL_LINK_MODE_25000baseSR_Full_BIT = 33,
+ ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT = 34,
+ ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT = 35,
+ ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT = 36,
+ ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT = 37,
+ ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT = 38,
+ ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT = 39,
+ ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT = 40,
+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT = 41,
+ ETHTOOL_LINK_MODE_10000baseCR_Full_BIT = 42,
+ ETHTOOL_LINK_MODE_10000baseSR_Full_BIT = 43,
+ ETHTOOL_LINK_MODE_10000baseLR_Full_BIT = 44,
+ ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT = 45,
+ ETHTOOL_LINK_MODE_10000baseER_Full_BIT = 46,
+ ETHTOOL_LINK_MODE_2500baseT_Full_BIT = 47,
+ ETHTOOL_LINK_MODE_5000baseT_Full_BIT = 48,
+
+ ETHTOOL_LINK_MODE_FEC_NONE_BIT = 49,
+ ETHTOOL_LINK_MODE_FEC_RS_BIT = 50,
+ ETHTOOL_LINK_MODE_FEC_BASER_BIT = 51,
+ ETHTOOL_LINK_MODE_50000baseKR_Full_BIT = 52,
+ ETHTOOL_LINK_MODE_50000baseSR_Full_BIT = 53,
+ ETHTOOL_LINK_MODE_50000baseCR_Full_BIT = 54,
+ ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT = 55,
+ ETHTOOL_LINK_MODE_50000baseDR_Full_BIT = 56,
+ ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT = 57,
+ ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT = 58,
+ ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT = 59,
+ ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT = 60,
+ ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT = 61,
+ ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT = 62,
+ ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT = 63,
+ ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT = 64,
+ ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT = 65,
+ ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66,
+ ETHTOOL_LINK_MODE_100baseT1_Full_BIT = 67,
+ ETHTOOL_LINK_MODE_1000baseT1_Full_BIT = 68,
+ ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT = 69,
+ ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT = 70,
+ ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71,
+ ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72,
+ ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73,
+ ETHTOOL_LINK_MODE_FEC_LLRS_BIT = 74,
+ ETHTOOL_LINK_MODE_100000baseKR_Full_BIT = 75,
+ ETHTOOL_LINK_MODE_100000baseSR_Full_BIT = 76,
+ ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT = 77,
+ ETHTOOL_LINK_MODE_100000baseCR_Full_BIT = 78,
+ ETHTOOL_LINK_MODE_100000baseDR_Full_BIT = 79,
+ ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT = 80,
+ ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT = 81,
+ ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT = 82,
+ ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT = 83,
+ ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT = 84,
+ ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT = 85,
+ ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT = 86,
+ ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT = 87,
+ ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT = 88,
+ ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT = 89,
+ ETHTOOL_LINK_MODE_100baseFX_Half_BIT = 90,
+ ETHTOOL_LINK_MODE_100baseFX_Full_BIT = 91,
+ ETHTOOL_LINK_MODE_10baseT1L_Full_BIT = 92,
+ ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT = 93,
+ ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT = 94,
+ ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT = 95,
+ ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT = 96,
+ ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT = 97,
+ ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT = 98,
+ ETHTOOL_LINK_MODE_10baseT1S_Full_BIT = 99,
+ ETHTOOL_LINK_MODE_10baseT1S_Half_BIT = 100,
+ ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT = 101,
+
+ /* must be last entry */
+ __ETHTOOL_LINK_MODE_MASK_NBITS
+};
+
+#define __ETHTOOL_LINK_MODE_LEGACY_MASK(base_name) \
+ (1UL << (ETHTOOL_LINK_MODE_ ## base_name ## _BIT))
+
+/* DEPRECATED macros. Please migrate to
+ * ETHTOOL_GLINKSETTINGS/ETHTOOL_SLINKSETTINGS API. Please do NOT
+ * define any new SUPPORTED_* macro for bits > 31.
+ */
+#define SUPPORTED_10baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(10baseT_Half)
+#define SUPPORTED_10baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10baseT_Full)
+#define SUPPORTED_100baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(100baseT_Half)
+#define SUPPORTED_100baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(100baseT_Full)
+#define SUPPORTED_1000baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseT_Half)
+#define SUPPORTED_1000baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseT_Full)
+#define SUPPORTED_Autoneg __ETHTOOL_LINK_MODE_LEGACY_MASK(Autoneg)
+#define SUPPORTED_TP __ETHTOOL_LINK_MODE_LEGACY_MASK(TP)
+#define SUPPORTED_AUI __ETHTOOL_LINK_MODE_LEGACY_MASK(AUI)
+#define SUPPORTED_MII __ETHTOOL_LINK_MODE_LEGACY_MASK(MII)
+#define SUPPORTED_FIBRE __ETHTOOL_LINK_MODE_LEGACY_MASK(FIBRE)
+#define SUPPORTED_BNC __ETHTOOL_LINK_MODE_LEGACY_MASK(BNC)
+#define SUPPORTED_10000baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseT_Full)
+#define SUPPORTED_Pause __ETHTOOL_LINK_MODE_LEGACY_MASK(Pause)
+#define SUPPORTED_Asym_Pause __ETHTOOL_LINK_MODE_LEGACY_MASK(Asym_Pause)
+#define SUPPORTED_2500baseX_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(2500baseX_Full)
+#define SUPPORTED_Backplane __ETHTOOL_LINK_MODE_LEGACY_MASK(Backplane)
+#define SUPPORTED_1000baseKX_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseKX_Full)
+#define SUPPORTED_10000baseKX4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseKX4_Full)
+#define SUPPORTED_10000baseKR_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseKR_Full)
+#define SUPPORTED_10000baseR_FEC __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseR_FEC)
+#define SUPPORTED_20000baseMLD2_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(20000baseMLD2_Full)
+#define SUPPORTED_20000baseKR2_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(20000baseKR2_Full)
+#define SUPPORTED_40000baseKR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseKR4_Full)
+#define SUPPORTED_40000baseCR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseCR4_Full)
+#define SUPPORTED_40000baseSR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseSR4_Full)
+#define SUPPORTED_40000baseLR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseLR4_Full)
+#define SUPPORTED_56000baseKR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseKR4_Full)
+#define SUPPORTED_56000baseCR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseCR4_Full)
+#define SUPPORTED_56000baseSR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseSR4_Full)
+#define SUPPORTED_56000baseLR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseLR4_Full)
+/* Please do not define any new SUPPORTED_* macro for bits > 31, see
+ * notice above.
+ */
+
+/*
+ * DEPRECATED macros. Please migrate to
+ * ETHTOOL_GLINKSETTINGS/ETHTOOL_SLINKSETTINGS API. Please do NOT
+ * define any new ADERTISE_* macro for bits > 31.
+ */
+#define ADVERTISED_10baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(10baseT_Half)
+#define ADVERTISED_10baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10baseT_Full)
+#define ADVERTISED_100baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(100baseT_Half)
+#define ADVERTISED_100baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(100baseT_Full)
+#define ADVERTISED_1000baseT_Half __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseT_Half)
+#define ADVERTISED_1000baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseT_Full)
+#define ADVERTISED_Autoneg __ETHTOOL_LINK_MODE_LEGACY_MASK(Autoneg)
+#define ADVERTISED_TP __ETHTOOL_LINK_MODE_LEGACY_MASK(TP)
+#define ADVERTISED_AUI __ETHTOOL_LINK_MODE_LEGACY_MASK(AUI)
+#define ADVERTISED_MII __ETHTOOL_LINK_MODE_LEGACY_MASK(MII)
+#define ADVERTISED_FIBRE __ETHTOOL_LINK_MODE_LEGACY_MASK(FIBRE)
+#define ADVERTISED_BNC __ETHTOOL_LINK_MODE_LEGACY_MASK(BNC)
+#define ADVERTISED_10000baseT_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseT_Full)
+#define ADVERTISED_Pause __ETHTOOL_LINK_MODE_LEGACY_MASK(Pause)
+#define ADVERTISED_Asym_Pause __ETHTOOL_LINK_MODE_LEGACY_MASK(Asym_Pause)
+#define ADVERTISED_2500baseX_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(2500baseX_Full)
+#define ADVERTISED_Backplane __ETHTOOL_LINK_MODE_LEGACY_MASK(Backplane)
+#define ADVERTISED_1000baseKX_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(1000baseKX_Full)
+#define ADVERTISED_10000baseKX4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseKX4_Full)
+#define ADVERTISED_10000baseKR_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseKR_Full)
+#define ADVERTISED_10000baseR_FEC __ETHTOOL_LINK_MODE_LEGACY_MASK(10000baseR_FEC)
+#define ADVERTISED_20000baseMLD2_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(20000baseMLD2_Full)
+#define ADVERTISED_20000baseKR2_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(20000baseKR2_Full)
+#define ADVERTISED_40000baseKR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseKR4_Full)
+#define ADVERTISED_40000baseCR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseCR4_Full)
+#define ADVERTISED_40000baseSR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseSR4_Full)
+#define ADVERTISED_40000baseLR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(40000baseLR4_Full)
+#define ADVERTISED_56000baseKR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseKR4_Full)
+#define ADVERTISED_56000baseCR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseCR4_Full)
+#define ADVERTISED_56000baseSR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseSR4_Full)
+#define ADVERTISED_56000baseLR4_Full __ETHTOOL_LINK_MODE_LEGACY_MASK(56000baseLR4_Full)
+/* Please do not define any new ADVERTISED_* macro for bits > 31, see
+ * notice above.
+ */
+
+/* The following are all involved in forcing a particular link
+ * mode for the device for setting things. When getting the
+ * devices settings, these indicate the current mode and whether
+ * it was forced up into this mode or autonegotiated.
+ */
+
+/* The forced speed, in units of 1Mb. All values 0 to INT_MAX are legal.
+ * Update drivers/net/phy/phy.c:phy_speed_to_str() and
+ * drivers/net/bonding/bond_3ad.c:__get_link_speed() when adding new values.
+ */
+#define SPEED_10 10
+#define SPEED_100 100
+#define SPEED_1000 1000
+#define SPEED_2500 2500
+#define SPEED_5000 5000
+#define SPEED_10000 10000
+#define SPEED_14000 14000
+#define SPEED_20000 20000
+#define SPEED_25000 25000
+#define SPEED_40000 40000
+#define SPEED_50000 50000
+#define SPEED_56000 56000
+#define SPEED_100000 100000
+#define SPEED_200000 200000
+#define SPEED_400000 400000
+#define SPEED_800000 800000
+
+#define SPEED_UNKNOWN -1
+
+static __inline__ int ethtool_validate_speed(__u32 speed)
+{
+ return speed <= INT_MAX || speed == (__u32)SPEED_UNKNOWN;
+}
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF 0x00
+#define DUPLEX_FULL 0x01
+#define DUPLEX_UNKNOWN 0xff
+
+static __inline__ int ethtool_validate_duplex(__u8 duplex)
+{
+ switch (duplex) {
+ case DUPLEX_HALF:
+ case DUPLEX_FULL:
+ case DUPLEX_UNKNOWN:
+ return 1;
+ }
+
+ return 0;
+}
+
+#define MASTER_SLAVE_CFG_UNSUPPORTED 0
+#define MASTER_SLAVE_CFG_UNKNOWN 1
+#define MASTER_SLAVE_CFG_MASTER_PREFERRED 2
+#define MASTER_SLAVE_CFG_SLAVE_PREFERRED 3
+#define MASTER_SLAVE_CFG_MASTER_FORCE 4
+#define MASTER_SLAVE_CFG_SLAVE_FORCE 5
+#define MASTER_SLAVE_STATE_UNSUPPORTED 0
+#define MASTER_SLAVE_STATE_UNKNOWN 1
+#define MASTER_SLAVE_STATE_MASTER 2
+#define MASTER_SLAVE_STATE_SLAVE 3
+#define MASTER_SLAVE_STATE_ERR 4
+
+/* These are used to throttle the rate of data on the phy interface when the
+ * native speed of the interface is higher than the link speed. These should
+ * not be used for phy interfaces which natively support multiple speeds (e.g.
+ * MII or SGMII).
+ */
+/* No rate matching performed. */
+#define RATE_MATCH_NONE 0
+/* The phy sends pause frames to throttle the MAC. */
+#define RATE_MATCH_PAUSE 1
+/* The phy asserts CRS to prevent the MAC from transmitting. */
+#define RATE_MATCH_CRS 2
+/* The MAC is programmed with a sufficiently-large IPG. */
+#define RATE_MATCH_OPEN_LOOP 3
+
+/* Which connector port. */
+#define PORT_TP 0x00
+#define PORT_AUI 0x01
+#define PORT_MII 0x02
+#define PORT_FIBRE 0x03
+#define PORT_BNC 0x04
+#define PORT_DA 0x05
+#define PORT_NONE 0xef
+#define PORT_OTHER 0xff
+
+/* Which transceiver to use. */
+#define XCVR_INTERNAL 0x00 /* PHY and MAC are in the same package */
+#define XCVR_EXTERNAL 0x01 /* PHY and MAC are in different packages */
+#define XCVR_DUMMY1 0x02
+#define XCVR_DUMMY2 0x03
+#define XCVR_DUMMY3 0x04
+
+/* Enable or disable autonegotiation. */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+/* MDI or MDI-X status/control - if MDI/MDI_X/AUTO is set then
+ * the driver is required to renegotiate link
+ */
+#define ETH_TP_MDI_INVALID 0x00 /* status: unknown; control: unsupported */
+#define ETH_TP_MDI 0x01 /* status: MDI; control: force MDI */
+#define ETH_TP_MDI_X 0x02 /* status: MDI-X; control: force MDI-X */
+#define ETH_TP_MDI_AUTO 0x03 /* control: auto-select */
+
+/* Wake-On-Lan options. */
+#define WAKE_PHY (1 << 0)
+#define WAKE_UCAST (1 << 1)
+#define WAKE_MCAST (1 << 2)
+#define WAKE_BCAST (1 << 3)
+#define WAKE_ARP (1 << 4)
+#define WAKE_MAGIC (1 << 5)
+#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */
+#define WAKE_FILTER (1 << 7)
+
+#define WOL_MODE_COUNT 8
+
+/* L2-L4 network traffic flow types */
+#define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */
+#define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */
+#define SCTP_V4_FLOW 0x03 /* hash or spec (sctp_ip4_spec) */
+#define AH_ESP_V4_FLOW 0x04 /* hash only */
+#define TCP_V6_FLOW 0x05 /* hash or spec (tcp_ip6_spec; nfc only) */
+#define UDP_V6_FLOW 0x06 /* hash or spec (udp_ip6_spec; nfc only) */
+#define SCTP_V6_FLOW 0x07 /* hash or spec (sctp_ip6_spec; nfc only) */
+#define AH_ESP_V6_FLOW 0x08 /* hash only */
+#define AH_V4_FLOW 0x09 /* hash or spec (ah_ip4_spec) */
+#define ESP_V4_FLOW 0x0a /* hash or spec (esp_ip4_spec) */
+#define AH_V6_FLOW 0x0b /* hash or spec (ah_ip6_spec; nfc only) */
+#define ESP_V6_FLOW 0x0c /* hash or spec (esp_ip6_spec; nfc only) */
+#define IPV4_USER_FLOW 0x0d /* spec only (usr_ip4_spec) */
+#define IP_USER_FLOW IPV4_USER_FLOW
+#define IPV6_USER_FLOW 0x0e /* spec only (usr_ip6_spec; nfc only) */
+#define IPV4_FLOW 0x10 /* hash only */
+#define IPV6_FLOW 0x11 /* hash only */
+#define ETHER_FLOW 0x12 /* spec only (ether_spec) */
+/* Flag to enable additional fields in struct ethtool_rx_flow_spec */
+#define FLOW_EXT 0x80000000
+#define FLOW_MAC_EXT 0x40000000
+/* Flag to enable RSS spreading of traffic matching rule (nfc only) */
+#define FLOW_RSS 0x20000000
+
+/* L3-L4 network traffic flow hash options */
+#define RXH_L2DA (1 << 1)
+#define RXH_VLAN (1 << 2)
+#define RXH_L3_PROTO (1 << 3)
+#define RXH_IP_SRC (1 << 4)
+#define RXH_IP_DST (1 << 5)
+#define RXH_L4_B_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */
+#define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */
+#define RXH_DISCARD (1 << 31)
+
+#define RX_CLS_FLOW_DISC 0xffffffffffffffffULL
+#define RX_CLS_FLOW_WAKE 0xfffffffffffffffeULL
+
+/* Special RX classification rule insert location values */
+#define RX_CLS_LOC_SPECIAL 0x80000000 /* flag */
+#define RX_CLS_LOC_ANY 0xffffffff
+#define RX_CLS_LOC_FIRST 0xfffffffe
+#define RX_CLS_LOC_LAST 0xfffffffd
+
+/* EEPROM Standards for plug in modules */
+#define ETH_MODULE_SFF_8079 0x1
+#define ETH_MODULE_SFF_8079_LEN 256
+#define ETH_MODULE_SFF_8472 0x2
+#define ETH_MODULE_SFF_8472_LEN 512
+#define ETH_MODULE_SFF_8636 0x3
+#define ETH_MODULE_SFF_8636_LEN 256
+#define ETH_MODULE_SFF_8436 0x4
+#define ETH_MODULE_SFF_8436_LEN 256
+
+#define ETH_MODULE_SFF_8636_MAX_LEN 640
+#define ETH_MODULE_SFF_8436_MAX_LEN 640
+
+/* Reset flags */
+/* The reset() operation must clear the flags for the components which
+ * were actually reset. On successful return, the flags indicate the
+ * components which were not reset, either because they do not exist
+ * in the hardware or because they cannot be reset independently. The
+ * driver must never reset any components that were not requested.
+ */
+enum ethtool_reset_flags {
+ /* These flags represent components dedicated to the interface
+ * the command is addressed to. Shift any flag left by
+ * ETH_RESET_SHARED_SHIFT to reset a shared component of the
+ * same type.
+ */
+ ETH_RESET_MGMT = 1 << 0, /* Management processor */
+ ETH_RESET_IRQ = 1 << 1, /* Interrupt requester */
+ ETH_RESET_DMA = 1 << 2, /* DMA engine */
+ ETH_RESET_FILTER = 1 << 3, /* Filtering/flow direction */
+ ETH_RESET_OFFLOAD = 1 << 4, /* Protocol offload */
+ ETH_RESET_MAC = 1 << 5, /* Media access controller */
+ ETH_RESET_PHY = 1 << 6, /* Transceiver/PHY */
+ ETH_RESET_RAM = 1 << 7, /* RAM shared between
+ * multiple components */
+ ETH_RESET_AP = 1 << 8, /* Application processor */
+
+ ETH_RESET_DEDICATED = 0x0000ffff, /* All components dedicated to
+ * this interface */
+ ETH_RESET_ALL = 0xffffffff, /* All components used by this
+ * interface, even if shared */
+};
+#define ETH_RESET_SHARED_SHIFT 16
+
+
+/**
+ * struct ethtool_link_settings - link control and status
+ *
+ * IMPORTANT, Backward compatibility notice: When implementing new
+ * user-space tools, please first try %ETHTOOL_GLINKSETTINGS, and
+ * if it succeeds use %ETHTOOL_SLINKSETTINGS to change link
+ * settings; do not use %ETHTOOL_SSET if %ETHTOOL_GLINKSETTINGS
+ * succeeded: stick to %ETHTOOL_GLINKSETTINGS/%SLINKSETTINGS in
+ * that case. Conversely, if %ETHTOOL_GLINKSETTINGS fails, use
+ * %ETHTOOL_GSET to query and %ETHTOOL_SSET to change link
+ * settings; do not use %ETHTOOL_SLINKSETTINGS if
+ * %ETHTOOL_GLINKSETTINGS failed: stick to
+ * %ETHTOOL_GSET/%ETHTOOL_SSET in that case.
+ *
+ * @cmd: Command number = %ETHTOOL_GLINKSETTINGS or %ETHTOOL_SLINKSETTINGS
+ * @speed: Link speed (Mbps)
+ * @duplex: Duplex mode; one of %DUPLEX_*
+ * @port: Physical connector type; one of %PORT_*
+ * @phy_address: MDIO address of PHY (transceiver); 0 or 255 if not
+ * applicable. For clause 45 PHYs this is the PRTAD.
+ * @autoneg: Enable/disable autonegotiation and auto-detection;
+ * either %AUTONEG_DISABLE or %AUTONEG_ENABLE
+ * @mdio_support: Bitmask of %ETH_MDIO_SUPPORTS_* flags for the MDIO
+ * protocols supported by the interface; 0 if unknown.
+ * Read-only.
+ * @eth_tp_mdix: Ethernet twisted-pair MDI(-X) status; one of
+ * %ETH_TP_MDI_*. If the status is unknown or not applicable, the
+ * value will be %ETH_TP_MDI_INVALID. Read-only.
+ * @eth_tp_mdix_ctrl: Ethernet twisted pair MDI(-X) control; one of
+ * %ETH_TP_MDI_*. If MDI(-X) control is not implemented, reads
+ * yield %ETH_TP_MDI_INVALID and writes may be ignored or rejected.
+ * When written successfully, the link should be renegotiated if
+ * necessary.
+ * @link_mode_masks_nwords: Number of 32-bit words for each of the
+ * supported, advertising, lp_advertising link mode bitmaps. For
+ * %ETHTOOL_GLINKSETTINGS: on entry, number of words passed by user
+ * (>= 0); on return, if handshake in progress, negative if
+ * request size unsupported by kernel: absolute value indicates
+ * kernel expected size and all the other fields but cmd
+ * are 0; otherwise (handshake completed), strictly positive
+ * to indicate size used by kernel and cmd field stays
+ * %ETHTOOL_GLINKSETTINGS, all other fields populated by driver. For
+ * %ETHTOOL_SLINKSETTINGS: must be valid on entry, ie. a positive
+ * value returned previously by %ETHTOOL_GLINKSETTINGS, otherwise
+ * refused. For drivers: ignore this field (use kernel's
+ * __ETHTOOL_LINK_MODE_MASK_NBITS instead), any change to it will
+ * be overwritten by kernel.
+ * @supported: Bitmap with each bit meaning given by
+ * %ethtool_link_mode_bit_indices for the link modes, physical
+ * connectors and other link features for which the interface
+ * supports autonegotiation or auto-detection. Read-only.
+ * @advertising: Bitmap with each bit meaning given by
+ * %ethtool_link_mode_bit_indices for the link modes, physical
+ * connectors and other link features that are advertised through
+ * autonegotiation or enabled for auto-detection.
+ * @lp_advertising: Bitmap with each bit meaning given by
+ * %ethtool_link_mode_bit_indices for the link modes, and other
+ * link features that the link partner advertised through
+ * autonegotiation; 0 if unknown or not applicable. Read-only.
+ * @transceiver: Used to distinguish different possible PHY types,
+ * reported consistently by PHYLIB. Read-only.
+ * @master_slave_cfg: Master/slave port mode.
+ * @master_slave_state: Master/slave port state.
+ * @rate_matching: Rate adaptation performed by the PHY
+ * @reserved: Reserved for future use; see the note on reserved space.
+ * @link_mode_masks: Variable length bitmaps.
+ *
+ * If autonegotiation is disabled, the speed and @duplex represent the
+ * fixed link mode and are writable if the driver supports multiple
+ * link modes. If it is enabled then they are read-only; if the link
+ * is up they represent the negotiated link mode; if the link is down,
+ * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and
+ * @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
+ *
+ * Some hardware interfaces may have multiple PHYs and/or physical
+ * connectors fitted or do not allow the driver to detect which are
+ * fitted. For these interfaces @port and/or @phy_address may be
+ * writable, possibly dependent on @autoneg being %AUTONEG_DISABLE.
+ * Otherwise, attempts to write different values may be ignored or
+ * rejected.
+ *
+ * Deprecated %ethtool_cmd fields transceiver, maxtxpkt and maxrxpkt
+ * are not available in %ethtool_link_settings. These fields will be
+ * always set to zero in %ETHTOOL_GSET reply and %ETHTOOL_SSET will
+ * fail if any of them is set to non-zero value.
+ *
+ * Users should assume that all fields not marked read-only are
+ * writable and subject to validation by the driver. They should use
+ * %ETHTOOL_GLINKSETTINGS to get the current values before making specific
+ * changes and then applying them with %ETHTOOL_SLINKSETTINGS.
+ *
+ * Drivers that implement %get_link_ksettings and/or
+ * %set_link_ksettings should ignore the @cmd
+ * and @link_mode_masks_nwords fields (any change to them overwritten
+ * by kernel), and rely only on kernel's internal
+ * %__ETHTOOL_LINK_MODE_MASK_NBITS and
+ * %ethtool_link_mode_mask_t. Drivers that implement
+ * %set_link_ksettings() should validate all fields other than @cmd
+ * and @link_mode_masks_nwords that are not described as read-only or
+ * deprecated, and must ignore all fields described as read-only.
+ */
+struct ethtool_link_settings {
+ __u32 cmd;
+ __u32 speed;
+ __u8 duplex;
+ __u8 port;
+ __u8 phy_address;
+ __u8 autoneg;
+ __u8 mdio_support;
+ __u8 eth_tp_mdix;
+ __u8 eth_tp_mdix_ctrl;
+ __s8 link_mode_masks_nwords;
+ __u8 transceiver;
+ __u8 master_slave_cfg;
+ __u8 master_slave_state;
+ __u8 rate_matching;
+ __u32 reserved[7];
+ __u32 link_mode_masks[];
+ /* layout of link_mode_masks fields:
+ * __u32 map_supported[link_mode_masks_nwords];
+ * __u32 map_advertising[link_mode_masks_nwords];
+ * __u32 map_lp_advertising[link_mode_masks_nwords];
+ */
+};
+#endif /* _LINUX_ETHTOOL_H */
diff --git a/uapi/linux/ethtool_netlink.h b/uapi/linux/ethtool_netlink.h
new file mode 100644
index 0000000..a8b0d79
--- /dev/null
+++ b/uapi/linux/ethtool_netlink.h
@@ -0,0 +1,984 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * include/uapi/linux/ethtool_netlink.h - netlink interface for ethtool
+ *
+ * See Documentation/networking/ethtool-netlink.rst in kernel source tree for
+ * doucumentation of the interface.
+ */
+
+#ifndef _LINUX_ETHTOOL_NETLINK_H_
+#define _LINUX_ETHTOOL_NETLINK_H_
+
+#include <linux/ethtool.h>
+
+/* message types - userspace to kernel */
+enum {
+ ETHTOOL_MSG_USER_NONE,
+ ETHTOOL_MSG_STRSET_GET,
+ ETHTOOL_MSG_LINKINFO_GET,
+ ETHTOOL_MSG_LINKINFO_SET,
+ ETHTOOL_MSG_LINKMODES_GET,
+ ETHTOOL_MSG_LINKMODES_SET,
+ ETHTOOL_MSG_LINKSTATE_GET,
+ ETHTOOL_MSG_DEBUG_GET,
+ ETHTOOL_MSG_DEBUG_SET,
+ ETHTOOL_MSG_WOL_GET,
+ ETHTOOL_MSG_WOL_SET,
+ ETHTOOL_MSG_FEATURES_GET,
+ ETHTOOL_MSG_FEATURES_SET,
+ ETHTOOL_MSG_PRIVFLAGS_GET,
+ ETHTOOL_MSG_PRIVFLAGS_SET,
+ ETHTOOL_MSG_RINGS_GET,
+ ETHTOOL_MSG_RINGS_SET,
+ ETHTOOL_MSG_CHANNELS_GET,
+ ETHTOOL_MSG_CHANNELS_SET,
+ ETHTOOL_MSG_COALESCE_GET,
+ ETHTOOL_MSG_COALESCE_SET,
+ ETHTOOL_MSG_PAUSE_GET,
+ ETHTOOL_MSG_PAUSE_SET,
+ ETHTOOL_MSG_EEE_GET,
+ ETHTOOL_MSG_EEE_SET,
+ ETHTOOL_MSG_TSINFO_GET,
+ ETHTOOL_MSG_CABLE_TEST_ACT,
+ ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
+ ETHTOOL_MSG_TUNNEL_INFO_GET,
+ ETHTOOL_MSG_FEC_GET,
+ ETHTOOL_MSG_FEC_SET,
+ ETHTOOL_MSG_MODULE_EEPROM_GET,
+ ETHTOOL_MSG_STATS_GET,
+ ETHTOOL_MSG_PHC_VCLOCKS_GET,
+ ETHTOOL_MSG_MODULE_GET,
+ ETHTOOL_MSG_MODULE_SET,
+ ETHTOOL_MSG_PSE_GET,
+ ETHTOOL_MSG_PSE_SET,
+ ETHTOOL_MSG_RSS_GET,
+ ETHTOOL_MSG_PLCA_GET_CFG,
+ ETHTOOL_MSG_PLCA_SET_CFG,
+ ETHTOOL_MSG_PLCA_GET_STATUS,
+ ETHTOOL_MSG_MM_GET,
+ ETHTOOL_MSG_MM_SET,
+
+ /* add new constants above here */
+ __ETHTOOL_MSG_USER_CNT,
+ ETHTOOL_MSG_USER_MAX = __ETHTOOL_MSG_USER_CNT - 1
+};
+
+/* message types - kernel to userspace */
+enum {
+ ETHTOOL_MSG_KERNEL_NONE,
+ ETHTOOL_MSG_STRSET_GET_REPLY,
+ ETHTOOL_MSG_LINKINFO_GET_REPLY,
+ ETHTOOL_MSG_LINKINFO_NTF,
+ ETHTOOL_MSG_LINKMODES_GET_REPLY,
+ ETHTOOL_MSG_LINKMODES_NTF,
+ ETHTOOL_MSG_LINKSTATE_GET_REPLY,
+ ETHTOOL_MSG_DEBUG_GET_REPLY,
+ ETHTOOL_MSG_DEBUG_NTF,
+ ETHTOOL_MSG_WOL_GET_REPLY,
+ ETHTOOL_MSG_WOL_NTF,
+ ETHTOOL_MSG_FEATURES_GET_REPLY,
+ ETHTOOL_MSG_FEATURES_SET_REPLY,
+ ETHTOOL_MSG_FEATURES_NTF,
+ ETHTOOL_MSG_PRIVFLAGS_GET_REPLY,
+ ETHTOOL_MSG_PRIVFLAGS_NTF,
+ ETHTOOL_MSG_RINGS_GET_REPLY,
+ ETHTOOL_MSG_RINGS_NTF,
+ ETHTOOL_MSG_CHANNELS_GET_REPLY,
+ ETHTOOL_MSG_CHANNELS_NTF,
+ ETHTOOL_MSG_COALESCE_GET_REPLY,
+ ETHTOOL_MSG_COALESCE_NTF,
+ ETHTOOL_MSG_PAUSE_GET_REPLY,
+ ETHTOOL_MSG_PAUSE_NTF,
+ ETHTOOL_MSG_EEE_GET_REPLY,
+ ETHTOOL_MSG_EEE_NTF,
+ ETHTOOL_MSG_TSINFO_GET_REPLY,
+ ETHTOOL_MSG_CABLE_TEST_NTF,
+ ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
+ ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY,
+ ETHTOOL_MSG_FEC_GET_REPLY,
+ ETHTOOL_MSG_FEC_NTF,
+ ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY,
+ ETHTOOL_MSG_STATS_GET_REPLY,
+ ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY,
+ ETHTOOL_MSG_MODULE_GET_REPLY,
+ ETHTOOL_MSG_MODULE_NTF,
+ ETHTOOL_MSG_PSE_GET_REPLY,
+ ETHTOOL_MSG_RSS_GET_REPLY,
+ ETHTOOL_MSG_PLCA_GET_CFG_REPLY,
+ ETHTOOL_MSG_PLCA_GET_STATUS_REPLY,
+ ETHTOOL_MSG_PLCA_NTF,
+ ETHTOOL_MSG_MM_GET_REPLY,
+ ETHTOOL_MSG_MM_NTF,
+
+ /* add new constants above here */
+ __ETHTOOL_MSG_KERNEL_CNT,
+ ETHTOOL_MSG_KERNEL_MAX = __ETHTOOL_MSG_KERNEL_CNT - 1
+};
+
+/* request header */
+
+/* use compact bitsets in reply */
+#define ETHTOOL_FLAG_COMPACT_BITSETS (1 << 0)
+/* provide optional reply for SET or ACT requests */
+#define ETHTOOL_FLAG_OMIT_REPLY (1 << 1)
+/* request statistics, if supported by the driver */
+#define ETHTOOL_FLAG_STATS (1 << 2)
+
+#define ETHTOOL_FLAG_ALL (ETHTOOL_FLAG_COMPACT_BITSETS | \
+ ETHTOOL_FLAG_OMIT_REPLY | \
+ ETHTOOL_FLAG_STATS)
+
+enum {
+ ETHTOOL_A_HEADER_UNSPEC,
+ ETHTOOL_A_HEADER_DEV_INDEX, /* u32 */
+ ETHTOOL_A_HEADER_DEV_NAME, /* string */
+ ETHTOOL_A_HEADER_FLAGS, /* u32 - ETHTOOL_FLAG_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_HEADER_CNT,
+ ETHTOOL_A_HEADER_MAX = __ETHTOOL_A_HEADER_CNT - 1
+};
+
+/* bit sets */
+
+enum {
+ ETHTOOL_A_BITSET_BIT_UNSPEC,
+ ETHTOOL_A_BITSET_BIT_INDEX, /* u32 */
+ ETHTOOL_A_BITSET_BIT_NAME, /* string */
+ ETHTOOL_A_BITSET_BIT_VALUE, /* flag */
+
+ /* add new constants above here */
+ __ETHTOOL_A_BITSET_BIT_CNT,
+ ETHTOOL_A_BITSET_BIT_MAX = __ETHTOOL_A_BITSET_BIT_CNT - 1
+};
+
+enum {
+ ETHTOOL_A_BITSET_BITS_UNSPEC,
+ ETHTOOL_A_BITSET_BITS_BIT, /* nest - _A_BITSET_BIT_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_BITSET_BITS_CNT,
+ ETHTOOL_A_BITSET_BITS_MAX = __ETHTOOL_A_BITSET_BITS_CNT - 1
+};
+
+enum {
+ ETHTOOL_A_BITSET_UNSPEC,
+ ETHTOOL_A_BITSET_NOMASK, /* flag */
+ ETHTOOL_A_BITSET_SIZE, /* u32 */
+ ETHTOOL_A_BITSET_BITS, /* nest - _A_BITSET_BITS_* */
+ ETHTOOL_A_BITSET_VALUE, /* binary */
+ ETHTOOL_A_BITSET_MASK, /* binary */
+
+ /* add new constants above here */
+ __ETHTOOL_A_BITSET_CNT,
+ ETHTOOL_A_BITSET_MAX = __ETHTOOL_A_BITSET_CNT - 1
+};
+
+/* string sets */
+
+enum {
+ ETHTOOL_A_STRING_UNSPEC,
+ ETHTOOL_A_STRING_INDEX, /* u32 */
+ ETHTOOL_A_STRING_VALUE, /* string */
+
+ /* add new constants above here */
+ __ETHTOOL_A_STRING_CNT,
+ ETHTOOL_A_STRING_MAX = __ETHTOOL_A_STRING_CNT - 1
+};
+
+enum {
+ ETHTOOL_A_STRINGS_UNSPEC,
+ ETHTOOL_A_STRINGS_STRING, /* nest - _A_STRINGS_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_STRINGS_CNT,
+ ETHTOOL_A_STRINGS_MAX = __ETHTOOL_A_STRINGS_CNT - 1
+};
+
+enum {
+ ETHTOOL_A_STRINGSET_UNSPEC,
+ ETHTOOL_A_STRINGSET_ID, /* u32 */
+ ETHTOOL_A_STRINGSET_COUNT, /* u32 */
+ ETHTOOL_A_STRINGSET_STRINGS, /* nest - _A_STRINGS_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_STRINGSET_CNT,
+ ETHTOOL_A_STRINGSET_MAX = __ETHTOOL_A_STRINGSET_CNT - 1
+};
+
+enum {
+ ETHTOOL_A_STRINGSETS_UNSPEC,
+ ETHTOOL_A_STRINGSETS_STRINGSET, /* nest - _A_STRINGSET_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_STRINGSETS_CNT,
+ ETHTOOL_A_STRINGSETS_MAX = __ETHTOOL_A_STRINGSETS_CNT - 1
+};
+
+/* STRSET */
+
+enum {
+ ETHTOOL_A_STRSET_UNSPEC,
+ ETHTOOL_A_STRSET_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_STRSET_STRINGSETS, /* nest - _A_STRINGSETS_* */
+ ETHTOOL_A_STRSET_COUNTS_ONLY, /* flag */
+
+ /* add new constants above here */
+ __ETHTOOL_A_STRSET_CNT,
+ ETHTOOL_A_STRSET_MAX = __ETHTOOL_A_STRSET_CNT - 1
+};
+
+/* LINKINFO */
+
+enum {
+ ETHTOOL_A_LINKINFO_UNSPEC,
+ ETHTOOL_A_LINKINFO_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_LINKINFO_PORT, /* u8 */
+ ETHTOOL_A_LINKINFO_PHYADDR, /* u8 */
+ ETHTOOL_A_LINKINFO_TP_MDIX, /* u8 */
+ ETHTOOL_A_LINKINFO_TP_MDIX_CTRL, /* u8 */
+ ETHTOOL_A_LINKINFO_TRANSCEIVER, /* u8 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_LINKINFO_CNT,
+ ETHTOOL_A_LINKINFO_MAX = __ETHTOOL_A_LINKINFO_CNT - 1
+};
+
+/* LINKMODES */
+
+enum {
+ ETHTOOL_A_LINKMODES_UNSPEC,
+ ETHTOOL_A_LINKMODES_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_LINKMODES_AUTONEG, /* u8 */
+ ETHTOOL_A_LINKMODES_OURS, /* bitset */
+ ETHTOOL_A_LINKMODES_PEER, /* bitset */
+ ETHTOOL_A_LINKMODES_SPEED, /* u32 */
+ ETHTOOL_A_LINKMODES_DUPLEX, /* u8 */
+ ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG, /* u8 */
+ ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE, /* u8 */
+ ETHTOOL_A_LINKMODES_LANES, /* u32 */
+ ETHTOOL_A_LINKMODES_RATE_MATCHING, /* u8 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_LINKMODES_CNT,
+ ETHTOOL_A_LINKMODES_MAX = __ETHTOOL_A_LINKMODES_CNT - 1
+};
+
+/* LINKSTATE */
+
+enum {
+ ETHTOOL_A_LINKSTATE_UNSPEC,
+ ETHTOOL_A_LINKSTATE_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_LINKSTATE_LINK, /* u8 */
+ ETHTOOL_A_LINKSTATE_SQI, /* u32 */
+ ETHTOOL_A_LINKSTATE_SQI_MAX, /* u32 */
+ ETHTOOL_A_LINKSTATE_EXT_STATE, /* u8 */
+ ETHTOOL_A_LINKSTATE_EXT_SUBSTATE, /* u8 */
+ ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_LINKSTATE_CNT,
+ ETHTOOL_A_LINKSTATE_MAX = __ETHTOOL_A_LINKSTATE_CNT - 1
+};
+
+/* DEBUG */
+
+enum {
+ ETHTOOL_A_DEBUG_UNSPEC,
+ ETHTOOL_A_DEBUG_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_DEBUG_MSGMASK, /* bitset */
+
+ /* add new constants above here */
+ __ETHTOOL_A_DEBUG_CNT,
+ ETHTOOL_A_DEBUG_MAX = __ETHTOOL_A_DEBUG_CNT - 1
+};
+
+/* WOL */
+
+enum {
+ ETHTOOL_A_WOL_UNSPEC,
+ ETHTOOL_A_WOL_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_WOL_MODES, /* bitset */
+ ETHTOOL_A_WOL_SOPASS, /* binary */
+
+ /* add new constants above here */
+ __ETHTOOL_A_WOL_CNT,
+ ETHTOOL_A_WOL_MAX = __ETHTOOL_A_WOL_CNT - 1
+};
+
+/* FEATURES */
+
+enum {
+ ETHTOOL_A_FEATURES_UNSPEC,
+ ETHTOOL_A_FEATURES_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_FEATURES_HW, /* bitset */
+ ETHTOOL_A_FEATURES_WANTED, /* bitset */
+ ETHTOOL_A_FEATURES_ACTIVE, /* bitset */
+ ETHTOOL_A_FEATURES_NOCHANGE, /* bitset */
+
+ /* add new constants above here */
+ __ETHTOOL_A_FEATURES_CNT,
+ ETHTOOL_A_FEATURES_MAX = __ETHTOOL_A_FEATURES_CNT - 1
+};
+
+/* PRIVFLAGS */
+
+enum {
+ ETHTOOL_A_PRIVFLAGS_UNSPEC,
+ ETHTOOL_A_PRIVFLAGS_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_PRIVFLAGS_FLAGS, /* bitset */
+
+ /* add new constants above here */
+ __ETHTOOL_A_PRIVFLAGS_CNT,
+ ETHTOOL_A_PRIVFLAGS_MAX = __ETHTOOL_A_PRIVFLAGS_CNT - 1
+};
+
+/* RINGS */
+
+enum {
+ ETHTOOL_TCP_DATA_SPLIT_UNKNOWN = 0,
+ ETHTOOL_TCP_DATA_SPLIT_DISABLED,
+ ETHTOOL_TCP_DATA_SPLIT_ENABLED,
+};
+
+enum {
+ ETHTOOL_A_RINGS_UNSPEC,
+ ETHTOOL_A_RINGS_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_RINGS_RX_MAX, /* u32 */
+ ETHTOOL_A_RINGS_RX_MINI_MAX, /* u32 */
+ ETHTOOL_A_RINGS_RX_JUMBO_MAX, /* u32 */
+ ETHTOOL_A_RINGS_TX_MAX, /* u32 */
+ ETHTOOL_A_RINGS_RX, /* u32 */
+ ETHTOOL_A_RINGS_RX_MINI, /* u32 */
+ ETHTOOL_A_RINGS_RX_JUMBO, /* u32 */
+ ETHTOOL_A_RINGS_TX, /* u32 */
+ ETHTOOL_A_RINGS_RX_BUF_LEN, /* u32 */
+ ETHTOOL_A_RINGS_TCP_DATA_SPLIT, /* u8 */
+ ETHTOOL_A_RINGS_CQE_SIZE, /* u32 */
+ ETHTOOL_A_RINGS_TX_PUSH, /* u8 */
+ ETHTOOL_A_RINGS_RX_PUSH, /* u8 */
+ ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN, /* u32 */
+ ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_RINGS_CNT,
+ ETHTOOL_A_RINGS_MAX = (__ETHTOOL_A_RINGS_CNT - 1)
+};
+
+/* CHANNELS */
+
+enum {
+ ETHTOOL_A_CHANNELS_UNSPEC,
+ ETHTOOL_A_CHANNELS_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_CHANNELS_RX_MAX, /* u32 */
+ ETHTOOL_A_CHANNELS_TX_MAX, /* u32 */
+ ETHTOOL_A_CHANNELS_OTHER_MAX, /* u32 */
+ ETHTOOL_A_CHANNELS_COMBINED_MAX, /* u32 */
+ ETHTOOL_A_CHANNELS_RX_COUNT, /* u32 */
+ ETHTOOL_A_CHANNELS_TX_COUNT, /* u32 */
+ ETHTOOL_A_CHANNELS_OTHER_COUNT, /* u32 */
+ ETHTOOL_A_CHANNELS_COMBINED_COUNT, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CHANNELS_CNT,
+ ETHTOOL_A_CHANNELS_MAX = (__ETHTOOL_A_CHANNELS_CNT - 1)
+};
+
+/* COALESCE */
+
+enum {
+ ETHTOOL_A_COALESCE_UNSPEC,
+ ETHTOOL_A_COALESCE_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_COALESCE_RX_USECS, /* u32 */
+ ETHTOOL_A_COALESCE_RX_MAX_FRAMES, /* u32 */
+ ETHTOOL_A_COALESCE_RX_USECS_IRQ, /* u32 */
+ ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, /* u32 */
+ ETHTOOL_A_COALESCE_TX_USECS, /* u32 */
+ ETHTOOL_A_COALESCE_TX_MAX_FRAMES, /* u32 */
+ ETHTOOL_A_COALESCE_TX_USECS_IRQ, /* u32 */
+ ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, /* u32 */
+ ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, /* u32 */
+ ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, /* u8 */
+ ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, /* u8 */
+ ETHTOOL_A_COALESCE_PKT_RATE_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_RX_USECS_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_TX_USECS_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, /* u32 */
+ ETHTOOL_A_COALESCE_PKT_RATE_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_RX_USECS_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_TX_USECS_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, /* u32 */
+ ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, /* u32 */
+ ETHTOOL_A_COALESCE_USE_CQE_MODE_TX, /* u8 */
+ ETHTOOL_A_COALESCE_USE_CQE_MODE_RX, /* u8 */
+ ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES, /* u32 */
+ ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES, /* u32 */
+ ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_COALESCE_CNT,
+ ETHTOOL_A_COALESCE_MAX = (__ETHTOOL_A_COALESCE_CNT - 1)
+};
+
+/* PAUSE */
+
+enum {
+ ETHTOOL_A_PAUSE_UNSPEC,
+ ETHTOOL_A_PAUSE_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_PAUSE_AUTONEG, /* u8 */
+ ETHTOOL_A_PAUSE_RX, /* u8 */
+ ETHTOOL_A_PAUSE_TX, /* u8 */
+ ETHTOOL_A_PAUSE_STATS, /* nest - _PAUSE_STAT_* */
+ ETHTOOL_A_PAUSE_STATS_SRC, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_PAUSE_CNT,
+ ETHTOOL_A_PAUSE_MAX = (__ETHTOOL_A_PAUSE_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_PAUSE_STAT_UNSPEC,
+ ETHTOOL_A_PAUSE_STAT_PAD,
+
+ ETHTOOL_A_PAUSE_STAT_TX_FRAMES,
+ ETHTOOL_A_PAUSE_STAT_RX_FRAMES,
+
+ /* add new constants above here
+ * adjust ETHTOOL_PAUSE_STAT_CNT if adding non-stats!
+ */
+ __ETHTOOL_A_PAUSE_STAT_CNT,
+ ETHTOOL_A_PAUSE_STAT_MAX = (__ETHTOOL_A_PAUSE_STAT_CNT - 1)
+};
+
+/* EEE */
+
+enum {
+ ETHTOOL_A_EEE_UNSPEC,
+ ETHTOOL_A_EEE_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_EEE_MODES_OURS, /* bitset */
+ ETHTOOL_A_EEE_MODES_PEER, /* bitset */
+ ETHTOOL_A_EEE_ACTIVE, /* u8 */
+ ETHTOOL_A_EEE_ENABLED, /* u8 */
+ ETHTOOL_A_EEE_TX_LPI_ENABLED, /* u8 */
+ ETHTOOL_A_EEE_TX_LPI_TIMER, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_EEE_CNT,
+ ETHTOOL_A_EEE_MAX = (__ETHTOOL_A_EEE_CNT - 1)
+};
+
+/* TSINFO */
+
+enum {
+ ETHTOOL_A_TSINFO_UNSPEC,
+ ETHTOOL_A_TSINFO_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_TSINFO_TIMESTAMPING, /* bitset */
+ ETHTOOL_A_TSINFO_TX_TYPES, /* bitset */
+ ETHTOOL_A_TSINFO_RX_FILTERS, /* bitset */
+ ETHTOOL_A_TSINFO_PHC_INDEX, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TSINFO_CNT,
+ ETHTOOL_A_TSINFO_MAX = (__ETHTOOL_A_TSINFO_CNT - 1)
+};
+
+/* PHC VCLOCKS */
+
+enum {
+ ETHTOOL_A_PHC_VCLOCKS_UNSPEC,
+ ETHTOOL_A_PHC_VCLOCKS_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_PHC_VCLOCKS_NUM, /* u32 */
+ ETHTOOL_A_PHC_VCLOCKS_INDEX, /* array, s32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_PHC_VCLOCKS_CNT,
+ ETHTOOL_A_PHC_VCLOCKS_MAX = (__ETHTOOL_A_PHC_VCLOCKS_CNT - 1)
+};
+
+/* CABLE TEST */
+
+enum {
+ ETHTOOL_A_CABLE_TEST_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_HEADER, /* nest - _A_HEADER_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CABLE_TEST_CNT,
+ ETHTOOL_A_CABLE_TEST_MAX = __ETHTOOL_A_CABLE_TEST_CNT - 1
+};
+
+/* CABLE TEST NOTIFY */
+enum {
+ ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC,
+ ETHTOOL_A_CABLE_RESULT_CODE_OK,
+ ETHTOOL_A_CABLE_RESULT_CODE_OPEN,
+ ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT,
+ ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT,
+};
+
+enum {
+ ETHTOOL_A_CABLE_PAIR_A,
+ ETHTOOL_A_CABLE_PAIR_B,
+ ETHTOOL_A_CABLE_PAIR_C,
+ ETHTOOL_A_CABLE_PAIR_D,
+};
+
+enum {
+ ETHTOOL_A_CABLE_RESULT_UNSPEC,
+ ETHTOOL_A_CABLE_RESULT_PAIR, /* u8 ETHTOOL_A_CABLE_PAIR_ */
+ ETHTOOL_A_CABLE_RESULT_CODE, /* u8 ETHTOOL_A_CABLE_RESULT_CODE_ */
+
+ __ETHTOOL_A_CABLE_RESULT_CNT,
+ ETHTOOL_A_CABLE_RESULT_MAX = (__ETHTOOL_A_CABLE_RESULT_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_FAULT_LENGTH_UNSPEC,
+ ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, /* u8 ETHTOOL_A_CABLE_PAIR_ */
+ ETHTOOL_A_CABLE_FAULT_LENGTH_CM, /* u32 */
+
+ __ETHTOOL_A_CABLE_FAULT_LENGTH_CNT,
+ ETHTOOL_A_CABLE_FAULT_LENGTH_MAX = (__ETHTOOL_A_CABLE_FAULT_LENGTH_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED,
+ ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED
+};
+
+enum {
+ ETHTOOL_A_CABLE_NEST_UNSPEC,
+ ETHTOOL_A_CABLE_NEST_RESULT, /* nest - ETHTOOL_A_CABLE_RESULT_ */
+ ETHTOOL_A_CABLE_NEST_FAULT_LENGTH, /* nest - ETHTOOL_A_CABLE_FAULT_LENGTH_ */
+ __ETHTOOL_A_CABLE_NEST_CNT,
+ ETHTOOL_A_CABLE_NEST_MAX = (__ETHTOOL_A_CABLE_NEST_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_TEST_NTF_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_NTF_HEADER, /* nest - ETHTOOL_A_HEADER_* */
+ ETHTOOL_A_CABLE_TEST_NTF_STATUS, /* u8 - _STARTED/_COMPLETE */
+ ETHTOOL_A_CABLE_TEST_NTF_NEST, /* nest - of results: */
+
+ __ETHTOOL_A_CABLE_TEST_NTF_CNT,
+ ETHTOOL_A_CABLE_TEST_NTF_MAX = (__ETHTOOL_A_CABLE_TEST_NTF_CNT - 1)
+};
+
+/* CABLE TEST TDR */
+
+enum {
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST, /* u32 */
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST, /* u32 */
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP, /* u32 */
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR, /* u8 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CABLE_TEST_TDR_CFG_CNT,
+ ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX = __ETHTOOL_A_CABLE_TEST_TDR_CFG_CNT - 1
+};
+
+enum {
+ ETHTOOL_A_CABLE_TEST_TDR_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_TDR_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_CABLE_TEST_TDR_CFG, /* nest - *_TDR_CFG_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CABLE_TEST_TDR_CNT,
+ ETHTOOL_A_CABLE_TEST_TDR_MAX = __ETHTOOL_A_CABLE_TEST_TDR_CNT - 1
+};
+
+/* CABLE TEST TDR NOTIFY */
+
+enum {
+ ETHTOOL_A_CABLE_AMPLITUDE_UNSPEC,
+ ETHTOOL_A_CABLE_AMPLITUDE_PAIR, /* u8 */
+ ETHTOOL_A_CABLE_AMPLITUDE_mV, /* s16 */
+
+ __ETHTOOL_A_CABLE_AMPLITUDE_CNT,
+ ETHTOOL_A_CABLE_AMPLITUDE_MAX = (__ETHTOOL_A_CABLE_AMPLITUDE_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_PULSE_UNSPEC,
+ ETHTOOL_A_CABLE_PULSE_mV, /* s16 */
+
+ __ETHTOOL_A_CABLE_PULSE_CNT,
+ ETHTOOL_A_CABLE_PULSE_MAX = (__ETHTOOL_A_CABLE_PULSE_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_STEP_UNSPEC,
+ ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE, /* u32 */
+ ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, /* u32 */
+ ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, /* u32 */
+
+ __ETHTOOL_A_CABLE_STEP_CNT,
+ ETHTOOL_A_CABLE_STEP_MAX = (__ETHTOOL_A_CABLE_STEP_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_TDR_NEST_UNSPEC,
+ ETHTOOL_A_CABLE_TDR_NEST_STEP, /* nest - ETHTTOOL_A_CABLE_STEP */
+ ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE, /* nest - ETHTOOL_A_CABLE_AMPLITUDE */
+ ETHTOOL_A_CABLE_TDR_NEST_PULSE, /* nest - ETHTOOL_A_CABLE_PULSE */
+
+ __ETHTOOL_A_CABLE_TDR_NEST_CNT,
+ ETHTOOL_A_CABLE_TDR_NEST_MAX = (__ETHTOOL_A_CABLE_TDR_NEST_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_UNSPEC,
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_HEADER, /* nest - ETHTOOL_A_HEADER_* */
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_STATUS, /* u8 - _STARTED/_COMPLETE */
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST, /* nest - of results: */
+
+ /* add new constants above here */
+ __ETHTOOL_A_CABLE_TEST_TDR_NTF_CNT,
+ ETHTOOL_A_CABLE_TEST_TDR_NTF_MAX = __ETHTOOL_A_CABLE_TEST_TDR_NTF_CNT - 1
+};
+
+/* TUNNEL INFO */
+
+enum {
+ ETHTOOL_UDP_TUNNEL_TYPE_VXLAN,
+ ETHTOOL_UDP_TUNNEL_TYPE_GENEVE,
+ ETHTOOL_UDP_TUNNEL_TYPE_VXLAN_GPE,
+
+ __ETHTOOL_UDP_TUNNEL_TYPE_CNT
+};
+
+enum {
+ ETHTOOL_A_TUNNEL_UDP_ENTRY_UNSPEC,
+
+ ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT, /* be16 */
+ ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TUNNEL_UDP_ENTRY_CNT,
+ ETHTOOL_A_TUNNEL_UDP_ENTRY_MAX = (__ETHTOOL_A_TUNNEL_UDP_ENTRY_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_TUNNEL_UDP_TABLE_UNSPEC,
+
+ ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE, /* u32 */
+ ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES, /* bitset */
+ ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY, /* nest - _UDP_ENTRY_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TUNNEL_UDP_TABLE_CNT,
+ ETHTOOL_A_TUNNEL_UDP_TABLE_MAX = (__ETHTOOL_A_TUNNEL_UDP_TABLE_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_TUNNEL_UDP_UNSPEC,
+
+ ETHTOOL_A_TUNNEL_UDP_TABLE, /* nest - _UDP_TABLE_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TUNNEL_UDP_CNT,
+ ETHTOOL_A_TUNNEL_UDP_MAX = (__ETHTOOL_A_TUNNEL_UDP_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_TUNNEL_INFO_UNSPEC,
+ ETHTOOL_A_TUNNEL_INFO_HEADER, /* nest - _A_HEADER_* */
+
+ ETHTOOL_A_TUNNEL_INFO_UDP_PORTS, /* nest - _UDP_TABLE */
+
+ /* add new constants above here */
+ __ETHTOOL_A_TUNNEL_INFO_CNT,
+ ETHTOOL_A_TUNNEL_INFO_MAX = (__ETHTOOL_A_TUNNEL_INFO_CNT - 1)
+};
+
+/* FEC */
+
+enum {
+ ETHTOOL_A_FEC_UNSPEC,
+ ETHTOOL_A_FEC_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_FEC_MODES, /* bitset */
+ ETHTOOL_A_FEC_AUTO, /* u8 */
+ ETHTOOL_A_FEC_ACTIVE, /* u32 */
+ ETHTOOL_A_FEC_STATS, /* nest - _A_FEC_STAT */
+
+ __ETHTOOL_A_FEC_CNT,
+ ETHTOOL_A_FEC_MAX = (__ETHTOOL_A_FEC_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_FEC_STAT_UNSPEC,
+ ETHTOOL_A_FEC_STAT_PAD,
+
+ ETHTOOL_A_FEC_STAT_CORRECTED, /* array, u64 */
+ ETHTOOL_A_FEC_STAT_UNCORR, /* array, u64 */
+ ETHTOOL_A_FEC_STAT_CORR_BITS, /* array, u64 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_FEC_STAT_CNT,
+ ETHTOOL_A_FEC_STAT_MAX = (__ETHTOOL_A_FEC_STAT_CNT - 1)
+};
+
+/* MODULE EEPROM */
+
+enum {
+ ETHTOOL_A_MODULE_EEPROM_UNSPEC,
+ ETHTOOL_A_MODULE_EEPROM_HEADER, /* nest - _A_HEADER_* */
+
+ ETHTOOL_A_MODULE_EEPROM_OFFSET, /* u32 */
+ ETHTOOL_A_MODULE_EEPROM_LENGTH, /* u32 */
+ ETHTOOL_A_MODULE_EEPROM_PAGE, /* u8 */
+ ETHTOOL_A_MODULE_EEPROM_BANK, /* u8 */
+ ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, /* u8 */
+ ETHTOOL_A_MODULE_EEPROM_DATA, /* binary */
+
+ __ETHTOOL_A_MODULE_EEPROM_CNT,
+ ETHTOOL_A_MODULE_EEPROM_MAX = (__ETHTOOL_A_MODULE_EEPROM_CNT - 1)
+};
+
+/* STATS */
+
+enum {
+ ETHTOOL_A_STATS_UNSPEC,
+ ETHTOOL_A_STATS_PAD,
+ ETHTOOL_A_STATS_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_STATS_GROUPS, /* bitset */
+
+ ETHTOOL_A_STATS_GRP, /* nest - _A_STATS_GRP_* */
+
+ ETHTOOL_A_STATS_SRC, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_STATS_CNT,
+ ETHTOOL_A_STATS_MAX = (__ETHTOOL_A_STATS_CNT - 1)
+};
+
+enum {
+ ETHTOOL_STATS_ETH_PHY,
+ ETHTOOL_STATS_ETH_MAC,
+ ETHTOOL_STATS_ETH_CTRL,
+ ETHTOOL_STATS_RMON,
+
+ /* add new constants above here */
+ __ETHTOOL_STATS_CNT
+};
+
+enum {
+ ETHTOOL_A_STATS_GRP_UNSPEC,
+ ETHTOOL_A_STATS_GRP_PAD,
+
+ ETHTOOL_A_STATS_GRP_ID, /* u32 */
+ ETHTOOL_A_STATS_GRP_SS_ID, /* u32 */
+
+ ETHTOOL_A_STATS_GRP_STAT, /* nest */
+
+ ETHTOOL_A_STATS_GRP_HIST_RX, /* nest */
+ ETHTOOL_A_STATS_GRP_HIST_TX, /* nest */
+
+ ETHTOOL_A_STATS_GRP_HIST_BKT_LOW, /* u32 */
+ ETHTOOL_A_STATS_GRP_HIST_BKT_HI, /* u32 */
+ ETHTOOL_A_STATS_GRP_HIST_VAL, /* u64 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_STATS_GRP_CNT,
+ ETHTOOL_A_STATS_GRP_MAX = (__ETHTOOL_A_STATS_GRP_CNT - 1)
+};
+
+enum {
+ /* 30.3.2.1.5 aSymbolErrorDuringCarrier */
+ ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR,
+
+ /* add new constants above here */
+ __ETHTOOL_A_STATS_ETH_PHY_CNT,
+ ETHTOOL_A_STATS_ETH_PHY_MAX = (__ETHTOOL_A_STATS_ETH_PHY_CNT - 1)
+};
+
+enum {
+ /* 30.3.1.1.2 aFramesTransmittedOK */
+ ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT,
+ /* 30.3.1.1.3 aSingleCollisionFrames */
+ ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL,
+ /* 30.3.1.1.4 aMultipleCollisionFrames */
+ ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL,
+ /* 30.3.1.1.5 aFramesReceivedOK */
+ ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT,
+ /* 30.3.1.1.6 aFrameCheckSequenceErrors */
+ ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR,
+ /* 30.3.1.1.7 aAlignmentErrors */
+ ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR,
+ /* 30.3.1.1.8 aOctetsTransmittedOK */
+ ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES,
+ /* 30.3.1.1.9 aFramesWithDeferredXmissions */
+ ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER,
+ /* 30.3.1.1.10 aLateCollisions */
+ ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL,
+ /* 30.3.1.1.11 aFramesAbortedDueToXSColls */
+ ETHTOOL_A_STATS_ETH_MAC_11_XS_COL,
+ /* 30.3.1.1.12 aFramesLostDueToIntMACXmitError */
+ ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR,
+ /* 30.3.1.1.13 aCarrierSenseErrors */
+ ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR,
+ /* 30.3.1.1.14 aOctetsReceivedOK */
+ ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES,
+ /* 30.3.1.1.15 aFramesLostDueToIntMACRcvError */
+ ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR,
+
+ /* 30.3.1.1.18 aMulticastFramesXmittedOK */
+ ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST,
+ /* 30.3.1.1.19 aBroadcastFramesXmittedOK */
+ ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST,
+ /* 30.3.1.1.20 aFramesWithExcessiveDeferral */
+ ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER,
+ /* 30.3.1.1.21 aMulticastFramesReceivedOK */
+ ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST,
+ /* 30.3.1.1.22 aBroadcastFramesReceivedOK */
+ ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST,
+ /* 30.3.1.1.23 aInRangeLengthErrors */
+ ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR,
+ /* 30.3.1.1.24 aOutOfRangeLengthField */
+ ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN,
+ /* 30.3.1.1.25 aFrameTooLongErrors */
+ ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR,
+
+ /* add new constants above here */
+ __ETHTOOL_A_STATS_ETH_MAC_CNT,
+ ETHTOOL_A_STATS_ETH_MAC_MAX = (__ETHTOOL_A_STATS_ETH_MAC_CNT - 1)
+};
+
+enum {
+ /* 30.3.3.3 aMACControlFramesTransmitted */
+ ETHTOOL_A_STATS_ETH_CTRL_3_TX,
+ /* 30.3.3.4 aMACControlFramesReceived */
+ ETHTOOL_A_STATS_ETH_CTRL_4_RX,
+ /* 30.3.3.5 aUnsupportedOpcodesReceived */
+ ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP,
+
+ /* add new constants above here */
+ __ETHTOOL_A_STATS_ETH_CTRL_CNT,
+ ETHTOOL_A_STATS_ETH_CTRL_MAX = (__ETHTOOL_A_STATS_ETH_CTRL_CNT - 1)
+};
+
+enum {
+ /* etherStatsUndersizePkts */
+ ETHTOOL_A_STATS_RMON_UNDERSIZE,
+ /* etherStatsOversizePkts */
+ ETHTOOL_A_STATS_RMON_OVERSIZE,
+ /* etherStatsFragments */
+ ETHTOOL_A_STATS_RMON_FRAG,
+ /* etherStatsJabbers */
+ ETHTOOL_A_STATS_RMON_JABBER,
+
+ /* add new constants above here */
+ __ETHTOOL_A_STATS_RMON_CNT,
+ ETHTOOL_A_STATS_RMON_MAX = (__ETHTOOL_A_STATS_RMON_CNT - 1)
+};
+
+/* MODULE */
+
+enum {
+ ETHTOOL_A_MODULE_UNSPEC,
+ ETHTOOL_A_MODULE_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_MODULE_POWER_MODE_POLICY, /* u8 */
+ ETHTOOL_A_MODULE_POWER_MODE, /* u8 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_MODULE_CNT,
+ ETHTOOL_A_MODULE_MAX = (__ETHTOOL_A_MODULE_CNT - 1)
+};
+
+/* Power Sourcing Equipment */
+enum {
+ ETHTOOL_A_PSE_UNSPEC,
+ ETHTOOL_A_PSE_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_PODL_PSE_ADMIN_STATE, /* u32 */
+ ETHTOOL_A_PODL_PSE_ADMIN_CONTROL, /* u32 */
+ ETHTOOL_A_PODL_PSE_PW_D_STATUS, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_PSE_CNT,
+ ETHTOOL_A_PSE_MAX = (__ETHTOOL_A_PSE_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_RSS_UNSPEC,
+ ETHTOOL_A_RSS_HEADER,
+ ETHTOOL_A_RSS_CONTEXT, /* u32 */
+ ETHTOOL_A_RSS_HFUNC, /* u32 */
+ ETHTOOL_A_RSS_INDIR, /* binary */
+ ETHTOOL_A_RSS_HKEY, /* binary */
+
+ __ETHTOOL_A_RSS_CNT,
+ ETHTOOL_A_RSS_MAX = (__ETHTOOL_A_RSS_CNT - 1),
+};
+
+/* PLCA */
+
+enum {
+ ETHTOOL_A_PLCA_UNSPEC,
+ ETHTOOL_A_PLCA_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_PLCA_VERSION, /* u16 */
+ ETHTOOL_A_PLCA_ENABLED, /* u8 */
+ ETHTOOL_A_PLCA_STATUS, /* u8 */
+ ETHTOOL_A_PLCA_NODE_CNT, /* u32 */
+ ETHTOOL_A_PLCA_NODE_ID, /* u32 */
+ ETHTOOL_A_PLCA_TO_TMR, /* u32 */
+ ETHTOOL_A_PLCA_BURST_CNT, /* u32 */
+ ETHTOOL_A_PLCA_BURST_TMR, /* u32 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_PLCA_CNT,
+ ETHTOOL_A_PLCA_MAX = (__ETHTOOL_A_PLCA_CNT - 1)
+};
+
+/* MAC Merge (802.3) */
+
+enum {
+ ETHTOOL_A_MM_STAT_UNSPEC,
+ ETHTOOL_A_MM_STAT_PAD,
+
+ /* aMACMergeFrameAssErrorCount */
+ ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS, /* u64 */
+ /* aMACMergeFrameSmdErrorCount */
+ ETHTOOL_A_MM_STAT_SMD_ERRORS, /* u64 */
+ /* aMACMergeFrameAssOkCount */
+ ETHTOOL_A_MM_STAT_REASSEMBLY_OK, /* u64 */
+ /* aMACMergeFragCountRx */
+ ETHTOOL_A_MM_STAT_RX_FRAG_COUNT, /* u64 */
+ /* aMACMergeFragCountTx */
+ ETHTOOL_A_MM_STAT_TX_FRAG_COUNT, /* u64 */
+ /* aMACMergeHoldCount */
+ ETHTOOL_A_MM_STAT_HOLD_COUNT, /* u64 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_MM_STAT_CNT,
+ ETHTOOL_A_MM_STAT_MAX = (__ETHTOOL_A_MM_STAT_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_MM_UNSPEC,
+ ETHTOOL_A_MM_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_MM_PMAC_ENABLED, /* u8 */
+ ETHTOOL_A_MM_TX_ENABLED, /* u8 */
+ ETHTOOL_A_MM_TX_ACTIVE, /* u8 */
+ ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, /* u32 */
+ ETHTOOL_A_MM_RX_MIN_FRAG_SIZE, /* u32 */
+ ETHTOOL_A_MM_VERIFY_ENABLED, /* u8 */
+ ETHTOOL_A_MM_VERIFY_STATUS, /* u8 */
+ ETHTOOL_A_MM_VERIFY_TIME, /* u32 */
+ ETHTOOL_A_MM_MAX_VERIFY_TIME, /* u32 */
+ ETHTOOL_A_MM_STATS, /* nest - _A_MM_STAT_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_MM_CNT,
+ ETHTOOL_A_MM_MAX = (__ETHTOOL_A_MM_CNT - 1)
+};
+
+/* generic netlink info */
+#define ETHTOOL_GENL_NAME "ethtool"
+#define ETHTOOL_GENL_VERSION 1
+
+#define ETHTOOL_MCGRP_MONITOR_NAME "monitor"
+
+#endif /* _LINUX_ETHTOOL_NETLINK_H_ */
diff --git a/uapi/linux/genetlink.h b/uapi/linux/genetlink.h
new file mode 100644
index 0000000..e9b8117
--- /dev/null
+++ b/uapi/linux/genetlink.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_GENERIC_NETLINK_H
+#define __LINUX_GENERIC_NETLINK_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+#define GENL_NAMSIZ 16 /* length of family name */
+
+#define GENL_MIN_ID NLMSG_MIN_TYPE
+#define GENL_MAX_ID 1023
+
+struct genlmsghdr {
+ __u8 cmd;
+ __u8 version;
+ __u16 reserved;
+};
+
+#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr))
+
+#define GENL_ADMIN_PERM 0x01
+#define GENL_CMD_CAP_DO 0x02
+#define GENL_CMD_CAP_DUMP 0x04
+#define GENL_CMD_CAP_HASPOL 0x08
+#define GENL_UNS_ADMIN_PERM 0x10
+
+/*
+ * List of reserved static generic netlink identifiers:
+ */
+#define GENL_ID_CTRL NLMSG_MIN_TYPE
+#define GENL_ID_VFS_DQUOT (NLMSG_MIN_TYPE + 1)
+#define GENL_ID_PMCRAID (NLMSG_MIN_TYPE + 2)
+/* must be last reserved + 1 */
+#define GENL_START_ALLOC (NLMSG_MIN_TYPE + 3)
+
+/**************************************************************************
+ * Controller
+ **************************************************************************/
+
+enum {
+ CTRL_CMD_UNSPEC,
+ CTRL_CMD_NEWFAMILY,
+ CTRL_CMD_DELFAMILY,
+ CTRL_CMD_GETFAMILY,
+ CTRL_CMD_NEWOPS,
+ CTRL_CMD_DELOPS,
+ CTRL_CMD_GETOPS,
+ CTRL_CMD_NEWMCAST_GRP,
+ CTRL_CMD_DELMCAST_GRP,
+ CTRL_CMD_GETMCAST_GRP, /* unused */
+ CTRL_CMD_GETPOLICY,
+ __CTRL_CMD_MAX,
+};
+
+#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1)
+
+enum {
+ CTRL_ATTR_UNSPEC,
+ CTRL_ATTR_FAMILY_ID,
+ CTRL_ATTR_FAMILY_NAME,
+ CTRL_ATTR_VERSION,
+ CTRL_ATTR_HDRSIZE,
+ CTRL_ATTR_MAXATTR,
+ CTRL_ATTR_OPS,
+ CTRL_ATTR_MCAST_GROUPS,
+ CTRL_ATTR_POLICY,
+ CTRL_ATTR_OP_POLICY,
+ CTRL_ATTR_OP,
+ __CTRL_ATTR_MAX,
+};
+
+#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1)
+
+enum {
+ CTRL_ATTR_OP_UNSPEC,
+ CTRL_ATTR_OP_ID,
+ CTRL_ATTR_OP_FLAGS,
+ __CTRL_ATTR_OP_MAX,
+};
+
+#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+
+enum {
+ CTRL_ATTR_MCAST_GRP_UNSPEC,
+ CTRL_ATTR_MCAST_GRP_NAME,
+ CTRL_ATTR_MCAST_GRP_ID,
+ __CTRL_ATTR_MCAST_GRP_MAX,
+};
+
+#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
+
+enum {
+ CTRL_ATTR_POLICY_UNSPEC,
+ CTRL_ATTR_POLICY_DO,
+ CTRL_ATTR_POLICY_DUMP,
+
+ __CTRL_ATTR_POLICY_DUMP_MAX,
+ CTRL_ATTR_POLICY_DUMP_MAX = __CTRL_ATTR_POLICY_DUMP_MAX - 1
+};
+
+#define CTRL_ATTR_POLICY_MAX (__CTRL_ATTR_POLICY_DUMP_MAX - 1)
+
+#endif /* __LINUX_GENERIC_NETLINK_H */
diff --git a/uapi/linux/hdlc/ioctl.h b/uapi/linux/hdlc/ioctl.h
new file mode 100644
index 0000000..b06341a
--- /dev/null
+++ b/uapi/linux/hdlc/ioctl.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __HDLC_IOCTL_H__
+#define __HDLC_IOCTL_H__
+
+
+#define GENERIC_HDLC_VERSION 4 /* For synchronization with sethdlc utility */
+
+#define CLOCK_DEFAULT 0 /* Default setting */
+#define CLOCK_EXT 1 /* External TX and RX clock - DTE */
+#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */
+#define CLOCK_TXINT 3 /* Internal TX and external RX clock */
+#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */
+
+
+#define ENCODING_DEFAULT 0 /* Default setting */
+#define ENCODING_NRZ 1
+#define ENCODING_NRZI 2
+#define ENCODING_FM_MARK 3
+#define ENCODING_FM_SPACE 4
+#define ENCODING_MANCHESTER 5
+
+
+#define PARITY_DEFAULT 0 /* Default setting */
+#define PARITY_NONE 1 /* No parity */
+#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */
+#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */
+#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */
+#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */
+#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */
+#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */
+
+#define LMI_DEFAULT 0 /* Default setting */
+#define LMI_NONE 1 /* No LMI, all PVCs are static */
+#define LMI_ANSI 2 /* ANSI Annex D */
+#define LMI_CCITT 3 /* ITU-T Annex A */
+#define LMI_CISCO 4 /* The "original" LMI, aka Gang of Four */
+
+#ifndef __ASSEMBLY__
+
+typedef struct {
+ unsigned int clock_rate; /* bits per second */
+ unsigned int clock_type; /* internal, external, TX-internal etc. */
+ unsigned short loopback;
+} sync_serial_settings; /* V.35, V.24, X.21 */
+
+typedef struct {
+ unsigned int clock_rate; /* bits per second */
+ unsigned int clock_type; /* internal, external, TX-internal etc. */
+ unsigned short loopback;
+ unsigned int slot_map;
+} te1_settings; /* T1, E1 */
+
+typedef struct {
+ unsigned short encoding;
+ unsigned short parity;
+} raw_hdlc_proto;
+
+typedef struct {
+ unsigned int t391;
+ unsigned int t392;
+ unsigned int n391;
+ unsigned int n392;
+ unsigned int n393;
+ unsigned short lmi;
+ unsigned short dce; /* 1 for DCE (network side) operation */
+} fr_proto;
+
+typedef struct {
+ unsigned int dlci;
+} fr_proto_pvc; /* for creating/deleting FR PVCs */
+
+typedef struct {
+ unsigned int dlci;
+ char master[IFNAMSIZ]; /* Name of master FRAD device */
+}fr_proto_pvc_info; /* for returning PVC information only */
+
+typedef struct {
+ unsigned int interval;
+ unsigned int timeout;
+} cisco_proto;
+
+typedef struct {
+ unsigned short dce; /* 1 for DCE (network side) operation */
+ unsigned int modulo; /* modulo (8 = basic / 128 = extended) */
+ unsigned int window; /* frame window size */
+ unsigned int t1; /* timeout t1 */
+ unsigned int t2; /* timeout t2 */
+ unsigned int n2; /* frame retry counter */
+} x25_hdlc_proto;
+
+/* PPP doesn't need any info now - supply length = 0 to ioctl */
+
+#endif /* __ASSEMBLY__ */
+#endif /* __HDLC_IOCTL_H__ */
diff --git a/uapi/linux/if.h b/uapi/linux/if.h
new file mode 100644
index 0000000..b287b2a
--- /dev/null
+++ b/uapi/linux/if.h
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the INET interface module.
+ *
+ * Version: @(#)if.h 1.0.2 04/18/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988
+ * Ross Biro
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_H
+#define _LINUX_IF_H
+
+#include <linux/libc-compat.h> /* for compatibility with glibc */
+#include <linux/types.h> /* for "__kernel_caddr_t" et al */
+#include <linux/socket.h> /* for "struct sockaddr" et al */
+ /* for "__user" et al */
+
+#include <sys/socket.h> /* for struct sockaddr. */
+
+#if __UAPI_DEF_IF_IFNAMSIZ
+#define IFNAMSIZ 16
+#endif /* __UAPI_DEF_IF_IFNAMSIZ */
+#define IFALIASZ 256
+#define ALTIFNAMSIZ 128
+#include <linux/hdlc/ioctl.h>
+
+/* For glibc compatibility. An empty enum does not compile. */
+#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || \
+ __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0
+/**
+ * enum net_device_flags - &struct net_device flags
+ *
+ * These are the &struct net_device flags, they can be set by drivers, the
+ * kernel and some can be triggered by userspace. Userspace can query and
+ * set these flags using userspace utilities but there is also a sysfs
+ * entry available for all dev flags which can be queried and set. These flags
+ * are shared for all types of net_devices. The sysfs entries are available
+ * via /sys/class/net/<dev>/flags. Flags which can be toggled through sysfs
+ * are annotated below, note that only a few flags can be toggled and some
+ * other flags are always preserved from the original net_device flags
+ * even if you try to set them via sysfs. Flags which are always preserved
+ * are kept under the flag grouping @IFF_VOLATILE. Flags which are __volatile__
+ * are annotated below as such.
+ *
+ * You should have a pretty good reason to be extending these flags.
+ *
+ * @IFF_UP: interface is up. Can be toggled through sysfs.
+ * @IFF_BROADCAST: broadcast address valid. Volatile.
+ * @IFF_DEBUG: turn on debugging. Can be toggled through sysfs.
+ * @IFF_LOOPBACK: is a loopback net. Volatile.
+ * @IFF_POINTOPOINT: interface is has p-p link. Volatile.
+ * @IFF_NOTRAILERS: avoid use of trailers. Can be toggled through sysfs.
+ * Volatile.
+ * @IFF_RUNNING: interface RFC2863 OPER_UP. Volatile.
+ * @IFF_NOARP: no ARP protocol. Can be toggled through sysfs. Volatile.
+ * @IFF_PROMISC: receive all packets. Can be toggled through sysfs.
+ * @IFF_ALLMULTI: receive all multicast packets. Can be toggled through
+ * sysfs.
+ * @IFF_MASTER: master of a load balancer. Volatile.
+ * @IFF_SLAVE: slave of a load balancer. Volatile.
+ * @IFF_MULTICAST: Supports multicast. Can be toggled through sysfs.
+ * @IFF_PORTSEL: can set media type. Can be toggled through sysfs.
+ * @IFF_AUTOMEDIA: auto media select active. Can be toggled through sysfs.
+ * @IFF_DYNAMIC: dialup device with changing addresses. Can be toggled
+ * through sysfs.
+ * @IFF_LOWER_UP: driver signals L1 up. Volatile.
+ * @IFF_DORMANT: driver signals dormant. Volatile.
+ * @IFF_ECHO: echo sent packets. Volatile.
+ */
+enum net_device_flags {
+/* for compatibility with glibc net/if.h */
+#if __UAPI_DEF_IF_NET_DEVICE_FLAGS
+ IFF_UP = 1<<0, /* sysfs */
+ IFF_BROADCAST = 1<<1, /* __volatile__ */
+ IFF_DEBUG = 1<<2, /* sysfs */
+ IFF_LOOPBACK = 1<<3, /* __volatile__ */
+ IFF_POINTOPOINT = 1<<4, /* __volatile__ */
+ IFF_NOTRAILERS = 1<<5, /* sysfs */
+ IFF_RUNNING = 1<<6, /* __volatile__ */
+ IFF_NOARP = 1<<7, /* sysfs */
+ IFF_PROMISC = 1<<8, /* sysfs */
+ IFF_ALLMULTI = 1<<9, /* sysfs */
+ IFF_MASTER = 1<<10, /* __volatile__ */
+ IFF_SLAVE = 1<<11, /* __volatile__ */
+ IFF_MULTICAST = 1<<12, /* sysfs */
+ IFF_PORTSEL = 1<<13, /* sysfs */
+ IFF_AUTOMEDIA = 1<<14, /* sysfs */
+ IFF_DYNAMIC = 1<<15, /* sysfs */
+#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */
+#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
+ IFF_LOWER_UP = 1<<16, /* __volatile__ */
+ IFF_DORMANT = 1<<17, /* __volatile__ */
+ IFF_ECHO = 1<<18, /* __volatile__ */
+#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */
+};
+#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO != 0 || __UAPI_DEF_IF_NET_DEVICE_FLAGS != 0 */
+
+/* for compatibility with glibc net/if.h */
+#if __UAPI_DEF_IF_NET_DEVICE_FLAGS
+#define IFF_UP IFF_UP
+#define IFF_BROADCAST IFF_BROADCAST
+#define IFF_DEBUG IFF_DEBUG
+#define IFF_LOOPBACK IFF_LOOPBACK
+#define IFF_POINTOPOINT IFF_POINTOPOINT
+#define IFF_NOTRAILERS IFF_NOTRAILERS
+#define IFF_RUNNING IFF_RUNNING
+#define IFF_NOARP IFF_NOARP
+#define IFF_PROMISC IFF_PROMISC
+#define IFF_ALLMULTI IFF_ALLMULTI
+#define IFF_MASTER IFF_MASTER
+#define IFF_SLAVE IFF_SLAVE
+#define IFF_MULTICAST IFF_MULTICAST
+#define IFF_PORTSEL IFF_PORTSEL
+#define IFF_AUTOMEDIA IFF_AUTOMEDIA
+#define IFF_DYNAMIC IFF_DYNAMIC
+#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */
+
+#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
+#define IFF_LOWER_UP IFF_LOWER_UP
+#define IFF_DORMANT IFF_DORMANT
+#define IFF_ECHO IFF_ECHO
+#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */
+
+#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
+ IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
+
+#define IF_GET_IFACE 0x0001 /* for querying only */
+#define IF_GET_PROTO 0x0002
+
+/* For definitions see hdlc.h */
+#define IF_IFACE_V35 0x1000 /* V.35 serial interface */
+#define IF_IFACE_V24 0x1001 /* V.24 serial interface */
+#define IF_IFACE_X21 0x1002 /* X.21 serial interface */
+#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */
+#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */
+#define IF_IFACE_SYNC_SERIAL 0x1005 /* can't be set by software */
+#define IF_IFACE_X21D 0x1006 /* X.21 Dual Clocking (FarSite) */
+
+/* For definitions see hdlc.h */
+#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */
+#define IF_PROTO_PPP 0x2001 /* PPP protocol */
+#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */
+#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */
+#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */
+#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */
+#define IF_PROTO_X25 0x2006 /* X.25 */
+#define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */
+#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */
+#define IF_PROTO_FR_ETH_PVC 0x200B
+#define IF_PROTO_RAW 0x200C /* RAW Socket */
+
+/* RFC 2863 operational status */
+enum {
+ IF_OPER_UNKNOWN,
+ IF_OPER_NOTPRESENT,
+ IF_OPER_DOWN,
+ IF_OPER_LOWERLAYERDOWN,
+ IF_OPER_TESTING,
+ IF_OPER_DORMANT,
+ IF_OPER_UP,
+};
+
+/* link modes */
+enum {
+ IF_LINK_MODE_DEFAULT,
+ IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */
+ IF_LINK_MODE_TESTING, /* limit upward transition to testing */
+};
+
+/*
+ * Device mapping structure. I'd just gone off and designed a
+ * beautiful scheme using only loadable modules with arguments
+ * for driver options and along come the PCMCIA people 8)
+ *
+ * Ah well. The get() side of this is good for WDSETUP, and it'll
+ * be handy for debugging things. The set side is fine for now and
+ * being very small might be worth keeping for clean configuration.
+ */
+
+/* for compatibility with glibc net/if.h */
+#if __UAPI_DEF_IF_IFMAP
+struct ifmap {
+ unsigned long mem_start;
+ unsigned long mem_end;
+ unsigned short base_addr;
+ unsigned char irq;
+ unsigned char dma;
+ unsigned char port;
+ /* 3 bytes spare */
+};
+#endif /* __UAPI_DEF_IF_IFMAP */
+
+struct if_settings {
+ unsigned int type; /* Type of physical device or protocol */
+ unsigned int size; /* Size of the data allocated by the caller */
+ union {
+ /* {atm/eth/dsl}_settings anyone ? */
+ raw_hdlc_proto *raw_hdlc;
+ cisco_proto *cisco;
+ fr_proto *fr;
+ fr_proto_pvc *fr_pvc;
+ fr_proto_pvc_info *fr_pvc_info;
+ x25_hdlc_proto *x25;
+
+ /* interface settings */
+ sync_serial_settings *sync;
+ te1_settings *te1;
+ } ifs_ifsu;
+};
+
+/*
+ * Interface request structure used for socket
+ * ioctl's. All interface ioctl's must have parameter
+ * definitions which begin with ifr_name. The
+ * remainder may be interface specific.
+ */
+
+/* for compatibility with glibc net/if.h */
+#if __UAPI_DEF_IF_IFREQ
+struct ifreq {
+#define IFHWADDRLEN 6
+ union
+ {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ } ifr_ifrn;
+
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short ifru_flags;
+ int ifru_ivalue;
+ int ifru_mtu;
+ struct ifmap ifru_map;
+ char ifru_slave[IFNAMSIZ]; /* Just fits the size */
+ char ifru_newname[IFNAMSIZ];
+ void * ifru_data;
+ struct if_settings ifru_settings;
+ } ifr_ifru;
+};
+#endif /* __UAPI_DEF_IF_IFREQ */
+
+#define ifr_name ifr_ifrn.ifrn_name /* interface name */
+#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
+#define ifr_addr ifr_ifru.ifru_addr /* address */
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
+#define ifr_flags ifr_ifru.ifru_flags /* flags */
+#define ifr_metric ifr_ifru.ifru_ivalue /* metric */
+#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
+#define ifr_map ifr_ifru.ifru_map /* device map */
+#define ifr_slave ifr_ifru.ifru_slave /* slave device */
+#define ifr_data ifr_ifru.ifru_data /* for use by interface */
+#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
+#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
+#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */
+#define ifr_newname ifr_ifru.ifru_newname /* New name */
+#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/
+
+/*
+ * Structure used in SIOCGIFCONF request.
+ * Used to retrieve interface configuration
+ * for machine (useful for programs which
+ * must know all networks accessible).
+ */
+
+/* for compatibility with glibc net/if.h */
+#if __UAPI_DEF_IF_IFCONF
+struct ifconf {
+ int ifc_len; /* size of buffer */
+ union {
+ char *ifcu_buf;
+ struct ifreq *ifcu_req;
+ } ifc_ifcu;
+};
+#endif /* __UAPI_DEF_IF_IFCONF */
+
+#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
+#define ifc_req ifc_ifcu.ifcu_req /* array of structures */
+
+#endif /* _LINUX_IF_H */
diff --git a/uapi/linux/if_addr.h b/uapi/linux/if_addr.h
new file mode 100644
index 0000000..d6db3ff
--- /dev/null
+++ b/uapi/linux/if_addr.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_IF_ADDR_H
+#define __LINUX_IF_ADDR_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+struct ifaddrmsg {
+ __u8 ifa_family;
+ __u8 ifa_prefixlen; /* The prefix length */
+ __u8 ifa_flags; /* Flags */
+ __u8 ifa_scope; /* Address scope */
+ __u32 ifa_index; /* Link index */
+};
+
+/*
+ * Important comment:
+ * IFA_ADDRESS is prefix address, rather than local interface address.
+ * It makes no difference for normally configured broadcast interfaces,
+ * but for point-to-point IFA_ADDRESS is DESTINATION address,
+ * local address is supplied in IFA_LOCAL attribute.
+ *
+ * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags.
+ * If present, the value from struct ifaddrmsg will be ignored.
+ */
+enum {
+ IFA_UNSPEC,
+ IFA_ADDRESS,
+ IFA_LOCAL,
+ IFA_LABEL,
+ IFA_BROADCAST,
+ IFA_ANYCAST,
+ IFA_CACHEINFO,
+ IFA_MULTICAST,
+ IFA_FLAGS,
+ IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */
+ IFA_TARGET_NETNSID,
+ IFA_PROTO, /* u8, address protocol */
+ __IFA_MAX,
+};
+
+#define IFA_MAX (__IFA_MAX - 1)
+
+/* ifa_flags */
+#define IFA_F_SECONDARY 0x01
+#define IFA_F_TEMPORARY IFA_F_SECONDARY
+
+#define IFA_F_NODAD 0x02
+#define IFA_F_OPTIMISTIC 0x04
+#define IFA_F_DADFAILED 0x08
+#define IFA_F_HOMEADDRESS 0x10
+#define IFA_F_DEPRECATED 0x20
+#define IFA_F_TENTATIVE 0x40
+#define IFA_F_PERMANENT 0x80
+#define IFA_F_MANAGETEMPADDR 0x100
+#define IFA_F_NOPREFIXROUTE 0x200
+#define IFA_F_MCAUTOJOIN 0x400
+#define IFA_F_STABLE_PRIVACY 0x800
+
+struct ifa_cacheinfo {
+ __u32 ifa_prefered;
+ __u32 ifa_valid;
+ __u32 cstamp; /* created timestamp, hundredths of seconds */
+ __u32 tstamp; /* updated timestamp, hundredths of seconds */
+};
+
+/* backwards compatibility for userspace */
+#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+
+/* ifa_proto */
+#define IFAPROT_UNSPEC 0
+#define IFAPROT_KERNEL_LO 1 /* loopback */
+#define IFAPROT_KERNEL_RA 2 /* set by kernel from router announcement */
+#define IFAPROT_KERNEL_LL 3 /* link-local set by kernel */
+
+#endif
diff --git a/uapi/linux/if_ether.h b/uapi/linux/if_ether.h
new file mode 100644
index 0000000..a1aff8e
--- /dev/null
+++ b/uapi/linux/if_ether.h
@@ -0,0 +1,181 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the Ethernet IEEE 802.3 interface.
+ *
+ * Version: @(#)if_ether.h 1.0.1a 02/08/94
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Alan Cox, <alan@lxorguk.ukuu.org.uk>
+ * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_ETHER_H
+#define _LINUX_IF_ETHER_H
+
+#include <linux/types.h>
+
+/*
+ * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble
+ * and FCS/CRC (frame check sequence).
+ */
+
+#define ETH_ALEN 6 /* Octets in one ethernet addr */
+#define ETH_TLEN 2 /* Octets in ethernet type field */
+#define ETH_HLEN 14 /* Total octets in header. */
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN 1500 /* Max. octets in payload */
+#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
+#define ETH_FCS_LEN 4 /* Octets in the FCS */
+
+#define ETH_MIN_MTU 68 /* Min IPv4 MTU per RFC791 */
+#define ETH_MAX_MTU 0xFFFFU /* 65535, same as IP_MAX_MTU */
+
+/*
+ * These are the defined Ethernet Protocol ID's.
+ */
+
+#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */
+#define ETH_P_PUP 0x0200 /* Xerox PUP packet */
+#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */
+#define ETH_P_TSN 0x22F0 /* TSN (IEEE 1722) packet */
+#define ETH_P_ERSPAN2 0x22EB /* ERSPAN version 2 (type III) */
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_X25 0x0805 /* CCITT X.25 */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */
+#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet */
+#define ETH_P_BATMAN 0x4305 /* B.A.T.M.A.N.-Advanced packet [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_DEC 0x6000 /* DEC Assigned proto */
+#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */
+#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */
+#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */
+#define ETH_P_LAT 0x6004 /* DEC LAT */
+#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */
+#define ETH_P_CUST 0x6006 /* DEC Customer use */
+#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */
+#define ETH_P_TEB 0x6558 /* Trans Ether Bridging */
+#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */
+#define ETH_P_ATALK 0x809B /* Appletalk DDP */
+#define ETH_P_AARP 0x80F3 /* Appletalk AARP */
+#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
+#define ETH_P_ERSPAN 0x88BE /* ERSPAN type II */
+#define ETH_P_IPX 0x8137 /* IPX over DIX */
+#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */
+#define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */
+#define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */
+#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol
+ * defined in draft-wilson-wrec-wccp-v2-00.txt */
+#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */
+#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */
+#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */
+#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */
+#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */
+#define ETH_P_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */
+#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport
+ * over Ethernet
+ */
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#define ETH_P_PROFINET 0x8892 /* PROFINET */
+#define ETH_P_REALTEK 0x8899 /* Multiple proprietary protocols */
+#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */
+#define ETH_P_ETHERCAT 0x88A4 /* EtherCAT */
+#define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */
+#define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */
+#define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */
+#define ETH_P_TIPC 0x88CA /* TIPC */
+#define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */
+#define ETH_P_MRP 0x88E3 /* Media Redundancy Protocol */
+#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */
+#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */
+#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */
+#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
+#define ETH_P_NCSI 0x88F8 /* NCSI protocol */
+#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */
+#define ETH_P_CFM 0x8902 /* Connectivity Fault Management */
+#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */
+#define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */
+#define ETH_P_TDLS 0x890D /* TDLS */
+#define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */
+#define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */
+#define ETH_P_HSR 0x892F /* IEC 62439-3 HSRv1 */
+#define ETH_P_NSH 0x894F /* Network Service Header */
+#define ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */
+#define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_DSA_8021Q 0xDADB /* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */
+#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
+
+#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value
+ * then the frame is Ethernet II. Else it is 802.3 */
+
+/*
+ * Non DIX types. Won't clash for 1500 types.
+ */
+
+#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */
+#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */
+#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */
+#define ETH_P_802_2 0x0004 /* 802.2 frames */
+#define ETH_P_SNAP 0x0005 /* Internal only */
+#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: Internal only */
+#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/
+#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */
+#define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */
+#define ETH_P_CAN 0x000C /* CAN: Controller Area Network */
+#define ETH_P_CANFD 0x000D /* CANFD: CAN flexible data rate*/
+#define ETH_P_CANXL 0x000E /* CANXL: eXtended frame Length */
+#define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/
+#define ETH_P_TR_802_2 0x0011 /* 802.2 frames */
+#define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */
+#define ETH_P_CONTROL 0x0016 /* Card specific control frames */
+#define ETH_P_IRDA 0x0017 /* Linux-IrDA */
+#define ETH_P_ECONET 0x0018 /* Acorn Econet */
+#define ETH_P_HDLC 0x0019 /* HDLC frames */
+#define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */
+#define ETH_P_DSA 0x001B /* Distributed Switch Arch. */
+#define ETH_P_TRAILER 0x001C /* Trailer switch tagging */
+#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */
+#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */
+#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */
+#define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */
+#define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and
+ * aggregation protocol
+ */
+#define ETH_P_MCTP 0x00FA /* Management component transport
+ * protocol packets
+ */
+
+/*
+ * This is an Ethernet frame header.
+ */
+
+/* allow libcs like musl to deactivate this, glibc does not implement this. */
+#ifndef __UAPI_DEF_ETHHDR
+#define __UAPI_DEF_ETHHDR 1
+#endif
+
+#if __UAPI_DEF_ETHHDR
+struct ethhdr {
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ __be16 h_proto; /* packet type ID field */
+} __attribute__((packed));
+#endif
+
+
+#endif /* _LINUX_IF_ETHER_H */
diff --git a/uapi/linux/if_link.h b/uapi/linux/if_link.h
new file mode 100644
index 0000000..c146352
--- /dev/null
+++ b/uapi/linux/if_link.h
@@ -0,0 +1,1427 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_IF_LINK_H
+#define _LINUX_IF_LINK_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+/* This struct should be in sync with struct rtnl_link_stats64 */
+struct rtnl_link_stats {
+ __u32 rx_packets;
+ __u32 tx_packets;
+ __u32 rx_bytes;
+ __u32 tx_bytes;
+ __u32 rx_errors;
+ __u32 tx_errors;
+ __u32 rx_dropped;
+ __u32 tx_dropped;
+ __u32 multicast;
+ __u32 collisions;
+ /* detailed rx_errors: */
+ __u32 rx_length_errors;
+ __u32 rx_over_errors;
+ __u32 rx_crc_errors;
+ __u32 rx_frame_errors;
+ __u32 rx_fifo_errors;
+ __u32 rx_missed_errors;
+
+ /* detailed tx_errors */
+ __u32 tx_aborted_errors;
+ __u32 tx_carrier_errors;
+ __u32 tx_fifo_errors;
+ __u32 tx_heartbeat_errors;
+ __u32 tx_window_errors;
+
+ /* for cslip etc */
+ __u32 rx_compressed;
+ __u32 tx_compressed;
+
+ __u32 rx_nohandler;
+};
+
+/**
+ * struct rtnl_link_stats64 - The main device statistics structure.
+ *
+ * @rx_packets: Number of good packets received by the interface.
+ * For hardware interfaces counts all good packets received from the device
+ * by the host, including packets which host had to drop at various stages
+ * of processing (even in the driver).
+ *
+ * @tx_packets: Number of packets successfully transmitted.
+ * For hardware interfaces counts packets which host was able to successfully
+ * hand over to the device, which does not necessarily mean that packets
+ * had been successfully transmitted out of the device, only that device
+ * acknowledged it copied them out of host memory.
+ *
+ * @rx_bytes: Number of good received bytes, corresponding to @rx_packets.
+ *
+ * For IEEE 802.3 devices should count the length of Ethernet Frames
+ * excluding the FCS.
+ *
+ * @tx_bytes: Number of good transmitted bytes, corresponding to @tx_packets.
+ *
+ * For IEEE 802.3 devices should count the length of Ethernet Frames
+ * excluding the FCS.
+ *
+ * @rx_errors: Total number of bad packets received on this network device.
+ * This counter must include events counted by @rx_length_errors,
+ * @rx_crc_errors, @rx_frame_errors and other errors not otherwise
+ * counted.
+ *
+ * @tx_errors: Total number of transmit problems.
+ * This counter must include events counter by @tx_aborted_errors,
+ * @tx_carrier_errors, @tx_fifo_errors, @tx_heartbeat_errors,
+ * @tx_window_errors and other errors not otherwise counted.
+ *
+ * @rx_dropped: Number of packets received but not processed,
+ * e.g. due to lack of resources or unsupported protocol.
+ * For hardware interfaces this counter may include packets discarded
+ * due to L2 address filtering but should not include packets dropped
+ * by the device due to buffer exhaustion which are counted separately in
+ * @rx_missed_errors (since procfs folds those two counters together).
+ *
+ * @tx_dropped: Number of packets dropped on their way to transmission,
+ * e.g. due to lack of resources.
+ *
+ * @multicast: Multicast packets received.
+ * For hardware interfaces this statistic is commonly calculated
+ * at the device level (unlike @rx_packets) and therefore may include
+ * packets which did not reach the host.
+ *
+ * For IEEE 802.3 devices this counter may be equivalent to:
+ *
+ * - 30.3.1.1.21 aMulticastFramesReceivedOK
+ *
+ * @collisions: Number of collisions during packet transmissions.
+ *
+ * @rx_length_errors: Number of packets dropped due to invalid length.
+ * Part of aggregate "frame" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter should be equivalent to a sum
+ * of the following attributes:
+ *
+ * - 30.3.1.1.23 aInRangeLengthErrors
+ * - 30.3.1.1.24 aOutOfRangeLengthField
+ * - 30.3.1.1.25 aFrameTooLongErrors
+ *
+ * @rx_over_errors: Receiver FIFO overflow event counter.
+ *
+ * Historically the count of overflow events. Such events may be
+ * reported in the receive descriptors or via interrupts, and may
+ * not correspond one-to-one with dropped packets.
+ *
+ * The recommended interpretation for high speed interfaces is -
+ * number of packets dropped because they did not fit into buffers
+ * provided by the host, e.g. packets larger than MTU or next buffer
+ * in the ring was not available for a scatter transfer.
+ *
+ * Part of aggregate "frame" errors in `/proc/net/dev`.
+ *
+ * This statistics was historically used interchangeably with
+ * @rx_fifo_errors.
+ *
+ * This statistic corresponds to hardware events and is not commonly used
+ * on software devices.
+ *
+ * @rx_crc_errors: Number of packets received with a CRC error.
+ * Part of aggregate "frame" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter must be equivalent to:
+ *
+ * - 30.3.1.1.6 aFrameCheckSequenceErrors
+ *
+ * @rx_frame_errors: Receiver frame alignment errors.
+ * Part of aggregate "frame" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter should be equivalent to:
+ *
+ * - 30.3.1.1.7 aAlignmentErrors
+ *
+ * @rx_fifo_errors: Receiver FIFO error counter.
+ *
+ * Historically the count of overflow events. Those events may be
+ * reported in the receive descriptors or via interrupts, and may
+ * not correspond one-to-one with dropped packets.
+ *
+ * This statistics was used interchangeably with @rx_over_errors.
+ * Not recommended for use in drivers for high speed interfaces.
+ *
+ * This statistic is used on software devices, e.g. to count software
+ * packet queue overflow (can) or sequencing errors (GRE).
+ *
+ * @rx_missed_errors: Count of packets missed by the host.
+ * Folded into the "drop" counter in `/proc/net/dev`.
+ *
+ * Counts number of packets dropped by the device due to lack
+ * of buffer space. This usually indicates that the host interface
+ * is slower than the network interface, or host is not keeping up
+ * with the receive packet rate.
+ *
+ * This statistic corresponds to hardware events and is not used
+ * on software devices.
+ *
+ * @tx_aborted_errors:
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ * For IEEE 802.3 devices capable of half-duplex operation this counter
+ * must be equivalent to:
+ *
+ * - 30.3.1.1.11 aFramesAbortedDueToXSColls
+ *
+ * High speed interfaces may use this counter as a general device
+ * discard counter.
+ *
+ * @tx_carrier_errors: Number of frame transmission errors due to loss
+ * of carrier during transmission.
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter must be equivalent to:
+ *
+ * - 30.3.1.1.13 aCarrierSenseErrors
+ *
+ * @tx_fifo_errors: Number of frame transmission errors due to device
+ * FIFO underrun / underflow. This condition occurs when the device
+ * begins transmission of a frame but is unable to deliver the
+ * entire frame to the transmitter in time for transmission.
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ *
+ * @tx_heartbeat_errors: Number of Heartbeat / SQE Test errors for
+ * old half-duplex Ethernet.
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices possibly equivalent to:
+ *
+ * - 30.3.2.1.4 aSQETestErrors
+ *
+ * @tx_window_errors: Number of frame transmission errors due
+ * to late collisions (for Ethernet - after the first 64B of transmission).
+ * Part of aggregate "carrier" errors in `/proc/net/dev`.
+ *
+ * For IEEE 802.3 devices this counter must be equivalent to:
+ *
+ * - 30.3.1.1.10 aLateCollisions
+ *
+ * @rx_compressed: Number of correctly received compressed packets.
+ * This counters is only meaningful for interfaces which support
+ * packet compression (e.g. CSLIP, PPP).
+ *
+ * @tx_compressed: Number of transmitted compressed packets.
+ * This counters is only meaningful for interfaces which support
+ * packet compression (e.g. CSLIP, PPP).
+ *
+ * @rx_nohandler: Number of packets received on the interface
+ * but dropped by the networking stack because the device is
+ * not designated to receive packets (e.g. backup link in a bond).
+ *
+ * @rx_otherhost_dropped: Number of packets dropped due to mismatch
+ * in destination MAC address.
+ */
+struct rtnl_link_stats64 {
+ __u64 rx_packets;
+ __u64 tx_packets;
+ __u64 rx_bytes;
+ __u64 tx_bytes;
+ __u64 rx_errors;
+ __u64 tx_errors;
+ __u64 rx_dropped;
+ __u64 tx_dropped;
+ __u64 multicast;
+ __u64 collisions;
+
+ /* detailed rx_errors: */
+ __u64 rx_length_errors;
+ __u64 rx_over_errors;
+ __u64 rx_crc_errors;
+ __u64 rx_frame_errors;
+ __u64 rx_fifo_errors;
+ __u64 rx_missed_errors;
+
+ /* detailed tx_errors */
+ __u64 tx_aborted_errors;
+ __u64 tx_carrier_errors;
+ __u64 tx_fifo_errors;
+ __u64 tx_heartbeat_errors;
+ __u64 tx_window_errors;
+
+ /* for cslip etc */
+ __u64 rx_compressed;
+ __u64 tx_compressed;
+ __u64 rx_nohandler;
+
+ __u64 rx_otherhost_dropped;
+};
+
+/* Subset of link stats useful for in-HW collection. Meaning of the fields is as
+ * for struct rtnl_link_stats64.
+ */
+struct rtnl_hw_stats64 {
+ __u64 rx_packets;
+ __u64 tx_packets;
+ __u64 rx_bytes;
+ __u64 tx_bytes;
+ __u64 rx_errors;
+ __u64 tx_errors;
+ __u64 rx_dropped;
+ __u64 tx_dropped;
+ __u64 multicast;
+};
+
+/* The struct should be in sync with struct ifmap */
+struct rtnl_link_ifmap {
+ __u64 mem_start;
+ __u64 mem_end;
+ __u64 base_addr;
+ __u16 irq;
+ __u8 dma;
+ __u8 port;
+};
+
+/*
+ * IFLA_AF_SPEC
+ * Contains nested attributes for address family specific attributes.
+ * Each address family may create a attribute with the address family
+ * number as type and create its own attribute structure in it.
+ *
+ * Example:
+ * [IFLA_AF_SPEC] = {
+ * [AF_INET] = {
+ * [IFLA_INET_CONF] = ...,
+ * },
+ * [AF_INET6] = {
+ * [IFLA_INET6_FLAGS] = ...,
+ * [IFLA_INET6_CONF] = ...,
+ * }
+ * }
+ */
+
+enum {
+ IFLA_UNSPEC,
+ IFLA_ADDRESS,
+ IFLA_BROADCAST,
+ IFLA_IFNAME,
+ IFLA_MTU,
+ IFLA_LINK,
+ IFLA_QDISC,
+ IFLA_STATS,
+ IFLA_COST,
+#define IFLA_COST IFLA_COST
+ IFLA_PRIORITY,
+#define IFLA_PRIORITY IFLA_PRIORITY
+ IFLA_MASTER,
+#define IFLA_MASTER IFLA_MASTER
+ IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */
+#define IFLA_WIRELESS IFLA_WIRELESS
+ IFLA_PROTINFO, /* Protocol specific information for a link */
+#define IFLA_PROTINFO IFLA_PROTINFO
+ IFLA_TXQLEN,
+#define IFLA_TXQLEN IFLA_TXQLEN
+ IFLA_MAP,
+#define IFLA_MAP IFLA_MAP
+ IFLA_WEIGHT,
+#define IFLA_WEIGHT IFLA_WEIGHT
+ IFLA_OPERSTATE,
+ IFLA_LINKMODE,
+ IFLA_LINKINFO,
+#define IFLA_LINKINFO IFLA_LINKINFO
+ IFLA_NET_NS_PID,
+ IFLA_IFALIAS,
+ IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */
+ IFLA_VFINFO_LIST,
+ IFLA_STATS64,
+ IFLA_VF_PORTS,
+ IFLA_PORT_SELF,
+ IFLA_AF_SPEC,
+ IFLA_GROUP, /* Group the device belongs to */
+ IFLA_NET_NS_FD,
+ IFLA_EXT_MASK, /* Extended info mask, VFs, etc */
+ IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */
+#define IFLA_PROMISCUITY IFLA_PROMISCUITY
+ IFLA_NUM_TX_QUEUES,
+ IFLA_NUM_RX_QUEUES,
+ IFLA_CARRIER,
+ IFLA_PHYS_PORT_ID,
+ IFLA_CARRIER_CHANGES,
+ IFLA_PHYS_SWITCH_ID,
+ IFLA_LINK_NETNSID,
+ IFLA_PHYS_PORT_NAME,
+ IFLA_PROTO_DOWN,
+ IFLA_GSO_MAX_SEGS,
+ IFLA_GSO_MAX_SIZE,
+ IFLA_PAD,
+ IFLA_XDP,
+ IFLA_EVENT,
+ IFLA_NEW_NETNSID,
+ IFLA_IF_NETNSID,
+ IFLA_TARGET_NETNSID = IFLA_IF_NETNSID, /* new alias */
+ IFLA_CARRIER_UP_COUNT,
+ IFLA_CARRIER_DOWN_COUNT,
+ IFLA_NEW_IFINDEX,
+ IFLA_MIN_MTU,
+ IFLA_MAX_MTU,
+ IFLA_PROP_LIST,
+ IFLA_ALT_IFNAME, /* Alternative ifname */
+ IFLA_PERM_ADDRESS,
+ IFLA_PROTO_DOWN_REASON,
+
+ /* device (sysfs) name as parent, used instead
+ * of IFLA_LINK where there's no parent netdev
+ */
+ IFLA_PARENT_DEV_NAME,
+ IFLA_PARENT_DEV_BUS_NAME,
+ IFLA_GRO_MAX_SIZE,
+ IFLA_TSO_MAX_SIZE,
+ IFLA_TSO_MAX_SEGS,
+ IFLA_ALLMULTI, /* Allmulti count: > 0 means acts ALLMULTI */
+
+ IFLA_DEVLINK_PORT,
+
+ IFLA_GSO_IPV4_MAX_SIZE,
+ IFLA_GRO_IPV4_MAX_SIZE,
+ IFLA_DPLL_PIN,
+ __IFLA_MAX
+};
+
+
+#define IFLA_MAX (__IFLA_MAX - 1)
+
+enum {
+ IFLA_PROTO_DOWN_REASON_UNSPEC,
+ IFLA_PROTO_DOWN_REASON_MASK, /* u32, mask for reason bits */
+ IFLA_PROTO_DOWN_REASON_VALUE, /* u32, reason bit value */
+
+ __IFLA_PROTO_DOWN_REASON_CNT,
+ IFLA_PROTO_DOWN_REASON_MAX = __IFLA_PROTO_DOWN_REASON_CNT - 1
+};
+
+/* backwards compatibility for userspace */
+#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+
+enum {
+ IFLA_INET_UNSPEC,
+ IFLA_INET_CONF,
+ __IFLA_INET_MAX,
+};
+
+#define IFLA_INET_MAX (__IFLA_INET_MAX - 1)
+
+/* ifi_flags.
+
+ IFF_* flags.
+
+ The only change is:
+ IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are
+ more not changeable by user. They describe link media
+ characteristics and set by device driver.
+
+ Comments:
+ - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid
+ - If neither of these three flags are set;
+ the interface is NBMA.
+
+ - IFF_MULTICAST does not mean anything special:
+ multicasts can be used on all not-NBMA links.
+ IFF_MULTICAST means that this media uses special encapsulation
+ for multicast frames. Apparently, all IFF_POINTOPOINT and
+ IFF_BROADCAST devices are able to use multicasts too.
+ */
+
+/* IFLA_LINK.
+ For usual devices it is equal ifi_index.
+ If it is a "virtual interface" (f.e. tunnel), ifi_link
+ can point to real physical interface (f.e. for bandwidth calculations),
+ or maybe 0, what means, that real media is unknown (usual
+ for IPIP tunnels, when route to endpoint is allowed to change)
+ */
+
+/* Subtype attributes for IFLA_PROTINFO */
+enum {
+ IFLA_INET6_UNSPEC,
+ IFLA_INET6_FLAGS, /* link flags */
+ IFLA_INET6_CONF, /* sysctl parameters */
+ IFLA_INET6_STATS, /* statistics */
+ IFLA_INET6_MCAST, /* MC things. What of them? */
+ IFLA_INET6_CACHEINFO, /* time values and max reasm size */
+ IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */
+ IFLA_INET6_TOKEN, /* device token */
+ IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */
+ IFLA_INET6_RA_MTU, /* mtu carried in the RA message */
+ __IFLA_INET6_MAX
+};
+
+#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
+
+enum in6_addr_gen_mode {
+ IN6_ADDR_GEN_MODE_EUI64,
+ IN6_ADDR_GEN_MODE_NONE,
+ IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+ IN6_ADDR_GEN_MODE_RANDOM,
+};
+
+/* Bridge section */
+
+enum {
+ IFLA_BR_UNSPEC,
+ IFLA_BR_FORWARD_DELAY,
+ IFLA_BR_HELLO_TIME,
+ IFLA_BR_MAX_AGE,
+ IFLA_BR_AGEING_TIME,
+ IFLA_BR_STP_STATE,
+ IFLA_BR_PRIORITY,
+ IFLA_BR_VLAN_FILTERING,
+ IFLA_BR_VLAN_PROTOCOL,
+ IFLA_BR_GROUP_FWD_MASK,
+ IFLA_BR_ROOT_ID,
+ IFLA_BR_BRIDGE_ID,
+ IFLA_BR_ROOT_PORT,
+ IFLA_BR_ROOT_PATH_COST,
+ IFLA_BR_TOPOLOGY_CHANGE,
+ IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+ IFLA_BR_HELLO_TIMER,
+ IFLA_BR_TCN_TIMER,
+ IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+ IFLA_BR_GC_TIMER,
+ IFLA_BR_GROUP_ADDR,
+ IFLA_BR_FDB_FLUSH,
+ IFLA_BR_MCAST_ROUTER,
+ IFLA_BR_MCAST_SNOOPING,
+ IFLA_BR_MCAST_QUERY_USE_IFADDR,
+ IFLA_BR_MCAST_QUERIER,
+ IFLA_BR_MCAST_HASH_ELASTICITY,
+ IFLA_BR_MCAST_HASH_MAX,
+ IFLA_BR_MCAST_LAST_MEMBER_CNT,
+ IFLA_BR_MCAST_STARTUP_QUERY_CNT,
+ IFLA_BR_MCAST_LAST_MEMBER_INTVL,
+ IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+ IFLA_BR_MCAST_QUERIER_INTVL,
+ IFLA_BR_MCAST_QUERY_INTVL,
+ IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
+ IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
+ IFLA_BR_NF_CALL_IPTABLES,
+ IFLA_BR_NF_CALL_IP6TABLES,
+ IFLA_BR_NF_CALL_ARPTABLES,
+ IFLA_BR_VLAN_DEFAULT_PVID,
+ IFLA_BR_PAD,
+ IFLA_BR_VLAN_STATS_ENABLED,
+ IFLA_BR_MCAST_STATS_ENABLED,
+ IFLA_BR_MCAST_IGMP_VERSION,
+ IFLA_BR_MCAST_MLD_VERSION,
+ IFLA_BR_VLAN_STATS_PER_PORT,
+ IFLA_BR_MULTI_BOOLOPT,
+ IFLA_BR_MCAST_QUERIER_STATE,
+ IFLA_BR_FDB_N_LEARNED,
+ IFLA_BR_FDB_MAX_LEARNED,
+ __IFLA_BR_MAX,
+};
+
+#define IFLA_BR_MAX (__IFLA_BR_MAX - 1)
+
+struct ifla_bridge_id {
+ __u8 prio[2];
+ __u8 addr[6]; /* ETH_ALEN */
+};
+
+enum {
+ BRIDGE_MODE_UNSPEC,
+ BRIDGE_MODE_HAIRPIN,
+};
+
+enum {
+ IFLA_BRPORT_UNSPEC,
+ IFLA_BRPORT_STATE, /* Spanning tree state */
+ IFLA_BRPORT_PRIORITY, /* " priority */
+ IFLA_BRPORT_COST, /* " cost */
+ IFLA_BRPORT_MODE, /* mode (hairpin) */
+ IFLA_BRPORT_GUARD, /* bpdu guard */
+ IFLA_BRPORT_PROTECT, /* root port protection */
+ IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */
+ IFLA_BRPORT_LEARNING, /* mac learning */
+ IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */
+ IFLA_BRPORT_PROXYARP, /* proxy ARP */
+ IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */
+ IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */
+ IFLA_BRPORT_ROOT_ID, /* designated root */
+ IFLA_BRPORT_BRIDGE_ID, /* designated bridge */
+ IFLA_BRPORT_DESIGNATED_PORT,
+ IFLA_BRPORT_DESIGNATED_COST,
+ IFLA_BRPORT_ID,
+ IFLA_BRPORT_NO,
+ IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+ IFLA_BRPORT_CONFIG_PENDING,
+ IFLA_BRPORT_MESSAGE_AGE_TIMER,
+ IFLA_BRPORT_FORWARD_DELAY_TIMER,
+ IFLA_BRPORT_HOLD_TIMER,
+ IFLA_BRPORT_FLUSH,
+ IFLA_BRPORT_MULTICAST_ROUTER,
+ IFLA_BRPORT_PAD,
+ IFLA_BRPORT_MCAST_FLOOD,
+ IFLA_BRPORT_MCAST_TO_UCAST,
+ IFLA_BRPORT_VLAN_TUNNEL,
+ IFLA_BRPORT_BCAST_FLOOD,
+ IFLA_BRPORT_GROUP_FWD_MASK,
+ IFLA_BRPORT_NEIGH_SUPPRESS,
+ IFLA_BRPORT_ISOLATED,
+ IFLA_BRPORT_BACKUP_PORT,
+ IFLA_BRPORT_MRP_RING_OPEN,
+ IFLA_BRPORT_MRP_IN_OPEN,
+ IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
+ IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
+ IFLA_BRPORT_LOCKED,
+ IFLA_BRPORT_MAB,
+ IFLA_BRPORT_MCAST_N_GROUPS,
+ IFLA_BRPORT_MCAST_MAX_GROUPS,
+ IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
+ IFLA_BRPORT_BACKUP_NHID,
+ __IFLA_BRPORT_MAX
+};
+#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
+
+struct ifla_cacheinfo {
+ __u32 max_reasm_len;
+ __u32 tstamp; /* ipv6InterfaceTable updated timestamp */
+ __u32 reachable_time;
+ __u32 retrans_time;
+};
+
+enum {
+ IFLA_INFO_UNSPEC,
+ IFLA_INFO_KIND,
+ IFLA_INFO_DATA,
+ IFLA_INFO_XSTATS,
+ IFLA_INFO_SLAVE_KIND,
+ IFLA_INFO_SLAVE_DATA,
+ __IFLA_INFO_MAX,
+};
+
+#define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1)
+
+/* VLAN section */
+
+enum {
+ IFLA_VLAN_UNSPEC,
+ IFLA_VLAN_ID,
+ IFLA_VLAN_FLAGS,
+ IFLA_VLAN_EGRESS_QOS,
+ IFLA_VLAN_INGRESS_QOS,
+ IFLA_VLAN_PROTOCOL,
+ __IFLA_VLAN_MAX,
+};
+
+#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
+
+struct ifla_vlan_flags {
+ __u32 flags;
+ __u32 mask;
+};
+
+enum {
+ IFLA_VLAN_QOS_UNSPEC,
+ IFLA_VLAN_QOS_MAPPING,
+ __IFLA_VLAN_QOS_MAX
+};
+
+#define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1)
+
+struct ifla_vlan_qos_mapping {
+ __u32 from;
+ __u32 to;
+};
+
+/* MACVLAN section */
+enum {
+ IFLA_MACVLAN_UNSPEC,
+ IFLA_MACVLAN_MODE,
+ IFLA_MACVLAN_FLAGS,
+ IFLA_MACVLAN_MACADDR_MODE,
+ IFLA_MACVLAN_MACADDR,
+ IFLA_MACVLAN_MACADDR_DATA,
+ IFLA_MACVLAN_MACADDR_COUNT,
+ IFLA_MACVLAN_BC_QUEUE_LEN,
+ IFLA_MACVLAN_BC_QUEUE_LEN_USED,
+ IFLA_MACVLAN_BC_CUTOFF,
+ __IFLA_MACVLAN_MAX,
+};
+
+#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
+
+enum macvlan_mode {
+ MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */
+ MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */
+ MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */
+ MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
+ MACVLAN_MODE_SOURCE = 16,/* use source MAC address list to assign */
+};
+
+enum macvlan_macaddr_mode {
+ MACVLAN_MACADDR_ADD,
+ MACVLAN_MACADDR_DEL,
+ MACVLAN_MACADDR_FLUSH,
+ MACVLAN_MACADDR_SET,
+};
+
+#define MACVLAN_FLAG_NOPROMISC 1
+#define MACVLAN_FLAG_NODST 2 /* skip dst macvlan if matching src macvlan */
+
+/* VRF section */
+enum {
+ IFLA_VRF_UNSPEC,
+ IFLA_VRF_TABLE,
+ __IFLA_VRF_MAX
+};
+
+#define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1)
+
+enum {
+ IFLA_VRF_PORT_UNSPEC,
+ IFLA_VRF_PORT_TABLE,
+ __IFLA_VRF_PORT_MAX
+};
+
+#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1)
+
+/* MACSEC section */
+enum {
+ IFLA_MACSEC_UNSPEC,
+ IFLA_MACSEC_SCI,
+ IFLA_MACSEC_PORT,
+ IFLA_MACSEC_ICV_LEN,
+ IFLA_MACSEC_CIPHER_SUITE,
+ IFLA_MACSEC_WINDOW,
+ IFLA_MACSEC_ENCODING_SA,
+ IFLA_MACSEC_ENCRYPT,
+ IFLA_MACSEC_PROTECT,
+ IFLA_MACSEC_INC_SCI,
+ IFLA_MACSEC_ES,
+ IFLA_MACSEC_SCB,
+ IFLA_MACSEC_REPLAY_PROTECT,
+ IFLA_MACSEC_VALIDATION,
+ IFLA_MACSEC_PAD,
+ IFLA_MACSEC_OFFLOAD,
+ __IFLA_MACSEC_MAX,
+};
+
+#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1)
+
+/* XFRM section */
+enum {
+ IFLA_XFRM_UNSPEC,
+ IFLA_XFRM_LINK,
+ IFLA_XFRM_IF_ID,
+ IFLA_XFRM_COLLECT_METADATA,
+ __IFLA_XFRM_MAX
+};
+
+#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1)
+
+enum macsec_validation_type {
+ MACSEC_VALIDATE_DISABLED = 0,
+ MACSEC_VALIDATE_CHECK = 1,
+ MACSEC_VALIDATE_STRICT = 2,
+ __MACSEC_VALIDATE_END,
+ MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1,
+};
+
+enum macsec_offload {
+ MACSEC_OFFLOAD_OFF = 0,
+ MACSEC_OFFLOAD_PHY = 1,
+ MACSEC_OFFLOAD_MAC = 2,
+ __MACSEC_OFFLOAD_END,
+ MACSEC_OFFLOAD_MAX = __MACSEC_OFFLOAD_END - 1,
+};
+
+/* IPVLAN section */
+enum {
+ IFLA_IPVLAN_UNSPEC,
+ IFLA_IPVLAN_MODE,
+ IFLA_IPVLAN_FLAGS,
+ __IFLA_IPVLAN_MAX
+};
+
+#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
+
+enum ipvlan_mode {
+ IPVLAN_MODE_L2 = 0,
+ IPVLAN_MODE_L3,
+ IPVLAN_MODE_L3S,
+ IPVLAN_MODE_MAX
+};
+
+#define IPVLAN_F_PRIVATE 0x01
+#define IPVLAN_F_VEPA 0x02
+
+/* Tunnel RTM header */
+struct tunnel_msg {
+ __u8 family;
+ __u8 flags;
+ __u16 reserved2;
+ __u32 ifindex;
+};
+
+/* netkit section */
+enum netkit_action {
+ NETKIT_NEXT = -1,
+ NETKIT_PASS = 0,
+ NETKIT_DROP = 2,
+ NETKIT_REDIRECT = 7,
+};
+
+enum netkit_mode {
+ NETKIT_L2,
+ NETKIT_L3,
+};
+
+enum {
+ IFLA_NETKIT_UNSPEC,
+ IFLA_NETKIT_PEER_INFO,
+ IFLA_NETKIT_PRIMARY,
+ IFLA_NETKIT_POLICY,
+ IFLA_NETKIT_PEER_POLICY,
+ IFLA_NETKIT_MODE,
+ __IFLA_NETKIT_MAX,
+};
+#define IFLA_NETKIT_MAX (__IFLA_NETKIT_MAX - 1)
+
+/* VXLAN section */
+
+/* include statistics in the dump */
+#define TUNNEL_MSG_FLAG_STATS 0x01
+
+#define TUNNEL_MSG_VALID_USER_FLAGS TUNNEL_MSG_FLAG_STATS
+
+/* Embedded inside VXLAN_VNIFILTER_ENTRY_STATS */
+enum {
+ VNIFILTER_ENTRY_STATS_UNSPEC,
+ VNIFILTER_ENTRY_STATS_RX_BYTES,
+ VNIFILTER_ENTRY_STATS_RX_PKTS,
+ VNIFILTER_ENTRY_STATS_RX_DROPS,
+ VNIFILTER_ENTRY_STATS_RX_ERRORS,
+ VNIFILTER_ENTRY_STATS_TX_BYTES,
+ VNIFILTER_ENTRY_STATS_TX_PKTS,
+ VNIFILTER_ENTRY_STATS_TX_DROPS,
+ VNIFILTER_ENTRY_STATS_TX_ERRORS,
+ VNIFILTER_ENTRY_STATS_PAD,
+ __VNIFILTER_ENTRY_STATS_MAX
+};
+#define VNIFILTER_ENTRY_STATS_MAX (__VNIFILTER_ENTRY_STATS_MAX - 1)
+
+enum {
+ VXLAN_VNIFILTER_ENTRY_UNSPEC,
+ VXLAN_VNIFILTER_ENTRY_START,
+ VXLAN_VNIFILTER_ENTRY_END,
+ VXLAN_VNIFILTER_ENTRY_GROUP,
+ VXLAN_VNIFILTER_ENTRY_GROUP6,
+ VXLAN_VNIFILTER_ENTRY_STATS,
+ __VXLAN_VNIFILTER_ENTRY_MAX
+};
+#define VXLAN_VNIFILTER_ENTRY_MAX (__VXLAN_VNIFILTER_ENTRY_MAX - 1)
+
+enum {
+ VXLAN_VNIFILTER_UNSPEC,
+ VXLAN_VNIFILTER_ENTRY,
+ __VXLAN_VNIFILTER_MAX
+};
+#define VXLAN_VNIFILTER_MAX (__VXLAN_VNIFILTER_MAX - 1)
+
+enum {
+ IFLA_VXLAN_UNSPEC,
+ IFLA_VXLAN_ID,
+ IFLA_VXLAN_GROUP, /* group or remote address */
+ IFLA_VXLAN_LINK,
+ IFLA_VXLAN_LOCAL,
+ IFLA_VXLAN_TTL,
+ IFLA_VXLAN_TOS,
+ IFLA_VXLAN_LEARNING,
+ IFLA_VXLAN_AGEING,
+ IFLA_VXLAN_LIMIT,
+ IFLA_VXLAN_PORT_RANGE, /* source port */
+ IFLA_VXLAN_PROXY,
+ IFLA_VXLAN_RSC,
+ IFLA_VXLAN_L2MISS,
+ IFLA_VXLAN_L3MISS,
+ IFLA_VXLAN_PORT, /* destination port */
+ IFLA_VXLAN_GROUP6,
+ IFLA_VXLAN_LOCAL6,
+ IFLA_VXLAN_UDP_CSUM,
+ IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
+ IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
+ IFLA_VXLAN_REMCSUM_TX,
+ IFLA_VXLAN_REMCSUM_RX,
+ IFLA_VXLAN_GBP,
+ IFLA_VXLAN_REMCSUM_NOPARTIAL,
+ IFLA_VXLAN_COLLECT_METADATA,
+ IFLA_VXLAN_LABEL,
+ IFLA_VXLAN_GPE,
+ IFLA_VXLAN_TTL_INHERIT,
+ IFLA_VXLAN_DF,
+ IFLA_VXLAN_VNIFILTER, /* only applicable with COLLECT_METADATA mode */
+ IFLA_VXLAN_LOCALBYPASS,
+ __IFLA_VXLAN_MAX
+};
+#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
+
+struct ifla_vxlan_port_range {
+ __be16 low;
+ __be16 high;
+};
+
+enum ifla_vxlan_df {
+ VXLAN_DF_UNSET = 0,
+ VXLAN_DF_SET,
+ VXLAN_DF_INHERIT,
+ __VXLAN_DF_END,
+ VXLAN_DF_MAX = __VXLAN_DF_END - 1,
+};
+
+/* GENEVE section */
+enum {
+ IFLA_GENEVE_UNSPEC,
+ IFLA_GENEVE_ID,
+ IFLA_GENEVE_REMOTE,
+ IFLA_GENEVE_TTL,
+ IFLA_GENEVE_TOS,
+ IFLA_GENEVE_PORT, /* destination port */
+ IFLA_GENEVE_COLLECT_METADATA,
+ IFLA_GENEVE_REMOTE6,
+ IFLA_GENEVE_UDP_CSUM,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+ IFLA_GENEVE_LABEL,
+ IFLA_GENEVE_TTL_INHERIT,
+ IFLA_GENEVE_DF,
+ IFLA_GENEVE_INNER_PROTO_INHERIT,
+ __IFLA_GENEVE_MAX
+};
+#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
+
+enum ifla_geneve_df {
+ GENEVE_DF_UNSET = 0,
+ GENEVE_DF_SET,
+ GENEVE_DF_INHERIT,
+ __GENEVE_DF_END,
+ GENEVE_DF_MAX = __GENEVE_DF_END - 1,
+};
+
+/* Bareudp section */
+enum {
+ IFLA_BAREUDP_UNSPEC,
+ IFLA_BAREUDP_PORT,
+ IFLA_BAREUDP_ETHERTYPE,
+ IFLA_BAREUDP_SRCPORT_MIN,
+ IFLA_BAREUDP_MULTIPROTO_MODE,
+ __IFLA_BAREUDP_MAX
+};
+
+#define IFLA_BAREUDP_MAX (__IFLA_BAREUDP_MAX - 1)
+
+/* PPP section */
+enum {
+ IFLA_PPP_UNSPEC,
+ IFLA_PPP_DEV_FD,
+ __IFLA_PPP_MAX
+};
+#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1)
+
+/* GTP section */
+
+enum ifla_gtp_role {
+ GTP_ROLE_GGSN = 0,
+ GTP_ROLE_SGSN,
+};
+
+enum {
+ IFLA_GTP_UNSPEC,
+ IFLA_GTP_FD0,
+ IFLA_GTP_FD1,
+ IFLA_GTP_PDP_HASHSIZE,
+ IFLA_GTP_ROLE,
+ IFLA_GTP_CREATE_SOCKETS,
+ IFLA_GTP_RESTART_COUNT,
+ __IFLA_GTP_MAX,
+};
+#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
+
+/* Bonding section */
+
+enum {
+ IFLA_BOND_UNSPEC,
+ IFLA_BOND_MODE,
+ IFLA_BOND_ACTIVE_SLAVE,
+ IFLA_BOND_MIIMON,
+ IFLA_BOND_UPDELAY,
+ IFLA_BOND_DOWNDELAY,
+ IFLA_BOND_USE_CARRIER,
+ IFLA_BOND_ARP_INTERVAL,
+ IFLA_BOND_ARP_IP_TARGET,
+ IFLA_BOND_ARP_VALIDATE,
+ IFLA_BOND_ARP_ALL_TARGETS,
+ IFLA_BOND_PRIMARY,
+ IFLA_BOND_PRIMARY_RESELECT,
+ IFLA_BOND_FAIL_OVER_MAC,
+ IFLA_BOND_XMIT_HASH_POLICY,
+ IFLA_BOND_RESEND_IGMP,
+ IFLA_BOND_NUM_PEER_NOTIF,
+ IFLA_BOND_ALL_SLAVES_ACTIVE,
+ IFLA_BOND_MIN_LINKS,
+ IFLA_BOND_LP_INTERVAL,
+ IFLA_BOND_PACKETS_PER_SLAVE,
+ IFLA_BOND_AD_LACP_RATE,
+ IFLA_BOND_AD_SELECT,
+ IFLA_BOND_AD_INFO,
+ IFLA_BOND_AD_ACTOR_SYS_PRIO,
+ IFLA_BOND_AD_USER_PORT_KEY,
+ IFLA_BOND_AD_ACTOR_SYSTEM,
+ IFLA_BOND_TLB_DYNAMIC_LB,
+ IFLA_BOND_PEER_NOTIF_DELAY,
+ IFLA_BOND_AD_LACP_ACTIVE,
+ IFLA_BOND_MISSED_MAX,
+ IFLA_BOND_NS_IP6_TARGET,
+ __IFLA_BOND_MAX,
+};
+
+#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1)
+
+enum {
+ IFLA_BOND_AD_INFO_UNSPEC,
+ IFLA_BOND_AD_INFO_AGGREGATOR,
+ IFLA_BOND_AD_INFO_NUM_PORTS,
+ IFLA_BOND_AD_INFO_ACTOR_KEY,
+ IFLA_BOND_AD_INFO_PARTNER_KEY,
+ IFLA_BOND_AD_INFO_PARTNER_MAC,
+ __IFLA_BOND_AD_INFO_MAX,
+};
+
+#define IFLA_BOND_AD_INFO_MAX (__IFLA_BOND_AD_INFO_MAX - 1)
+
+enum {
+ IFLA_BOND_SLAVE_UNSPEC,
+ IFLA_BOND_SLAVE_STATE,
+ IFLA_BOND_SLAVE_MII_STATUS,
+ IFLA_BOND_SLAVE_LINK_FAILURE_COUNT,
+ IFLA_BOND_SLAVE_PERM_HWADDR,
+ IFLA_BOND_SLAVE_QUEUE_ID,
+ IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
+ IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE,
+ IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE,
+ IFLA_BOND_SLAVE_PRIO,
+ __IFLA_BOND_SLAVE_MAX,
+};
+
+#define IFLA_BOND_SLAVE_MAX (__IFLA_BOND_SLAVE_MAX - 1)
+
+/* SR-IOV virtual function management section */
+
+enum {
+ IFLA_VF_INFO_UNSPEC,
+ IFLA_VF_INFO,
+ __IFLA_VF_INFO_MAX,
+};
+
+#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1)
+
+enum {
+ IFLA_VF_UNSPEC,
+ IFLA_VF_MAC, /* Hardware queue specific attributes */
+ IFLA_VF_VLAN, /* VLAN ID and QoS */
+ IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */
+ IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */
+ IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */
+ IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */
+ IFLA_VF_RSS_QUERY_EN, /* RSS Redirection Table and Hash Key query
+ * on/off switch
+ */
+ IFLA_VF_STATS, /* network device statistics */
+ IFLA_VF_TRUST, /* Trust VF */
+ IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */
+ IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */
+ IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */
+ IFLA_VF_BROADCAST, /* VF broadcast */
+ __IFLA_VF_MAX,
+};
+
+#define IFLA_VF_MAX (__IFLA_VF_MAX - 1)
+
+struct ifla_vf_mac {
+ __u32 vf;
+ __u8 mac[32]; /* MAX_ADDR_LEN */
+};
+
+struct ifla_vf_broadcast {
+ __u8 broadcast[32];
+};
+
+struct ifla_vf_vlan {
+ __u32 vf;
+ __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
+ __u32 qos;
+};
+
+enum {
+ IFLA_VF_VLAN_INFO_UNSPEC,
+ IFLA_VF_VLAN_INFO, /* VLAN ID, QoS and VLAN protocol */
+ __IFLA_VF_VLAN_INFO_MAX,
+};
+
+#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1)
+#define MAX_VLAN_LIST_LEN 1
+
+struct ifla_vf_vlan_info {
+ __u32 vf;
+ __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
+ __u32 qos;
+ __be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */
+};
+
+struct ifla_vf_tx_rate {
+ __u32 vf;
+ __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
+};
+
+struct ifla_vf_rate {
+ __u32 vf;
+ __u32 min_tx_rate; /* Min Bandwidth in Mbps */
+ __u32 max_tx_rate; /* Max Bandwidth in Mbps */
+};
+
+struct ifla_vf_spoofchk {
+ __u32 vf;
+ __u32 setting;
+};
+
+struct ifla_vf_guid {
+ __u32 vf;
+ __u64 guid;
+};
+
+enum {
+ IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */
+ IFLA_VF_LINK_STATE_ENABLE, /* link always up */
+ IFLA_VF_LINK_STATE_DISABLE, /* link always down */
+ __IFLA_VF_LINK_STATE_MAX,
+};
+
+struct ifla_vf_link_state {
+ __u32 vf;
+ __u32 link_state;
+};
+
+struct ifla_vf_rss_query_en {
+ __u32 vf;
+ __u32 setting;
+};
+
+enum {
+ IFLA_VF_STATS_RX_PACKETS,
+ IFLA_VF_STATS_TX_PACKETS,
+ IFLA_VF_STATS_RX_BYTES,
+ IFLA_VF_STATS_TX_BYTES,
+ IFLA_VF_STATS_BROADCAST,
+ IFLA_VF_STATS_MULTICAST,
+ IFLA_VF_STATS_PAD,
+ IFLA_VF_STATS_RX_DROPPED,
+ IFLA_VF_STATS_TX_DROPPED,
+ __IFLA_VF_STATS_MAX,
+};
+
+#define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1)
+
+struct ifla_vf_trust {
+ __u32 vf;
+ __u32 setting;
+};
+
+/* VF ports management section
+ *
+ * Nested layout of set/get msg is:
+ *
+ * [IFLA_NUM_VF]
+ * [IFLA_VF_PORTS]
+ * [IFLA_VF_PORT]
+ * [IFLA_PORT_*], ...
+ * [IFLA_VF_PORT]
+ * [IFLA_PORT_*], ...
+ * ...
+ * [IFLA_PORT_SELF]
+ * [IFLA_PORT_*], ...
+ */
+
+enum {
+ IFLA_VF_PORT_UNSPEC,
+ IFLA_VF_PORT, /* nest */
+ __IFLA_VF_PORT_MAX,
+};
+
+#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1)
+
+enum {
+ IFLA_PORT_UNSPEC,
+ IFLA_PORT_VF, /* __u32 */
+ IFLA_PORT_PROFILE, /* string */
+ IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */
+ IFLA_PORT_INSTANCE_UUID, /* binary UUID */
+ IFLA_PORT_HOST_UUID, /* binary UUID */
+ IFLA_PORT_REQUEST, /* __u8 */
+ IFLA_PORT_RESPONSE, /* __u16, output only */
+ __IFLA_PORT_MAX,
+};
+
+#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1)
+
+#define PORT_PROFILE_MAX 40
+#define PORT_UUID_MAX 16
+#define PORT_SELF_VF -1
+
+enum {
+ PORT_REQUEST_PREASSOCIATE = 0,
+ PORT_REQUEST_PREASSOCIATE_RR,
+ PORT_REQUEST_ASSOCIATE,
+ PORT_REQUEST_DISASSOCIATE,
+};
+
+enum {
+ PORT_VDP_RESPONSE_SUCCESS = 0,
+ PORT_VDP_RESPONSE_INVALID_FORMAT,
+ PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES,
+ PORT_VDP_RESPONSE_UNUSED_VTID,
+ PORT_VDP_RESPONSE_VTID_VIOLATION,
+ PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION,
+ PORT_VDP_RESPONSE_OUT_OF_SYNC,
+ /* 0x08-0xFF reserved for future VDP use */
+ PORT_PROFILE_RESPONSE_SUCCESS = 0x100,
+ PORT_PROFILE_RESPONSE_INPROGRESS,
+ PORT_PROFILE_RESPONSE_INVALID,
+ PORT_PROFILE_RESPONSE_BADSTATE,
+ PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES,
+ PORT_PROFILE_RESPONSE_ERROR,
+};
+
+struct ifla_port_vsi {
+ __u8 vsi_mgr_id;
+ __u8 vsi_type_id[3];
+ __u8 vsi_type_version;
+ __u8 pad[3];
+};
+
+
+/* IPoIB section */
+
+enum {
+ IFLA_IPOIB_UNSPEC,
+ IFLA_IPOIB_PKEY,
+ IFLA_IPOIB_MODE,
+ IFLA_IPOIB_UMCAST,
+ __IFLA_IPOIB_MAX
+};
+
+enum {
+ IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */
+ IPOIB_MODE_CONNECTED = 1, /* using connected QPs */
+};
+
+#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
+
+
+/* HSR/PRP section, both uses same interface */
+
+/* Different redundancy protocols for hsr device */
+enum {
+ HSR_PROTOCOL_HSR,
+ HSR_PROTOCOL_PRP,
+ HSR_PROTOCOL_MAX,
+};
+
+enum {
+ IFLA_HSR_UNSPEC,
+ IFLA_HSR_SLAVE1,
+ IFLA_HSR_SLAVE2,
+ IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */
+ IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */
+ IFLA_HSR_SEQ_NR,
+ IFLA_HSR_VERSION, /* HSR version */
+ IFLA_HSR_PROTOCOL, /* Indicate different protocol than
+ * HSR. For example PRP.
+ */
+ __IFLA_HSR_MAX,
+};
+
+#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1)
+
+/* STATS section */
+
+struct if_stats_msg {
+ __u8 family;
+ __u8 pad1;
+ __u16 pad2;
+ __u32 ifindex;
+ __u32 filter_mask;
+};
+
+/* A stats attribute can be netdev specific or a global stat.
+ * For netdev stats, lets use the prefix IFLA_STATS_LINK_*
+ */
+enum {
+ IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */
+ IFLA_STATS_LINK_64,
+ IFLA_STATS_LINK_XSTATS,
+ IFLA_STATS_LINK_XSTATS_SLAVE,
+ IFLA_STATS_LINK_OFFLOAD_XSTATS,
+ IFLA_STATS_AF_SPEC,
+ __IFLA_STATS_MAX,
+};
+
+#define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1)
+
+#define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1))
+
+enum {
+ IFLA_STATS_GETSET_UNSPEC,
+ IFLA_STATS_GET_FILTERS, /* Nest of IFLA_STATS_LINK_xxx, each a u32 with
+ * a filter mask for the corresponding group.
+ */
+ IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS, /* 0 or 1 as u8 */
+ __IFLA_STATS_GETSET_MAX,
+};
+
+#define IFLA_STATS_GETSET_MAX (__IFLA_STATS_GETSET_MAX - 1)
+
+/* These are embedded into IFLA_STATS_LINK_XSTATS:
+ * [IFLA_STATS_LINK_XSTATS]
+ * -> [LINK_XSTATS_TYPE_xxx]
+ * -> [rtnl link type specific attributes]
+ */
+enum {
+ LINK_XSTATS_TYPE_UNSPEC,
+ LINK_XSTATS_TYPE_BRIDGE,
+ LINK_XSTATS_TYPE_BOND,
+ __LINK_XSTATS_TYPE_MAX
+};
+#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
+
+/* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */
+enum {
+ IFLA_OFFLOAD_XSTATS_UNSPEC,
+ IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */
+ IFLA_OFFLOAD_XSTATS_HW_S_INFO, /* HW stats info. A nest */
+ IFLA_OFFLOAD_XSTATS_L3_STATS, /* struct rtnl_hw_stats64 */
+ __IFLA_OFFLOAD_XSTATS_MAX
+};
+#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1)
+
+enum {
+ IFLA_OFFLOAD_XSTATS_HW_S_INFO_UNSPEC,
+ IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST, /* u8 */
+ IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED, /* u8 */
+ __IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX,
+};
+#define IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX \
+ (__IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX - 1)
+
+/* XDP section */
+
+#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0)
+#define XDP_FLAGS_SKB_MODE (1U << 1)
+#define XDP_FLAGS_DRV_MODE (1U << 2)
+#define XDP_FLAGS_HW_MODE (1U << 3)
+#define XDP_FLAGS_REPLACE (1U << 4)
+#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \
+ XDP_FLAGS_DRV_MODE | \
+ XDP_FLAGS_HW_MODE)
+#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \
+ XDP_FLAGS_MODES | XDP_FLAGS_REPLACE)
+
+/* These are stored into IFLA_XDP_ATTACHED on dump. */
+enum {
+ XDP_ATTACHED_NONE = 0,
+ XDP_ATTACHED_DRV,
+ XDP_ATTACHED_SKB,
+ XDP_ATTACHED_HW,
+ XDP_ATTACHED_MULTI,
+};
+
+enum {
+ IFLA_XDP_UNSPEC,
+ IFLA_XDP_FD,
+ IFLA_XDP_ATTACHED,
+ IFLA_XDP_FLAGS,
+ IFLA_XDP_PROG_ID,
+ IFLA_XDP_DRV_PROG_ID,
+ IFLA_XDP_SKB_PROG_ID,
+ IFLA_XDP_HW_PROG_ID,
+ IFLA_XDP_EXPECTED_FD,
+ __IFLA_XDP_MAX,
+};
+
+#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1)
+
+enum {
+ IFLA_EVENT_NONE,
+ IFLA_EVENT_REBOOT, /* internal reset / reboot */
+ IFLA_EVENT_FEATURES, /* change in offload features */
+ IFLA_EVENT_BONDING_FAILOVER, /* change in active slave */
+ IFLA_EVENT_NOTIFY_PEERS, /* re-sent grat. arp/ndisc */
+ IFLA_EVENT_IGMP_RESEND, /* re-sent IGMP JOIN */
+ IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */
+};
+
+/* tun section */
+
+enum {
+ IFLA_TUN_UNSPEC,
+ IFLA_TUN_OWNER,
+ IFLA_TUN_GROUP,
+ IFLA_TUN_TYPE,
+ IFLA_TUN_PI,
+ IFLA_TUN_VNET_HDR,
+ IFLA_TUN_PERSIST,
+ IFLA_TUN_MULTI_QUEUE,
+ IFLA_TUN_NUM_QUEUES,
+ IFLA_TUN_NUM_DISABLED_QUEUES,
+ __IFLA_TUN_MAX,
+};
+
+#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1)
+
+/* rmnet section */
+
+#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0)
+#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1)
+#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2)
+#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3)
+#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 4)
+#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 5)
+
+enum {
+ IFLA_RMNET_UNSPEC,
+ IFLA_RMNET_MUX_ID,
+ IFLA_RMNET_FLAGS,
+ __IFLA_RMNET_MAX,
+};
+
+#define IFLA_RMNET_MAX (__IFLA_RMNET_MAX - 1)
+
+struct ifla_rmnet_flags {
+ __u32 flags;
+ __u32 mask;
+};
+
+/* MCTP section */
+
+enum {
+ IFLA_MCTP_UNSPEC,
+ IFLA_MCTP_NET,
+ __IFLA_MCTP_MAX,
+};
+
+#define IFLA_MCTP_MAX (__IFLA_MCTP_MAX - 1)
+
+/* DSA section */
+
+enum {
+ IFLA_DSA_UNSPEC,
+ IFLA_DSA_CONDUIT,
+ /* Deprecated, use IFLA_DSA_CONDUIT instead */
+ IFLA_DSA_MASTER = IFLA_DSA_CONDUIT,
+ __IFLA_DSA_MAX,
+};
+
+#define IFLA_DSA_MAX (__IFLA_DSA_MAX - 1)
+
+#endif /* _LINUX_IF_LINK_H */
diff --git a/uapi/linux/libc-compat.h b/uapi/linux/libc-compat.h
new file mode 100644
index 0000000..a159991
--- /dev/null
+++ b/uapi/linux/libc-compat.h
@@ -0,0 +1,267 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Compatibility interface for userspace libc header coordination:
+ *
+ * Define compatibility macros that are used to control the inclusion or
+ * exclusion of UAPI structures and definitions in coordination with another
+ * userspace C library.
+ *
+ * This header is intended to solve the problem of UAPI definitions that
+ * conflict with userspace definitions. If a UAPI header has such conflicting
+ * definitions then the solution is as follows:
+ *
+ * * Synchronize the UAPI header and the libc headers so either one can be
+ * used and such that the ABI is preserved. If this is not possible then
+ * no simple compatibility interface exists (you need to write translating
+ * wrappers and rename things) and you can't use this interface.
+ *
+ * Then follow this process:
+ *
+ * (a) Include libc-compat.h in the UAPI header.
+ * e.g. #include <linux/libc-compat.h>
+ * This include must be as early as possible.
+ *
+ * (b) In libc-compat.h add enough code to detect that the comflicting
+ * userspace libc header has been included first.
+ *
+ * (c) If the userspace libc header has been included first define a set of
+ * guard macros of the form __UAPI_DEF_FOO and set their values to 1, else
+ * set their values to 0.
+ *
+ * (d) Back in the UAPI header with the conflicting definitions, guard the
+ * definitions with:
+ * #if __UAPI_DEF_FOO
+ * ...
+ * #endif
+ *
+ * This fixes the situation where the linux headers are included *after* the
+ * libc headers. To fix the problem with the inclusion in the other order the
+ * userspace libc headers must be fixed like this:
+ *
+ * * For all definitions that conflict with kernel definitions wrap those
+ * defines in the following:
+ * #if !__UAPI_DEF_FOO
+ * ...
+ * #endif
+ *
+ * This prevents the redefinition of a construct already defined by the kernel.
+ */
+#ifndef _LIBC_COMPAT_H
+#define _LIBC_COMPAT_H
+
+/* We have included glibc headers... */
+#if defined(__GLIBC__)
+
+/* Coordinate with glibc net/if.h header. */
+#if defined(_NET_IF_H) && defined(__USE_MISC)
+
+/* GLIBC headers included first so don't define anything
+ * that would already be defined. */
+
+#define __UAPI_DEF_IF_IFCONF 0
+#define __UAPI_DEF_IF_IFMAP 0
+#define __UAPI_DEF_IF_IFNAMSIZ 0
+#define __UAPI_DEF_IF_IFREQ 0
+/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */
+#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 0
+/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */
+#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
+#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1
+#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */
+
+#else /* _NET_IF_H */
+
+/* Linux headers included first, and we must define everything
+ * we need. The expectation is that glibc will check the
+ * __UAPI_DEF_* defines and adjust appropriately. */
+
+#define __UAPI_DEF_IF_IFCONF 1
+#define __UAPI_DEF_IF_IFMAP 1
+#define __UAPI_DEF_IF_IFNAMSIZ 1
+#define __UAPI_DEF_IF_IFREQ 1
+/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */
+#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1
+/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */
+#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1
+
+#endif /* _NET_IF_H */
+
+/* Coordinate with glibc netinet/in.h header. */
+#if defined(_NETINET_IN_H)
+
+/* GLIBC headers included first so don't define anything
+ * that would already be defined. */
+#define __UAPI_DEF_IN_ADDR 0
+#define __UAPI_DEF_IN_IPPROTO 0
+#define __UAPI_DEF_IN_PKTINFO 0
+#define __UAPI_DEF_IP_MREQ 0
+#define __UAPI_DEF_SOCKADDR_IN 0
+#define __UAPI_DEF_IN_CLASS 0
+
+#define __UAPI_DEF_IN6_ADDR 0
+/* The exception is the in6_addr macros which must be defined
+ * if the glibc code didn't define them. This guard matches
+ * the guard in glibc/inet/netinet/in.h which defines the
+ * additional in6_addr macros e.g. s6_addr16, and s6_addr32. */
+#if defined(__USE_MISC) || defined (__USE_GNU)
+#define __UAPI_DEF_IN6_ADDR_ALT 0
+#else
+#define __UAPI_DEF_IN6_ADDR_ALT 1
+#endif
+#define __UAPI_DEF_SOCKADDR_IN6 0
+#define __UAPI_DEF_IPV6_MREQ 0
+#define __UAPI_DEF_IPPROTO_V6 0
+#define __UAPI_DEF_IPV6_OPTIONS 0
+#define __UAPI_DEF_IN6_PKTINFO 0
+#define __UAPI_DEF_IP6_MTUINFO 0
+
+#else
+
+/* Linux headers included first, and we must define everything
+ * we need. The expectation is that glibc will check the
+ * __UAPI_DEF_* defines and adjust appropriately. */
+#define __UAPI_DEF_IN_ADDR 1
+#define __UAPI_DEF_IN_IPPROTO 1
+#define __UAPI_DEF_IN_PKTINFO 1
+#define __UAPI_DEF_IP_MREQ 1
+#define __UAPI_DEF_SOCKADDR_IN 1
+#define __UAPI_DEF_IN_CLASS 1
+
+#define __UAPI_DEF_IN6_ADDR 1
+/* We unconditionally define the in6_addr macros and glibc must
+ * coordinate. */
+#define __UAPI_DEF_IN6_ADDR_ALT 1
+#define __UAPI_DEF_SOCKADDR_IN6 1
+#define __UAPI_DEF_IPV6_MREQ 1
+#define __UAPI_DEF_IPPROTO_V6 1
+#define __UAPI_DEF_IPV6_OPTIONS 1
+#define __UAPI_DEF_IN6_PKTINFO 1
+#define __UAPI_DEF_IP6_MTUINFO 1
+
+#endif /* _NETINET_IN_H */
+
+/* Coordinate with glibc netipx/ipx.h header. */
+#if defined(__NETIPX_IPX_H)
+
+#define __UAPI_DEF_SOCKADDR_IPX 0
+#define __UAPI_DEF_IPX_ROUTE_DEFINITION 0
+#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 0
+#define __UAPI_DEF_IPX_CONFIG_DATA 0
+#define __UAPI_DEF_IPX_ROUTE_DEF 0
+
+#else /* defined(__NETIPX_IPX_H) */
+
+#define __UAPI_DEF_SOCKADDR_IPX 1
+#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1
+#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1
+#define __UAPI_DEF_IPX_CONFIG_DATA 1
+#define __UAPI_DEF_IPX_ROUTE_DEF 1
+
+#endif /* defined(__NETIPX_IPX_H) */
+
+/* Definitions for xattr.h */
+#if defined(_SYS_XATTR_H)
+#define __UAPI_DEF_XATTR 0
+#else
+#define __UAPI_DEF_XATTR 1
+#endif
+
+/* If we did not see any headers from any supported C libraries,
+ * or we are being included in the kernel, then define everything
+ * that we need. Check for previous __UAPI_* definitions to give
+ * unsupported C libraries a way to opt out of any kernel definition. */
+#else /* !defined(__GLIBC__) */
+
+/* Definitions for if.h */
+#ifndef __UAPI_DEF_IF_IFCONF
+#define __UAPI_DEF_IF_IFCONF 1
+#endif
+#ifndef __UAPI_DEF_IF_IFMAP
+#define __UAPI_DEF_IF_IFMAP 1
+#endif
+#ifndef __UAPI_DEF_IF_IFNAMSIZ
+#define __UAPI_DEF_IF_IFNAMSIZ 1
+#endif
+#ifndef __UAPI_DEF_IF_IFREQ
+#define __UAPI_DEF_IF_IFREQ 1
+#endif
+/* Everything up to IFF_DYNAMIC, matches net/if.h until glibc 2.23 */
+#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS
+#define __UAPI_DEF_IF_NET_DEVICE_FLAGS 1
+#endif
+/* For the future if glibc adds IFF_LOWER_UP, IFF_DORMANT and IFF_ECHO */
+#ifndef __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
+#define __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO 1
+#endif
+
+/* Definitions for in.h */
+#ifndef __UAPI_DEF_IN_ADDR
+#define __UAPI_DEF_IN_ADDR 1
+#endif
+#ifndef __UAPI_DEF_IN_IPPROTO
+#define __UAPI_DEF_IN_IPPROTO 1
+#endif
+#ifndef __UAPI_DEF_IN_PKTINFO
+#define __UAPI_DEF_IN_PKTINFO 1
+#endif
+#ifndef __UAPI_DEF_IP_MREQ
+#define __UAPI_DEF_IP_MREQ 1
+#endif
+#ifndef __UAPI_DEF_SOCKADDR_IN
+#define __UAPI_DEF_SOCKADDR_IN 1
+#endif
+#ifndef __UAPI_DEF_IN_CLASS
+#define __UAPI_DEF_IN_CLASS 1
+#endif
+
+/* Definitions for in6.h */
+#ifndef __UAPI_DEF_IN6_ADDR
+#define __UAPI_DEF_IN6_ADDR 1
+#endif
+#ifndef __UAPI_DEF_IN6_ADDR_ALT
+#define __UAPI_DEF_IN6_ADDR_ALT 1
+#endif
+#ifndef __UAPI_DEF_SOCKADDR_IN6
+#define __UAPI_DEF_SOCKADDR_IN6 1
+#endif
+#ifndef __UAPI_DEF_IPV6_MREQ
+#define __UAPI_DEF_IPV6_MREQ 1
+#endif
+#ifndef __UAPI_DEF_IPPROTO_V6
+#define __UAPI_DEF_IPPROTO_V6 1
+#endif
+#ifndef __UAPI_DEF_IPV6_OPTIONS
+#define __UAPI_DEF_IPV6_OPTIONS 1
+#endif
+#ifndef __UAPI_DEF_IN6_PKTINFO
+#define __UAPI_DEF_IN6_PKTINFO 1
+#endif
+#ifndef __UAPI_DEF_IP6_MTUINFO
+#define __UAPI_DEF_IP6_MTUINFO 1
+#endif
+
+/* Definitions for ipx.h */
+#ifndef __UAPI_DEF_SOCKADDR_IPX
+#define __UAPI_DEF_SOCKADDR_IPX 1
+#endif
+#ifndef __UAPI_DEF_IPX_ROUTE_DEFINITION
+#define __UAPI_DEF_IPX_ROUTE_DEFINITION 1
+#endif
+#ifndef __UAPI_DEF_IPX_INTERFACE_DEFINITION
+#define __UAPI_DEF_IPX_INTERFACE_DEFINITION 1
+#endif
+#ifndef __UAPI_DEF_IPX_CONFIG_DATA
+#define __UAPI_DEF_IPX_CONFIG_DATA 1
+#endif
+#ifndef __UAPI_DEF_IPX_ROUTE_DEF
+#define __UAPI_DEF_IPX_ROUTE_DEF 1
+#endif
+
+/* Definitions for xattr.h */
+#ifndef __UAPI_DEF_XATTR
+#define __UAPI_DEF_XATTR 1
+#endif
+
+#endif /* __GLIBC__ */
+
+#endif /* _LIBC_COMPAT_H */
diff --git a/uapi/linux/neighbour.h b/uapi/linux/neighbour.h
new file mode 100644
index 0000000..5e67a7e
--- /dev/null
+++ b/uapi/linux/neighbour.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_NEIGHBOUR_H
+#define __LINUX_NEIGHBOUR_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+struct ndmsg {
+ __u8 ndm_family;
+ __u8 ndm_pad1;
+ __u16 ndm_pad2;
+ __s32 ndm_ifindex;
+ __u16 ndm_state;
+ __u8 ndm_flags;
+ __u8 ndm_type;
+};
+
+enum {
+ NDA_UNSPEC,
+ NDA_DST,
+ NDA_LLADDR,
+ NDA_CACHEINFO,
+ NDA_PROBES,
+ NDA_VLAN,
+ NDA_PORT,
+ NDA_VNI,
+ NDA_IFINDEX,
+ NDA_MASTER,
+ NDA_LINK_NETNSID,
+ NDA_SRC_VNI,
+ NDA_PROTOCOL, /* Originator of entry */
+ NDA_NH_ID,
+ NDA_FDB_EXT_ATTRS,
+ NDA_FLAGS_EXT,
+ NDA_NDM_STATE_MASK,
+ NDA_NDM_FLAGS_MASK,
+ __NDA_MAX
+};
+
+#define NDA_MAX (__NDA_MAX - 1)
+
+/*
+ * Neighbor Cache Entry Flags
+ */
+
+#define NTF_USE (1 << 0)
+#define NTF_SELF (1 << 1)
+#define NTF_MASTER (1 << 2)
+#define NTF_PROXY (1 << 3) /* == ATF_PUBL */
+#define NTF_EXT_LEARNED (1 << 4)
+#define NTF_OFFLOADED (1 << 5)
+#define NTF_STICKY (1 << 6)
+#define NTF_ROUTER (1 << 7)
+/* Extended flags under NDA_FLAGS_EXT: */
+#define NTF_EXT_MANAGED (1 << 0)
+#define NTF_EXT_LOCKED (1 << 1)
+
+/*
+ * Neighbor Cache Entry States.
+ */
+
+#define NUD_INCOMPLETE 0x01
+#define NUD_REACHABLE 0x02
+#define NUD_STALE 0x04
+#define NUD_DELAY 0x08
+#define NUD_PROBE 0x10
+#define NUD_FAILED 0x20
+
+/* Dummy states */
+#define NUD_NOARP 0x40
+#define NUD_PERMANENT 0x80
+#define NUD_NONE 0x00
+
+/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change and make no
+ * address resolution or NUD.
+ *
+ * NUD_PERMANENT also cannot be deleted by garbage collectors. This holds true
+ * for dynamic entries with NTF_EXT_LEARNED flag as well. However, upon carrier
+ * down event, NUD_PERMANENT entries are not flushed whereas NTF_EXT_LEARNED
+ * flagged entries explicitly are (which is also consistent with the routing
+ * subsystem).
+ *
+ * When NTF_EXT_LEARNED is set for a bridge fdb entry the different cache entry
+ * states don't make sense and thus are ignored. Such entries don't age and
+ * can roam.
+ *
+ * NTF_EXT_MANAGED flagged neigbor entries are managed by the kernel on behalf
+ * of a user space control plane, and automatically refreshed so that (if
+ * possible) they remain in NUD_REACHABLE state.
+ *
+ * NTF_EXT_LOCKED flagged bridge FDB entries are entries generated by the
+ * bridge in response to a host trying to communicate via a locked bridge port
+ * with MAB enabled. Their purpose is to notify user space that a host requires
+ * authentication.
+ */
+
+struct nda_cacheinfo {
+ __u32 ndm_confirmed;
+ __u32 ndm_used;
+ __u32 ndm_updated;
+ __u32 ndm_refcnt;
+};
+
+/*****************************************************************
+ * Neighbour tables specific messages.
+ *
+ * To retrieve the neighbour tables send RTM_GETNEIGHTBL with the
+ * NLM_F_DUMP flag set. Every neighbour table configuration is
+ * spread over multiple messages to avoid running into message
+ * size limits on systems with many interfaces. The first message
+ * in the sequence transports all not device specific data such as
+ * statistics, configuration, and the default parameter set.
+ * This message is followed by 0..n messages carrying device
+ * specific parameter sets.
+ * Although the ordering should be sufficient, NDTA_NAME can be
+ * used to identify sequences. The initial message can be identified
+ * by checking for NDTA_CONFIG. The device specific messages do
+ * not contain this TLV but have NDTPA_IFINDEX set to the
+ * corresponding interface index.
+ *
+ * To change neighbour table attributes, send RTM_SETNEIGHTBL
+ * with NDTA_NAME set. Changeable attribute include NDTA_THRESH[1-3],
+ * NDTA_GC_INTERVAL, and all TLVs in NDTA_PARMS unless marked
+ * otherwise. Device specific parameter sets can be changed by
+ * setting NDTPA_IFINDEX to the interface index of the corresponding
+ * device.
+ ****/
+
+struct ndt_stats {
+ __u64 ndts_allocs;
+ __u64 ndts_destroys;
+ __u64 ndts_hash_grows;
+ __u64 ndts_res_failed;
+ __u64 ndts_lookups;
+ __u64 ndts_hits;
+ __u64 ndts_rcv_probes_mcast;
+ __u64 ndts_rcv_probes_ucast;
+ __u64 ndts_periodic_gc_runs;
+ __u64 ndts_forced_gc_runs;
+ __u64 ndts_table_fulls;
+};
+
+enum {
+ NDTPA_UNSPEC,
+ NDTPA_IFINDEX, /* u32, unchangeable */
+ NDTPA_REFCNT, /* u32, read-only */
+ NDTPA_REACHABLE_TIME, /* u64, read-only, msecs */
+ NDTPA_BASE_REACHABLE_TIME, /* u64, msecs */
+ NDTPA_RETRANS_TIME, /* u64, msecs */
+ NDTPA_GC_STALETIME, /* u64, msecs */
+ NDTPA_DELAY_PROBE_TIME, /* u64, msecs */
+ NDTPA_QUEUE_LEN, /* u32 */
+ NDTPA_APP_PROBES, /* u32 */
+ NDTPA_UCAST_PROBES, /* u32 */
+ NDTPA_MCAST_PROBES, /* u32 */
+ NDTPA_ANYCAST_DELAY, /* u64, msecs */
+ NDTPA_PROXY_DELAY, /* u64, msecs */
+ NDTPA_PROXY_QLEN, /* u32 */
+ NDTPA_LOCKTIME, /* u64, msecs */
+ NDTPA_QUEUE_LENBYTES, /* u32 */
+ NDTPA_MCAST_REPROBES, /* u32 */
+ NDTPA_PAD,
+ NDTPA_INTERVAL_PROBE_TIME_MS, /* u64, msecs */
+ __NDTPA_MAX
+};
+#define NDTPA_MAX (__NDTPA_MAX - 1)
+
+struct ndtmsg {
+ __u8 ndtm_family;
+ __u8 ndtm_pad1;
+ __u16 ndtm_pad2;
+};
+
+struct ndt_config {
+ __u16 ndtc_key_len;
+ __u16 ndtc_entry_size;
+ __u32 ndtc_entries;
+ __u32 ndtc_last_flush; /* delta to now in msecs */
+ __u32 ndtc_last_rand; /* delta to now in msecs */
+ __u32 ndtc_hash_rnd;
+ __u32 ndtc_hash_mask;
+ __u32 ndtc_hash_chain_gc;
+ __u32 ndtc_proxy_qlen;
+};
+
+enum {
+ NDTA_UNSPEC,
+ NDTA_NAME, /* char *, unchangeable */
+ NDTA_THRESH1, /* u32 */
+ NDTA_THRESH2, /* u32 */
+ NDTA_THRESH3, /* u32 */
+ NDTA_CONFIG, /* struct ndt_config, read-only */
+ NDTA_PARMS, /* nested TLV NDTPA_* */
+ NDTA_STATS, /* struct ndt_stats, read-only */
+ NDTA_GC_INTERVAL, /* u64, msecs */
+ NDTA_PAD,
+ __NDTA_MAX
+};
+#define NDTA_MAX (__NDTA_MAX - 1)
+
+ /* FDB activity notification bits used in NFEA_ACTIVITY_NOTIFY:
+ * - FDB_NOTIFY_BIT - notify on activity/expire for any entry
+ * - FDB_NOTIFY_INACTIVE_BIT - mark as inactive to avoid multiple notifications
+ */
+enum {
+ FDB_NOTIFY_BIT = (1 << 0),
+ FDB_NOTIFY_INACTIVE_BIT = (1 << 1)
+};
+
+/* embedded into NDA_FDB_EXT_ATTRS:
+ * [NDA_FDB_EXT_ATTRS] = {
+ * [NFEA_ACTIVITY_NOTIFY]
+ * ...
+ * }
+ */
+enum {
+ NFEA_UNSPEC,
+ NFEA_ACTIVITY_NOTIFY,
+ NFEA_DONT_REFRESH,
+ __NFEA_MAX
+};
+#define NFEA_MAX (__NFEA_MAX - 1)
+
+#endif
diff --git a/uapi/linux/net_tstamp.h b/uapi/linux/net_tstamp.h
new file mode 100644
index 0000000..a2c66b3
--- /dev/null
+++ b/uapi/linux/net_tstamp.h
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Userspace API for hardware time stamping of network packets
+ *
+ * Copyright (C) 2008,2009 Intel Corporation
+ * Author: Patrick Ohly <patrick.ohly@intel.com>
+ *
+ */
+
+#ifndef _NET_TIMESTAMPING_H
+#define _NET_TIMESTAMPING_H
+
+#include <linux/types.h>
+#include <linux/socket.h> /* for SO_TIMESTAMPING */
+
+/* SO_TIMESTAMPING flags */
+enum {
+ SOF_TIMESTAMPING_TX_HARDWARE = (1<<0),
+ SOF_TIMESTAMPING_TX_SOFTWARE = (1<<1),
+ SOF_TIMESTAMPING_RX_HARDWARE = (1<<2),
+ SOF_TIMESTAMPING_RX_SOFTWARE = (1<<3),
+ SOF_TIMESTAMPING_SOFTWARE = (1<<4),
+ SOF_TIMESTAMPING_SYS_HARDWARE = (1<<5),
+ SOF_TIMESTAMPING_RAW_HARDWARE = (1<<6),
+ SOF_TIMESTAMPING_OPT_ID = (1<<7),
+ SOF_TIMESTAMPING_TX_SCHED = (1<<8),
+ SOF_TIMESTAMPING_TX_ACK = (1<<9),
+ SOF_TIMESTAMPING_OPT_CMSG = (1<<10),
+ SOF_TIMESTAMPING_OPT_TSONLY = (1<<11),
+ SOF_TIMESTAMPING_OPT_STATS = (1<<12),
+ SOF_TIMESTAMPING_OPT_PKTINFO = (1<<13),
+ SOF_TIMESTAMPING_OPT_TX_SWHW = (1<<14),
+ SOF_TIMESTAMPING_BIND_PHC = (1 << 15),
+ SOF_TIMESTAMPING_OPT_ID_TCP = (1 << 16),
+
+ SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_ID_TCP,
+ SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) |
+ SOF_TIMESTAMPING_LAST
+};
+
+/*
+ * SO_TIMESTAMPING flags are either for recording a packet timestamp or for
+ * reporting the timestamp to user space.
+ * Recording flags can be set both via socket options and control messages.
+ */
+#define SOF_TIMESTAMPING_TX_RECORD_MASK (SOF_TIMESTAMPING_TX_HARDWARE | \
+ SOF_TIMESTAMPING_TX_SOFTWARE | \
+ SOF_TIMESTAMPING_TX_SCHED | \
+ SOF_TIMESTAMPING_TX_ACK)
+
+/**
+ * struct so_timestamping - SO_TIMESTAMPING parameter
+ *
+ * @flags: SO_TIMESTAMPING flags
+ * @bind_phc: Index of PTP virtual clock bound to sock. This is available
+ * if flag SOF_TIMESTAMPING_BIND_PHC is set.
+ */
+struct so_timestamping {
+ int flags;
+ int bind_phc;
+};
+
+/**
+ * struct hwtstamp_config - %SIOCGHWTSTAMP and %SIOCSHWTSTAMP parameter
+ *
+ * @flags: one of HWTSTAMP_FLAG_*
+ * @tx_type: one of HWTSTAMP_TX_*
+ * @rx_filter: one of HWTSTAMP_FILTER_*
+ *
+ * %SIOCGHWTSTAMP and %SIOCSHWTSTAMP expect a &struct ifreq with a
+ * ifr_data pointer to this structure. For %SIOCSHWTSTAMP, if the
+ * driver or hardware does not support the requested @rx_filter value,
+ * the driver may use a more general filter mode. In this case
+ * @rx_filter will indicate the actual mode on return.
+ */
+struct hwtstamp_config {
+ int flags;
+ int tx_type;
+ int rx_filter;
+};
+
+/* possible values for hwtstamp_config->flags */
+enum hwtstamp_flags {
+ /*
+ * With this flag, the user could get bond active interface's
+ * PHC index. Note this PHC index is not stable as when there
+ * is a failover, the bond active interface will be changed, so
+ * will be the PHC index.
+ */
+ HWTSTAMP_FLAG_BONDED_PHC_INDEX = (1<<0),
+#define HWTSTAMP_FLAG_BONDED_PHC_INDEX HWTSTAMP_FLAG_BONDED_PHC_INDEX
+
+ HWTSTAMP_FLAG_LAST = HWTSTAMP_FLAG_BONDED_PHC_INDEX,
+ HWTSTAMP_FLAG_MASK = (HWTSTAMP_FLAG_LAST - 1) | HWTSTAMP_FLAG_LAST
+};
+
+/* possible values for hwtstamp_config->tx_type */
+enum hwtstamp_tx_types {
+ /*
+ * No outgoing packet will need hardware time stamping;
+ * should a packet arrive which asks for it, no hardware
+ * time stamping will be done.
+ */
+ HWTSTAMP_TX_OFF,
+
+ /*
+ * Enables hardware time stamping for outgoing packets;
+ * the sender of the packet decides which are to be
+ * time stamped by setting %SOF_TIMESTAMPING_TX_SOFTWARE
+ * before sending the packet.
+ */
+ HWTSTAMP_TX_ON,
+
+ /*
+ * Enables time stamping for outgoing packets just as
+ * HWTSTAMP_TX_ON does, but also enables time stamp insertion
+ * directly into Sync packets. In this case, transmitted Sync
+ * packets will not received a time stamp via the socket error
+ * queue.
+ */
+ HWTSTAMP_TX_ONESTEP_SYNC,
+
+ /*
+ * Same as HWTSTAMP_TX_ONESTEP_SYNC, but also enables time
+ * stamp insertion directly into PDelay_Resp packets. In this
+ * case, neither transmitted Sync nor PDelay_Resp packets will
+ * receive a time stamp via the socket error queue.
+ */
+ HWTSTAMP_TX_ONESTEP_P2P,
+
+ /* add new constants above here */
+ __HWTSTAMP_TX_CNT
+};
+
+/* possible values for hwtstamp_config->rx_filter */
+enum hwtstamp_rx_filters {
+ /* time stamp no incoming packet at all */
+ HWTSTAMP_FILTER_NONE,
+
+ /* time stamp any incoming packet */
+ HWTSTAMP_FILTER_ALL,
+
+ /* return value: time stamp all packets requested plus some others */
+ HWTSTAMP_FILTER_SOME,
+
+ /* PTP v1, UDP, any kind of event packet */
+ HWTSTAMP_FILTER_PTP_V1_L4_EVENT,
+ /* PTP v1, UDP, Sync packet */
+ HWTSTAMP_FILTER_PTP_V1_L4_SYNC,
+ /* PTP v1, UDP, Delay_req packet */
+ HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ,
+ /* PTP v2, UDP, any kind of event packet */
+ HWTSTAMP_FILTER_PTP_V2_L4_EVENT,
+ /* PTP v2, UDP, Sync packet */
+ HWTSTAMP_FILTER_PTP_V2_L4_SYNC,
+ /* PTP v2, UDP, Delay_req packet */
+ HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ,
+
+ /* 802.AS1, Ethernet, any kind of event packet */
+ HWTSTAMP_FILTER_PTP_V2_L2_EVENT,
+ /* 802.AS1, Ethernet, Sync packet */
+ HWTSTAMP_FILTER_PTP_V2_L2_SYNC,
+ /* 802.AS1, Ethernet, Delay_req packet */
+ HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ,
+
+ /* PTP v2/802.AS1, any layer, any kind of event packet */
+ HWTSTAMP_FILTER_PTP_V2_EVENT,
+ /* PTP v2/802.AS1, any layer, Sync packet */
+ HWTSTAMP_FILTER_PTP_V2_SYNC,
+ /* PTP v2/802.AS1, any layer, Delay_req packet */
+ HWTSTAMP_FILTER_PTP_V2_DELAY_REQ,
+
+ /* NTP, UDP, all versions and packet modes */
+ HWTSTAMP_FILTER_NTP_ALL,
+
+ /* add new constants above here */
+ __HWTSTAMP_FILTER_CNT
+};
+
+/* SCM_TIMESTAMPING_PKTINFO control message */
+struct scm_ts_pktinfo {
+ __u32 if_index;
+ __u32 pkt_length;
+ __u32 reserved[2];
+};
+
+/*
+ * SO_TXTIME gets a struct sock_txtime with flags being an integer bit
+ * field comprised of these values.
+ */
+enum txtime_flags {
+ SOF_TXTIME_DEADLINE_MODE = (1 << 0),
+ SOF_TXTIME_REPORT_ERRORS = (1 << 1),
+
+ SOF_TXTIME_FLAGS_LAST = SOF_TXTIME_REPORT_ERRORS,
+ SOF_TXTIME_FLAGS_MASK = (SOF_TXTIME_FLAGS_LAST - 1) |
+ SOF_TXTIME_FLAGS_LAST
+};
+
+struct sock_txtime {
+ __kernel_clockid_t clockid;/* reference clockid */
+ __u32 flags; /* as defined by enum txtime_flags */
+};
+
+#endif /* _NET_TIMESTAMPING_H */
diff --git a/uapi/linux/netlink.h b/uapi/linux/netlink.h
new file mode 100644
index 0000000..ff64eb1
--- /dev/null
+++ b/uapi/linux/netlink.h
@@ -0,0 +1,379 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+#include <linux/const.h>
+#include <linux/socket.h> /* for __kernel_sa_family_t */
+#include <linux/types.h>
+
+#define NETLINK_ROUTE 0 /* Routing/device hook */
+#define NETLINK_UNUSED 1 /* Unused number */
+#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
+#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
+#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
+#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
+#define NETLINK_XFRM 6 /* ipsec */
+#define NETLINK_SELINUX 7 /* SELinux event notifications */
+#define NETLINK_ISCSI 8 /* Open-iSCSI */
+#define NETLINK_AUDIT 9 /* auditing */
+#define NETLINK_FIB_LOOKUP 10
+#define NETLINK_CONNECTOR 11
+#define NETLINK_NETFILTER 12 /* netfilter subsystem */
+#define NETLINK_IP6_FW 13
+#define NETLINK_DNRTMSG 14 /* DECnet routing messages (obsolete) */
+#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
+#define NETLINK_GENERIC 16
+/* leave room for NETLINK_DM (DM Events) */
+#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
+#define NETLINK_ECRYPTFS 19
+#define NETLINK_RDMA 20
+#define NETLINK_CRYPTO 21 /* Crypto layer */
+#define NETLINK_SMC 22 /* SMC monitoring */
+
+#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
+
+#define MAX_LINKS 32
+
+struct sockaddr_nl {
+ __kernel_sa_family_t nl_family; /* AF_NETLINK */
+ unsigned short nl_pad; /* zero */
+ __u32 nl_pid; /* port ID */
+ __u32 nl_groups; /* multicast groups mask */
+};
+
+/**
+ * struct nlmsghdr - fixed format metadata header of Netlink messages
+ * @nlmsg_len: Length of message including header
+ * @nlmsg_type: Message content type
+ * @nlmsg_flags: Additional flags
+ * @nlmsg_seq: Sequence number
+ * @nlmsg_pid: Sending process port ID
+ */
+struct nlmsghdr {
+ __u32 nlmsg_len;
+ __u16 nlmsg_type;
+ __u16 nlmsg_flags;
+ __u32 nlmsg_seq;
+ __u32 nlmsg_pid;
+};
+
+/* Flags values */
+
+#define NLM_F_REQUEST 0x01 /* It is request message. */
+#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */
+#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */
+#define NLM_F_ECHO 0x08 /* Receive resulting notifications */
+#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */
+#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */
+
+/* Modifiers to GET request */
+#define NLM_F_ROOT 0x100 /* specify tree root */
+#define NLM_F_MATCH 0x200 /* return all matching */
+#define NLM_F_ATOMIC 0x400 /* atomic GET */
+#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
+
+/* Modifiers to NEW request */
+#define NLM_F_REPLACE 0x100 /* Override existing */
+#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
+#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
+#define NLM_F_APPEND 0x800 /* Add to end of list */
+
+/* Modifiers to DELETE request */
+#define NLM_F_NONREC 0x100 /* Do not delete recursively */
+#define NLM_F_BULK 0x200 /* Delete multiple objects */
+
+/* Flags for ACK message */
+#define NLM_F_CAPPED 0x100 /* request was capped */
+#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
+
+/*
+ 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
+ 4.4BSD CHANGE NLM_F_REPLACE
+
+ True CHANGE NLM_F_CREATE|NLM_F_REPLACE
+ Append NLM_F_CREATE
+ Check NLM_F_EXCL
+ */
+
+#define NLMSG_ALIGNTO 4U
+#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh) ((void *)(((char *)nlh) + NLMSG_HDRLEN))
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+ (struct nlmsghdr *)(((char *)(nlh)) + \
+ NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define NLMSG_NOOP 0x1 /* Nothing. */
+#define NLMSG_ERROR 0x2 /* Error */
+#define NLMSG_DONE 0x3 /* End of a dump */
+#define NLMSG_OVERRUN 0x4 /* Data lost */
+
+#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */
+
+struct nlmsgerr {
+ int error;
+ struct nlmsghdr msg;
+ /*
+ * followed by the message contents unless NETLINK_CAP_ACK was set
+ * or the ACK indicates success (error == 0)
+ * message length is aligned with NLMSG_ALIGN()
+ */
+ /*
+ * followed by TLVs defined in enum nlmsgerr_attrs
+ * if NETLINK_EXT_ACK was set
+ */
+};
+
+/**
+ * enum nlmsgerr_attrs - nlmsgerr attributes
+ * @NLMSGERR_ATTR_UNUSED: unused
+ * @NLMSGERR_ATTR_MSG: error message string (string)
+ * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original
+ * message, counting from the beginning of the header (u32)
+ * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to
+ * be used - in the success case - to identify a created
+ * object or operation or similar (binary)
+ * @NLMSGERR_ATTR_POLICY: policy for a rejected attribute
+ * @NLMSGERR_ATTR_MISS_TYPE: type of a missing required attribute,
+ * %NLMSGERR_ATTR_MISS_NEST will not be present if the attribute was
+ * missing at the message level
+ * @NLMSGERR_ATTR_MISS_NEST: offset of the nest where attribute was missing
+ * @__NLMSGERR_ATTR_MAX: number of attributes
+ * @NLMSGERR_ATTR_MAX: highest attribute number
+ */
+enum nlmsgerr_attrs {
+ NLMSGERR_ATTR_UNUSED,
+ NLMSGERR_ATTR_MSG,
+ NLMSGERR_ATTR_OFFS,
+ NLMSGERR_ATTR_COOKIE,
+ NLMSGERR_ATTR_POLICY,
+ NLMSGERR_ATTR_MISS_TYPE,
+ NLMSGERR_ATTR_MISS_NEST,
+
+ __NLMSGERR_ATTR_MAX,
+ NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
+};
+
+#define NETLINK_ADD_MEMBERSHIP 1
+#define NETLINK_DROP_MEMBERSHIP 2
+#define NETLINK_PKTINFO 3
+#define NETLINK_BROADCAST_ERROR 4
+#define NETLINK_NO_ENOBUFS 5
+#define NETLINK_RX_RING 6
+#define NETLINK_TX_RING 7
+#define NETLINK_LISTEN_ALL_NSID 8
+#define NETLINK_LIST_MEMBERSHIPS 9
+#define NETLINK_CAP_ACK 10
+#define NETLINK_EXT_ACK 11
+#define NETLINK_GET_STRICT_CHK 12
+
+struct nl_pktinfo {
+ __u32 group;
+};
+
+struct nl_mmap_req {
+ unsigned int nm_block_size;
+ unsigned int nm_block_nr;
+ unsigned int nm_frame_size;
+ unsigned int nm_frame_nr;
+};
+
+struct nl_mmap_hdr {
+ unsigned int nm_status;
+ unsigned int nm_len;
+ __u32 nm_group;
+ /* credentials */
+ __u32 nm_pid;
+ __u32 nm_uid;
+ __u32 nm_gid;
+};
+
+enum nl_mmap_status {
+ NL_MMAP_STATUS_UNUSED,
+ NL_MMAP_STATUS_RESERVED,
+ NL_MMAP_STATUS_VALID,
+ NL_MMAP_STATUS_COPY,
+ NL_MMAP_STATUS_SKIP,
+};
+
+#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO
+#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
+#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
+
+#define NET_MAJOR 36 /* Major 36 is reserved for networking */
+
+enum {
+ NETLINK_UNCONNECTED = 0,
+ NETLINK_CONNECTED,
+};
+
+/*
+ * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * | Header | Pad | Payload | Pad |
+ * | (struct nlattr) | ing | | ing |
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * <-------------- nlattr->nla_len -------------->
+ */
+
+struct nlattr {
+ __u16 nla_len;
+ __u16 nla_type;
+};
+
+/*
+ * nla_type (16 bits)
+ * +---+---+-------------------------------+
+ * | N | O | Attribute Type |
+ * +---+---+-------------------------------+
+ * N := Carries nested attributes
+ * O := Payload stored in network byte order
+ *
+ * Note: The N and O flag are mutually exclusive.
+ */
+#define NLA_F_NESTED (1 << 15)
+#define NLA_F_NET_BYTEORDER (1 << 14)
+#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
+
+#define NLA_ALIGNTO 4
+#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
+#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
+
+/* Generic 32 bitflags attribute content sent to the kernel.
+ *
+ * The value is a bitmap that defines the values being set
+ * The selector is a bitmask that defines which value is legit
+ *
+ * Examples:
+ * value = 0x0, and selector = 0x1
+ * implies we are selecting bit 1 and we want to set its value to 0.
+ *
+ * value = 0x2, and selector = 0x2
+ * implies we are selecting bit 2 and we want to set its value to 1.
+ *
+ */
+struct nla_bitfield32 {
+ __u32 value;
+ __u32 selector;
+};
+
+/*
+ * policy descriptions - it's specific to each family how this is used
+ * Normally, it should be retrieved via a dump inside another attribute
+ * specifying where it applies.
+ */
+
+/**
+ * enum netlink_attribute_type - type of an attribute
+ * @NL_ATTR_TYPE_INVALID: unused
+ * @NL_ATTR_TYPE_FLAG: flag attribute (present/not present)
+ * @NL_ATTR_TYPE_U8: 8-bit unsigned attribute
+ * @NL_ATTR_TYPE_U16: 16-bit unsigned attribute
+ * @NL_ATTR_TYPE_U32: 32-bit unsigned attribute
+ * @NL_ATTR_TYPE_U64: 64-bit unsigned attribute
+ * @NL_ATTR_TYPE_S8: 8-bit signed attribute
+ * @NL_ATTR_TYPE_S16: 16-bit signed attribute
+ * @NL_ATTR_TYPE_S32: 32-bit signed attribute
+ * @NL_ATTR_TYPE_S64: 64-bit signed attribute
+ * @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified
+ * @NL_ATTR_TYPE_STRING: string, min/max length may be specified
+ * @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string,
+ * min/max length may be specified
+ * @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute
+ * consists of sub-attributes. The nested policy and maxtype
+ * inside may be specified.
+ * @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this
+ * attribute contains sub-attributes whose type is irrelevant
+ * (just used to separate the array entries) and each such array
+ * entry has attributes again, the policy for those inner ones
+ * and the corresponding maxtype may be specified.
+ * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute
+ * @NL_ATTR_TYPE_SINT: 32-bit or 64-bit signed attribute, aligned to 4B
+ * @NL_ATTR_TYPE_UINT: 32-bit or 64-bit unsigned attribute, aligned to 4B
+ */
+enum netlink_attribute_type {
+ NL_ATTR_TYPE_INVALID,
+
+ NL_ATTR_TYPE_FLAG,
+
+ NL_ATTR_TYPE_U8,
+ NL_ATTR_TYPE_U16,
+ NL_ATTR_TYPE_U32,
+ NL_ATTR_TYPE_U64,
+
+ NL_ATTR_TYPE_S8,
+ NL_ATTR_TYPE_S16,
+ NL_ATTR_TYPE_S32,
+ NL_ATTR_TYPE_S64,
+
+ NL_ATTR_TYPE_BINARY,
+ NL_ATTR_TYPE_STRING,
+ NL_ATTR_TYPE_NUL_STRING,
+
+ NL_ATTR_TYPE_NESTED,
+ NL_ATTR_TYPE_NESTED_ARRAY,
+
+ NL_ATTR_TYPE_BITFIELD32,
+
+ NL_ATTR_TYPE_SINT,
+ NL_ATTR_TYPE_UINT,
+};
+
+/**
+ * enum netlink_policy_type_attr - policy type attributes
+ * @NL_POLICY_TYPE_ATTR_UNSPEC: unused
+ * @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute,
+ * &enum netlink_attribute_type (U32)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed
+ * integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed
+ * integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned
+ * integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned
+ * integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary
+ * attributes, no minimum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary
+ * attributes, no maximum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and
+ * nested array types (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy
+ * attribute for nested and nested array types, this can
+ * in theory be < the size of the policy pointed to by
+ * the index, if limited inside the nesting (U32)
+ * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the
+ * bitfield32 type (U32)
+ * @NL_POLICY_TYPE_ATTR_MASK: mask of valid bits for unsigned integers (U64)
+ * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment
+ *
+ * @__NL_POLICY_TYPE_ATTR_MAX: number of attributes
+ * @NL_POLICY_TYPE_ATTR_MAX: highest attribute number
+ */
+enum netlink_policy_type_attr {
+ NL_POLICY_TYPE_ATTR_UNSPEC,
+ NL_POLICY_TYPE_ATTR_TYPE,
+ NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
+ NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
+ NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
+ NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
+ NL_POLICY_TYPE_ATTR_MIN_LENGTH,
+ NL_POLICY_TYPE_ATTR_MAX_LENGTH,
+ NL_POLICY_TYPE_ATTR_POLICY_IDX,
+ NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
+ NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
+ NL_POLICY_TYPE_ATTR_PAD,
+ NL_POLICY_TYPE_ATTR_MASK,
+
+ /* keep last */
+ __NL_POLICY_TYPE_ATTR_MAX,
+ NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1
+};
+
+#endif /* __LINUX_NETLINK_H */
diff --git a/uapi/linux/posix_types.h b/uapi/linux/posix_types.h
new file mode 100644
index 0000000..9a7a740
--- /dev/null
+++ b/uapi/linux/posix_types.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_POSIX_TYPES_H
+#define _LINUX_POSIX_TYPES_H
+
+#include <linux/stddef.h>
+
+/*
+ * This allows for 1024 file descriptors: if NR_OPEN is ever grown
+ * beyond that you'll have to change this too. But 1024 fd's seem to be
+ * enough even for such "real" unices like OSF/1, so hopefully this is
+ * one limit that doesn't have to be changed [again].
+ *
+ * Note that POSIX wants the FD_CLEAR(fd,fdsetp) defines to be in
+ * <sys/time.h> (and thus <linux/time.h>) - but this is a more logical
+ * place for them. Solved by having dummy defines in <sys/time.h>.
+ */
+
+/*
+ * This macro may have been defined in <gnu/types.h>. But we always
+ * use the one here.
+ */
+#undef __FD_SETSIZE
+#define __FD_SETSIZE 1024
+
+typedef struct {
+ unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
+} __kernel_fd_set;
+
+/* Type of a signal handler. */
+typedef void (*__kernel_sighandler_t)(int);
+
+/* Type of a SYSV IPC key. */
+typedef int __kernel_key_t;
+typedef int __kernel_mqd_t;
+
+#include <asm/posix_types.h>
+
+#endif /* _LINUX_POSIX_TYPES_H */
diff --git a/uapi/linux/rtnetlink.h b/uapi/linux/rtnetlink.h
new file mode 100644
index 0000000..4e6c8e1
--- /dev/null
+++ b/uapi/linux/rtnetlink.h
@@ -0,0 +1,830 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_RTNETLINK_H
+#define __LINUX_RTNETLINK_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/if_link.h>
+#include <linux/if_addr.h>
+#include <linux/neighbour.h>
+
+/* rtnetlink families. Values up to 127 are reserved for real address
+ * families, values above 128 may be used arbitrarily.
+ */
+#define RTNL_FAMILY_IPMR 128
+#define RTNL_FAMILY_IP6MR 129
+#define RTNL_FAMILY_MAX 129
+
+/****
+ * Routing/neighbour discovery messages.
+ ****/
+
+/* Types of messages */
+
+enum {
+ RTM_BASE = 16,
+#define RTM_BASE RTM_BASE
+
+ RTM_NEWLINK = 16,
+#define RTM_NEWLINK RTM_NEWLINK
+ RTM_DELLINK,
+#define RTM_DELLINK RTM_DELLINK
+ RTM_GETLINK,
+#define RTM_GETLINK RTM_GETLINK
+ RTM_SETLINK,
+#define RTM_SETLINK RTM_SETLINK
+
+ RTM_NEWADDR = 20,
+#define RTM_NEWADDR RTM_NEWADDR
+ RTM_DELADDR,
+#define RTM_DELADDR RTM_DELADDR
+ RTM_GETADDR,
+#define RTM_GETADDR RTM_GETADDR
+
+ RTM_NEWROUTE = 24,
+#define RTM_NEWROUTE RTM_NEWROUTE
+ RTM_DELROUTE,
+#define RTM_DELROUTE RTM_DELROUTE
+ RTM_GETROUTE,
+#define RTM_GETROUTE RTM_GETROUTE
+
+ RTM_NEWNEIGH = 28,
+#define RTM_NEWNEIGH RTM_NEWNEIGH
+ RTM_DELNEIGH,
+#define RTM_DELNEIGH RTM_DELNEIGH
+ RTM_GETNEIGH,
+#define RTM_GETNEIGH RTM_GETNEIGH
+
+ RTM_NEWRULE = 32,
+#define RTM_NEWRULE RTM_NEWRULE
+ RTM_DELRULE,
+#define RTM_DELRULE RTM_DELRULE
+ RTM_GETRULE,
+#define RTM_GETRULE RTM_GETRULE
+
+ RTM_NEWQDISC = 36,
+#define RTM_NEWQDISC RTM_NEWQDISC
+ RTM_DELQDISC,
+#define RTM_DELQDISC RTM_DELQDISC
+ RTM_GETQDISC,
+#define RTM_GETQDISC RTM_GETQDISC
+
+ RTM_NEWTCLASS = 40,
+#define RTM_NEWTCLASS RTM_NEWTCLASS
+ RTM_DELTCLASS,
+#define RTM_DELTCLASS RTM_DELTCLASS
+ RTM_GETTCLASS,
+#define RTM_GETTCLASS RTM_GETTCLASS
+
+ RTM_NEWTFILTER = 44,
+#define RTM_NEWTFILTER RTM_NEWTFILTER
+ RTM_DELTFILTER,
+#define RTM_DELTFILTER RTM_DELTFILTER
+ RTM_GETTFILTER,
+#define RTM_GETTFILTER RTM_GETTFILTER
+
+ RTM_NEWACTION = 48,
+#define RTM_NEWACTION RTM_NEWACTION
+ RTM_DELACTION,
+#define RTM_DELACTION RTM_DELACTION
+ RTM_GETACTION,
+#define RTM_GETACTION RTM_GETACTION
+
+ RTM_NEWPREFIX = 52,
+#define RTM_NEWPREFIX RTM_NEWPREFIX
+
+ RTM_GETMULTICAST = 58,
+#define RTM_GETMULTICAST RTM_GETMULTICAST
+
+ RTM_GETANYCAST = 62,
+#define RTM_GETANYCAST RTM_GETANYCAST
+
+ RTM_NEWNEIGHTBL = 64,
+#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL
+ RTM_GETNEIGHTBL = 66,
+#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL
+ RTM_SETNEIGHTBL,
+#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL
+
+ RTM_NEWNDUSEROPT = 68,
+#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT
+
+ RTM_NEWADDRLABEL = 72,
+#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
+ RTM_DELADDRLABEL,
+#define RTM_DELADDRLABEL RTM_DELADDRLABEL
+ RTM_GETADDRLABEL,
+#define RTM_GETADDRLABEL RTM_GETADDRLABEL
+
+ RTM_GETDCB = 78,
+#define RTM_GETDCB RTM_GETDCB
+ RTM_SETDCB,
+#define RTM_SETDCB RTM_SETDCB
+
+ RTM_NEWNETCONF = 80,
+#define RTM_NEWNETCONF RTM_NEWNETCONF
+ RTM_DELNETCONF,
+#define RTM_DELNETCONF RTM_DELNETCONF
+ RTM_GETNETCONF = 82,
+#define RTM_GETNETCONF RTM_GETNETCONF
+
+ RTM_NEWMDB = 84,
+#define RTM_NEWMDB RTM_NEWMDB
+ RTM_DELMDB = 85,
+#define RTM_DELMDB RTM_DELMDB
+ RTM_GETMDB = 86,
+#define RTM_GETMDB RTM_GETMDB
+
+ RTM_NEWNSID = 88,
+#define RTM_NEWNSID RTM_NEWNSID
+ RTM_DELNSID = 89,
+#define RTM_DELNSID RTM_DELNSID
+ RTM_GETNSID = 90,
+#define RTM_GETNSID RTM_GETNSID
+
+ RTM_NEWSTATS = 92,
+#define RTM_NEWSTATS RTM_NEWSTATS
+ RTM_GETSTATS = 94,
+#define RTM_GETSTATS RTM_GETSTATS
+ RTM_SETSTATS,
+#define RTM_SETSTATS RTM_SETSTATS
+
+ RTM_NEWCACHEREPORT = 96,
+#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT
+
+ RTM_NEWCHAIN = 100,
+#define RTM_NEWCHAIN RTM_NEWCHAIN
+ RTM_DELCHAIN,
+#define RTM_DELCHAIN RTM_DELCHAIN
+ RTM_GETCHAIN,
+#define RTM_GETCHAIN RTM_GETCHAIN
+
+ RTM_NEWNEXTHOP = 104,
+#define RTM_NEWNEXTHOP RTM_NEWNEXTHOP
+ RTM_DELNEXTHOP,
+#define RTM_DELNEXTHOP RTM_DELNEXTHOP
+ RTM_GETNEXTHOP,
+#define RTM_GETNEXTHOP RTM_GETNEXTHOP
+
+ RTM_NEWLINKPROP = 108,
+#define RTM_NEWLINKPROP RTM_NEWLINKPROP
+ RTM_DELLINKPROP,
+#define RTM_DELLINKPROP RTM_DELLINKPROP
+ RTM_GETLINKPROP,
+#define RTM_GETLINKPROP RTM_GETLINKPROP
+
+ RTM_NEWVLAN = 112,
+#define RTM_NEWNVLAN RTM_NEWVLAN
+ RTM_DELVLAN,
+#define RTM_DELVLAN RTM_DELVLAN
+ RTM_GETVLAN,
+#define RTM_GETVLAN RTM_GETVLAN
+
+ RTM_NEWNEXTHOPBUCKET = 116,
+#define RTM_NEWNEXTHOPBUCKET RTM_NEWNEXTHOPBUCKET
+ RTM_DELNEXTHOPBUCKET,
+#define RTM_DELNEXTHOPBUCKET RTM_DELNEXTHOPBUCKET
+ RTM_GETNEXTHOPBUCKET,
+#define RTM_GETNEXTHOPBUCKET RTM_GETNEXTHOPBUCKET
+
+ RTM_NEWTUNNEL = 120,
+#define RTM_NEWTUNNEL RTM_NEWTUNNEL
+ RTM_DELTUNNEL,
+#define RTM_DELTUNNEL RTM_DELTUNNEL
+ RTM_GETTUNNEL,
+#define RTM_GETTUNNEL RTM_GETTUNNEL
+
+ __RTM_MAX,
+#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
+};
+
+#define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE)
+#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2)
+#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2)
+
+/*
+ Generic structure for encapsulation of optional route information.
+ It is reminiscent of sockaddr, but with sa_family replaced
+ with attribute type.
+ */
+
+struct rtattr {
+ unsigned short rta_len;
+ unsigned short rta_type;
+};
+
+/* Macros to handle rtattributes */
+
+#define RTA_ALIGNTO 4U
+#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )
+#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \
+ (rta)->rta_len >= sizeof(struct rtattr) && \
+ (rta)->rta_len <= (len))
+#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \
+ (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
+#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
+#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len))
+#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
+#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
+
+
+
+
+/******************************************************************************
+ * Definitions used in routing table administration.
+ ****/
+
+struct rtmsg {
+ unsigned char rtm_family;
+ unsigned char rtm_dst_len;
+ unsigned char rtm_src_len;
+ unsigned char rtm_tos;
+
+ unsigned char rtm_table; /* Routing table id */
+ unsigned char rtm_protocol; /* Routing protocol; see below */
+ unsigned char rtm_scope; /* See below */
+ unsigned char rtm_type; /* See below */
+
+ unsigned rtm_flags;
+};
+
+/* rtm_type */
+
+enum {
+ RTN_UNSPEC,
+ RTN_UNICAST, /* Gateway or direct route */
+ RTN_LOCAL, /* Accept locally */
+ RTN_BROADCAST, /* Accept locally as broadcast,
+ send as broadcast */
+ RTN_ANYCAST, /* Accept locally as broadcast,
+ but send as unicast */
+ RTN_MULTICAST, /* Multicast route */
+ RTN_BLACKHOLE, /* Drop */
+ RTN_UNREACHABLE, /* Destination is unreachable */
+ RTN_PROHIBIT, /* Administratively prohibited */
+ RTN_THROW, /* Not in this table */
+ RTN_NAT, /* Translate this address */
+ RTN_XRESOLVE, /* Use external resolver */
+ __RTN_MAX
+};
+
+#define RTN_MAX (__RTN_MAX - 1)
+
+
+/* rtm_protocol */
+
+#define RTPROT_UNSPEC 0
+#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects;
+ not used by current IPv4 */
+#define RTPROT_KERNEL 2 /* Route installed by kernel */
+#define RTPROT_BOOT 3 /* Route installed during boot */
+#define RTPROT_STATIC 4 /* Route installed by administrator */
+
+/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel;
+ they are just passed from user and back as is.
+ It will be used by hypothetical multiple routing daemons.
+ Note that protocol values should be standardized in order to
+ avoid conflicts.
+ */
+
+#define RTPROT_GATED 8 /* Apparently, GateD */
+#define RTPROT_RA 9 /* RDISC/ND router advertisements */
+#define RTPROT_MRT 10 /* Merit MRT */
+#define RTPROT_ZEBRA 11 /* Zebra */
+#define RTPROT_BIRD 12 /* BIRD */
+#define RTPROT_DNROUTED 13 /* DECnet routing daemon */
+#define RTPROT_XORP 14 /* XORP */
+#define RTPROT_NTK 15 /* Netsukuku */
+#define RTPROT_DHCP 16 /* DHCP client */
+#define RTPROT_MROUTED 17 /* Multicast daemon */
+#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */
+#define RTPROT_BABEL 42 /* Babel daemon */
+#define RTPROT_OPENR 99 /* Open Routing (Open/R) Routes */
+#define RTPROT_BGP 186 /* BGP Routes */
+#define RTPROT_ISIS 187 /* ISIS Routes */
+#define RTPROT_OSPF 188 /* OSPF Routes */
+#define RTPROT_RIP 189 /* RIP Routes */
+#define RTPROT_EIGRP 192 /* EIGRP Routes */
+
+/* rtm_scope
+
+ Really it is not scope, but sort of distance to the destination.
+ NOWHERE are reserved for not existing destinations, HOST is our
+ local addresses, LINK are destinations, located on directly attached
+ link and UNIVERSE is everywhere in the Universe.
+
+ Intermediate values are also possible f.e. interior routes
+ could be assigned a value between UNIVERSE and LINK.
+*/
+
+enum rt_scope_t {
+ RT_SCOPE_UNIVERSE=0,
+/* User defined values */
+ RT_SCOPE_SITE=200,
+ RT_SCOPE_LINK=253,
+ RT_SCOPE_HOST=254,
+ RT_SCOPE_NOWHERE=255
+};
+
+/* rtm_flags */
+
+#define RTM_F_NOTIFY 0x100 /* Notify user of route change */
+#define RTM_F_CLONED 0x200 /* This route is cloned */
+#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */
+#define RTM_F_PREFIX 0x800 /* Prefix addresses */
+#define RTM_F_LOOKUP_TABLE 0x1000 /* set rtm_table to FIB lookup result */
+#define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */
+#define RTM_F_OFFLOAD 0x4000 /* route is offloaded */
+#define RTM_F_TRAP 0x8000 /* route is trapping packets */
+#define RTM_F_OFFLOAD_FAILED 0x20000000 /* route offload failed, this value
+ * is chosen to avoid conflicts with
+ * other flags defined in
+ * include/uapi/linux/ipv6_route.h
+ */
+
+/* Reserved table identifiers */
+
+enum rt_class_t {
+ RT_TABLE_UNSPEC=0,
+/* User defined values */
+ RT_TABLE_COMPAT=252,
+ RT_TABLE_DEFAULT=253,
+ RT_TABLE_MAIN=254,
+ RT_TABLE_LOCAL=255,
+ RT_TABLE_MAX=0xFFFFFFFF
+};
+
+
+/* Routing message attributes */
+
+enum rtattr_type_t {
+ RTA_UNSPEC,
+ RTA_DST,
+ RTA_SRC,
+ RTA_IIF,
+ RTA_OIF,
+ RTA_GATEWAY,
+ RTA_PRIORITY,
+ RTA_PREFSRC,
+ RTA_METRICS,
+ RTA_MULTIPATH,
+ RTA_PROTOINFO, /* no longer used */
+ RTA_FLOW,
+ RTA_CACHEINFO,
+ RTA_SESSION, /* no longer used */
+ RTA_MP_ALGO, /* no longer used */
+ RTA_TABLE,
+ RTA_MARK,
+ RTA_MFC_STATS,
+ RTA_VIA,
+ RTA_NEWDST,
+ RTA_PREF,
+ RTA_ENCAP_TYPE,
+ RTA_ENCAP,
+ RTA_EXPIRES,
+ RTA_PAD,
+ RTA_UID,
+ RTA_TTL_PROPAGATE,
+ RTA_IP_PROTO,
+ RTA_SPORT,
+ RTA_DPORT,
+ RTA_NH_ID,
+ __RTA_MAX
+};
+
+#define RTA_MAX (__RTA_MAX - 1)
+
+#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
+#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg))
+
+/* RTM_MULTIPATH --- array of struct rtnexthop.
+ *
+ * "struct rtnexthop" describes all necessary nexthop information,
+ * i.e. parameters of path to a destination via this nexthop.
+ *
+ * At the moment it is impossible to set different prefsrc, mtu, window
+ * and rtt for different paths from multipath.
+ */
+
+struct rtnexthop {
+ unsigned short rtnh_len;
+ unsigned char rtnh_flags;
+ unsigned char rtnh_hops;
+ int rtnh_ifindex;
+};
+
+/* rtnh_flags */
+
+#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */
+#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */
+#define RTNH_F_ONLINK 4 /* Gateway is forced on link */
+#define RTNH_F_OFFLOAD 8 /* Nexthop is offloaded */
+#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */
+#define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */
+#define RTNH_F_TRAP 64 /* Nexthop is trapping packets */
+
+#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | \
+ RTNH_F_OFFLOAD | RTNH_F_TRAP)
+
+/* Macros to handle hexthops */
+
+#define RTNH_ALIGNTO 4
+#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) )
+#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \
+ ((int)(rtnh)->rtnh_len) <= (len))
+#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len)))
+#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len))
+#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len))
+#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0)))
+
+/* RTA_VIA */
+struct rtvia {
+ __kernel_sa_family_t rtvia_family;
+ __u8 rtvia_addr[];
+};
+
+/* RTM_CACHEINFO */
+
+struct rta_cacheinfo {
+ __u32 rta_clntref;
+ __u32 rta_lastuse;
+ __s32 rta_expires;
+ __u32 rta_error;
+ __u32 rta_used;
+
+#define RTNETLINK_HAVE_PEERINFO 1
+ __u32 rta_id;
+ __u32 rta_ts;
+ __u32 rta_tsage;
+};
+
+/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
+
+enum {
+ RTAX_UNSPEC,
+#define RTAX_UNSPEC RTAX_UNSPEC
+ RTAX_LOCK,
+#define RTAX_LOCK RTAX_LOCK
+ RTAX_MTU,
+#define RTAX_MTU RTAX_MTU
+ RTAX_WINDOW,
+#define RTAX_WINDOW RTAX_WINDOW
+ RTAX_RTT,
+#define RTAX_RTT RTAX_RTT
+ RTAX_RTTVAR,
+#define RTAX_RTTVAR RTAX_RTTVAR
+ RTAX_SSTHRESH,
+#define RTAX_SSTHRESH RTAX_SSTHRESH
+ RTAX_CWND,
+#define RTAX_CWND RTAX_CWND
+ RTAX_ADVMSS,
+#define RTAX_ADVMSS RTAX_ADVMSS
+ RTAX_REORDERING,
+#define RTAX_REORDERING RTAX_REORDERING
+ RTAX_HOPLIMIT,
+#define RTAX_HOPLIMIT RTAX_HOPLIMIT
+ RTAX_INITCWND,
+#define RTAX_INITCWND RTAX_INITCWND
+ RTAX_FEATURES,
+#define RTAX_FEATURES RTAX_FEATURES
+ RTAX_RTO_MIN,
+#define RTAX_RTO_MIN RTAX_RTO_MIN
+ RTAX_INITRWND,
+#define RTAX_INITRWND RTAX_INITRWND
+ RTAX_QUICKACK,
+#define RTAX_QUICKACK RTAX_QUICKACK
+ RTAX_CC_ALGO,
+#define RTAX_CC_ALGO RTAX_CC_ALGO
+ RTAX_FASTOPEN_NO_COOKIE,
+#define RTAX_FASTOPEN_NO_COOKIE RTAX_FASTOPEN_NO_COOKIE
+ __RTAX_MAX
+};
+
+#define RTAX_MAX (__RTAX_MAX - 1)
+
+#define RTAX_FEATURE_ECN (1 << 0)
+#define RTAX_FEATURE_SACK (1 << 1) /* unused */
+#define RTAX_FEATURE_TIMESTAMP (1 << 2) /* unused */
+#define RTAX_FEATURE_ALLFRAG (1 << 3) /* unused */
+#define RTAX_FEATURE_TCP_USEC_TS (1 << 4)
+
+#define RTAX_FEATURE_MASK (RTAX_FEATURE_ECN | \
+ RTAX_FEATURE_SACK | \
+ RTAX_FEATURE_TIMESTAMP | \
+ RTAX_FEATURE_ALLFRAG | \
+ RTAX_FEATURE_TCP_USEC_TS)
+
+struct rta_session {
+ __u8 proto;
+ __u8 pad1;
+ __u16 pad2;
+
+ union {
+ struct {
+ __u16 sport;
+ __u16 dport;
+ } ports;
+
+ struct {
+ __u8 type;
+ __u8 code;
+ __u16 ident;
+ } icmpt;
+
+ __u32 spi;
+ } u;
+};
+
+struct rta_mfc_stats {
+ __u64 mfcs_packets;
+ __u64 mfcs_bytes;
+ __u64 mfcs_wrong_if;
+};
+
+/****
+ * General form of address family dependent message.
+ ****/
+
+struct rtgenmsg {
+ unsigned char rtgen_family;
+};
+
+/*****************************************************************
+ * Link layer specific messages.
+ ****/
+
+/* struct ifinfomsg
+ * passes link level specific information, not dependent
+ * on network protocol.
+ */
+
+struct ifinfomsg {
+ unsigned char ifi_family;
+ unsigned char __ifi_pad;
+ unsigned short ifi_type; /* ARPHRD_* */
+ int ifi_index; /* Link index */
+ unsigned ifi_flags; /* IFF_* flags */
+ unsigned ifi_change; /* IFF_* change mask */
+};
+
+/********************************************************************
+ * prefix information
+ ****/
+
+struct prefixmsg {
+ unsigned char prefix_family;
+ unsigned char prefix_pad1;
+ unsigned short prefix_pad2;
+ int prefix_ifindex;
+ unsigned char prefix_type;
+ unsigned char prefix_len;
+ unsigned char prefix_flags;
+ unsigned char prefix_pad3;
+};
+
+enum
+{
+ PREFIX_UNSPEC,
+ PREFIX_ADDRESS,
+ PREFIX_CACHEINFO,
+ __PREFIX_MAX
+};
+
+#define PREFIX_MAX (__PREFIX_MAX - 1)
+
+struct prefix_cacheinfo {
+ __u32 preferred_time;
+ __u32 valid_time;
+};
+
+
+/*****************************************************************
+ * Traffic control messages.
+ ****/
+
+struct tcmsg {
+ unsigned char tcm_family;
+ unsigned char tcm__pad1;
+ unsigned short tcm__pad2;
+ int tcm_ifindex;
+ __u32 tcm_handle;
+ __u32 tcm_parent;
+/* tcm_block_index is used instead of tcm_parent
+ * in case tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK
+ */
+#define tcm_block_index tcm_parent
+ __u32 tcm_info;
+};
+
+/* For manipulation of filters in shared block, tcm_ifindex is set to
+ * TCM_IFINDEX_MAGIC_BLOCK, and tcm_parent is aliased to tcm_block_index
+ * which is the block index.
+ */
+#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
+
+enum {
+ TCA_UNSPEC,
+ TCA_KIND,
+ TCA_OPTIONS,
+ TCA_STATS,
+ TCA_XSTATS,
+ TCA_RATE,
+ TCA_FCNT,
+ TCA_STATS2,
+ TCA_STAB,
+ TCA_PAD,
+ TCA_DUMP_INVISIBLE,
+ TCA_CHAIN,
+ TCA_HW_OFFLOAD,
+ TCA_INGRESS_BLOCK,
+ TCA_EGRESS_BLOCK,
+ TCA_DUMP_FLAGS,
+ TCA_EXT_WARN_MSG,
+ __TCA_MAX
+};
+
+#define TCA_MAX (__TCA_MAX - 1)
+
+#define TCA_DUMP_FLAGS_TERSE (1 << 0) /* Means that in dump user gets only basic
+ * data necessary to identify the objects
+ * (handle, cookie, etc.) and stats.
+ */
+
+#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg))))
+#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg))
+
+/********************************************************************
+ * Neighbor Discovery userland options
+ ****/
+
+struct nduseroptmsg {
+ unsigned char nduseropt_family;
+ unsigned char nduseropt_pad1;
+ unsigned short nduseropt_opts_len; /* Total length of options */
+ int nduseropt_ifindex;
+ __u8 nduseropt_icmp_type;
+ __u8 nduseropt_icmp_code;
+ unsigned short nduseropt_pad2;
+ unsigned int nduseropt_pad3;
+ /* Followed by one or more ND options */
+};
+
+enum {
+ NDUSEROPT_UNSPEC,
+ NDUSEROPT_SRCADDR,
+ __NDUSEROPT_MAX
+};
+
+#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1)
+
+/* RTnetlink multicast groups - backwards compatibility for userspace */
+#define RTMGRP_LINK 1
+#define RTMGRP_NOTIFY 2
+#define RTMGRP_NEIGH 4
+#define RTMGRP_TC 8
+
+#define RTMGRP_IPV4_IFADDR 0x10
+#define RTMGRP_IPV4_MROUTE 0x20
+#define RTMGRP_IPV4_ROUTE 0x40
+#define RTMGRP_IPV4_RULE 0x80
+
+#define RTMGRP_IPV6_IFADDR 0x100
+#define RTMGRP_IPV6_MROUTE 0x200
+#define RTMGRP_IPV6_ROUTE 0x400
+#define RTMGRP_IPV6_IFINFO 0x800
+
+#define RTMGRP_DECnet_IFADDR 0x1000
+#define RTMGRP_DECnet_ROUTE 0x4000
+
+#define RTMGRP_IPV6_PREFIX 0x20000
+
+/* RTnetlink multicast groups */
+enum rtnetlink_groups {
+ RTNLGRP_NONE,
+#define RTNLGRP_NONE RTNLGRP_NONE
+ RTNLGRP_LINK,
+#define RTNLGRP_LINK RTNLGRP_LINK
+ RTNLGRP_NOTIFY,
+#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY
+ RTNLGRP_NEIGH,
+#define RTNLGRP_NEIGH RTNLGRP_NEIGH
+ RTNLGRP_TC,
+#define RTNLGRP_TC RTNLGRP_TC
+ RTNLGRP_IPV4_IFADDR,
+#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR
+ RTNLGRP_IPV4_MROUTE,
+#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE
+ RTNLGRP_IPV4_ROUTE,
+#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE
+ RTNLGRP_IPV4_RULE,
+#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE
+ RTNLGRP_IPV6_IFADDR,
+#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR
+ RTNLGRP_IPV6_MROUTE,
+#define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE
+ RTNLGRP_IPV6_ROUTE,
+#define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE
+ RTNLGRP_IPV6_IFINFO,
+#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO
+ RTNLGRP_DECnet_IFADDR,
+#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR
+ RTNLGRP_NOP2,
+ RTNLGRP_DECnet_ROUTE,
+#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE
+ RTNLGRP_DECnet_RULE,
+#define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE
+ RTNLGRP_NOP4,
+ RTNLGRP_IPV6_PREFIX,
+#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX
+ RTNLGRP_IPV6_RULE,
+#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE
+ RTNLGRP_ND_USEROPT,
+#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT
+ RTNLGRP_PHONET_IFADDR,
+#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR
+ RTNLGRP_PHONET_ROUTE,
+#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
+ RTNLGRP_DCB,
+#define RTNLGRP_DCB RTNLGRP_DCB
+ RTNLGRP_IPV4_NETCONF,
+#define RTNLGRP_IPV4_NETCONF RTNLGRP_IPV4_NETCONF
+ RTNLGRP_IPV6_NETCONF,
+#define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF
+ RTNLGRP_MDB,
+#define RTNLGRP_MDB RTNLGRP_MDB
+ RTNLGRP_MPLS_ROUTE,
+#define RTNLGRP_MPLS_ROUTE RTNLGRP_MPLS_ROUTE
+ RTNLGRP_NSID,
+#define RTNLGRP_NSID RTNLGRP_NSID
+ RTNLGRP_MPLS_NETCONF,
+#define RTNLGRP_MPLS_NETCONF RTNLGRP_MPLS_NETCONF
+ RTNLGRP_IPV4_MROUTE_R,
+#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R
+ RTNLGRP_IPV6_MROUTE_R,
+#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R
+ RTNLGRP_NEXTHOP,
+#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP
+ RTNLGRP_BRVLAN,
+#define RTNLGRP_BRVLAN RTNLGRP_BRVLAN
+ RTNLGRP_MCTP_IFADDR,
+#define RTNLGRP_MCTP_IFADDR RTNLGRP_MCTP_IFADDR
+ RTNLGRP_TUNNEL,
+#define RTNLGRP_TUNNEL RTNLGRP_TUNNEL
+ RTNLGRP_STATS,
+#define RTNLGRP_STATS RTNLGRP_STATS
+ __RTNLGRP_MAX
+};
+#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
+
+/* TC action piece */
+struct tcamsg {
+ unsigned char tca_family;
+ unsigned char tca__pad1;
+ unsigned short tca__pad2;
+};
+
+enum {
+ TCA_ROOT_UNSPEC,
+ TCA_ROOT_TAB,
+#define TCA_ACT_TAB TCA_ROOT_TAB
+#define TCAA_MAX TCA_ROOT_TAB
+ TCA_ROOT_FLAGS,
+ TCA_ROOT_COUNT,
+ TCA_ROOT_TIME_DELTA, /* in msecs */
+ TCA_ROOT_EXT_WARN_MSG,
+ __TCA_ROOT_MAX,
+#define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1)
+};
+
+#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg))))
+#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg))
+/* tcamsg flags stored in attribute TCA_ROOT_FLAGS
+ *
+ * TCA_ACT_FLAG_LARGE_DUMP_ON user->kernel to request for larger than
+ * TCA_ACT_MAX_PRIO actions in a dump. All dump responses will contain the
+ * number of actions being dumped stored in for user app's consumption in
+ * TCA_ROOT_COUNT
+ *
+ * TCA_ACT_FLAG_TERSE_DUMP user->kernel to request terse (brief) dump that only
+ * includes essential action info (kind, index, etc.)
+ *
+ */
+#define TCA_FLAG_LARGE_DUMP_ON (1 << 0)
+#define TCA_ACT_FLAG_LARGE_DUMP_ON TCA_FLAG_LARGE_DUMP_ON
+#define TCA_ACT_FLAG_TERSE_DUMP (1 << 1)
+
+/* New extended info filters for IFLA_EXT_MASK */
+#define RTEXT_FILTER_VF (1 << 0)
+#define RTEXT_FILTER_BRVLAN (1 << 1)
+#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
+#define RTEXT_FILTER_SKIP_STATS (1 << 3)
+#define RTEXT_FILTER_MRP (1 << 4)
+#define RTEXT_FILTER_CFM_CONFIG (1 << 5)
+#define RTEXT_FILTER_CFM_STATUS (1 << 6)
+#define RTEXT_FILTER_MST (1 << 7)
+
+/* End of information exported to user level */
+
+
+
+#endif /* __LINUX_RTNETLINK_H */
diff --git a/uapi/linux/socket.h b/uapi/linux/socket.h
new file mode 100644
index 0000000..89c227f
--- /dev/null
+++ b/uapi/linux/socket.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_SOCKET_H
+#define _LINUX_SOCKET_H
+
+/*
+ * Desired design of maximum size and alignment (see RFC2553)
+ */
+#define _K_SS_MAXSIZE 128 /* Implementation specific max size */
+
+typedef unsigned short __kernel_sa_family_t;
+
+/*
+ * The definition uses anonymous union and struct in order to control the
+ * default alignment.
+ */
+struct __kernel_sockaddr_storage {
+ union {
+ struct {
+ __kernel_sa_family_t ss_family; /* address family */
+ /* Following field(s) are implementation specific */
+ char __data[_K_SS_MAXSIZE - sizeof(unsigned short)];
+ /* space to achieve desired size, */
+ /* _SS_MAXSIZE value minus size of ss_family */
+ };
+ void *__align; /* implementation specific desired alignment */
+ };
+};
+
+#define SOCK_SNDBUF_LOCK 1
+#define SOCK_RCVBUF_LOCK 2
+
+#define SOCK_BUF_LOCK_MASK (SOCK_SNDBUF_LOCK | SOCK_RCVBUF_LOCK)
+
+#define SOCK_TXREHASH_DEFAULT 255
+#define SOCK_TXREHASH_DISABLED 0
+#define SOCK_TXREHASH_ENABLED 1
+
+#endif /* _LINUX_SOCKET_H */
diff --git a/uapi/linux/stddef.h b/uapi/linux/stddef.h
new file mode 100644
index 0000000..bf9749d
--- /dev/null
+++ b/uapi/linux/stddef.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_STDDEF_H
+#define _LINUX_STDDEF_H
+
+
+
+#ifndef __always_inline
+#define __always_inline __inline__
+#endif
+
+/**
+ * __struct_group() - Create a mirrored named and anonyomous struct
+ *
+ * @TAG: The tag name for the named sub-struct (usually empty)
+ * @NAME: The identifier name of the mirrored sub-struct
+ * @ATTRS: Any struct attributes (usually empty)
+ * @MEMBERS: The member declarations for the mirrored structs
+ *
+ * Used to create an anonymous union of two structs with identical layout
+ * and size: one anonymous and one named. The former's members can be used
+ * normally without sub-struct naming, and the latter can be used to
+ * reason about the start, end, and size of the group of struct members.
+ * The named struct can also be explicitly tagged for layer reuse, as well
+ * as both having struct attributes appended.
+ */
+#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \
+ union { \
+ struct { MEMBERS } ATTRS; \
+ struct TAG { MEMBERS } ATTRS NAME; \
+ } ATTRS
+
+#ifdef __cplusplus
+/* sizeof(struct{}) is 1 in C++, not 0, can't use C version of the macro. */
+#define __DECLARE_FLEX_ARRAY(T, member) \
+ T member[0]
+#else
+/**
+ * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
+ *
+ * @TYPE: The type of each flexible array element
+ * @NAME: The name of the flexible array member
+ *
+ * In order to have a flexible array member in a union or alone in a
+ * struct, it needs to be wrapped in an anonymous struct with at least 1
+ * named member, but that member can be empty.
+ */
+#define __DECLARE_FLEX_ARRAY(TYPE, NAME) \
+ struct { \
+ struct { } __empty_ ## NAME; \
+ TYPE NAME[]; \
+ }
+#endif
+
+#ifndef __counted_by
+#define __counted_by(m)
+#endif
+
+#endif /* _LINUX_STDDEF_H */
diff --git a/uapi/linux/types.h b/uapi/linux/types.h
new file mode 100644
index 0000000..e670013
--- /dev/null
+++ b/uapi/linux/types.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_TYPES_H
+#define _LINUX_TYPES_H
+
+#include <asm/types.h>
+
+#ifndef __ASSEMBLY__
+
+#include <linux/posix_types.h>
+
+#ifdef __SIZEOF_INT128__
+typedef __signed__ __int128 __s128 __attribute__((aligned(16)));
+typedef unsigned __int128 __u128 __attribute__((aligned(16)));
+#endif
+
+/*
+ * Below are truly Linux-specific types that should never collide with
+ * any application/library that wants linux/types.h.
+ */
+
+/* sparse defines __CHECKER__; see Documentation/dev-tools/sparse.rst */
+#ifdef __CHECKER__
+#define __bitwise __attribute__((bitwise))
+#else
+#define __bitwise
+#endif
+
+/* The kernel doesn't use this legacy form, but user space does */
+#define __bitwise__ __bitwise
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+
+typedef __u16 __bitwise __sum16;
+typedef __u32 __bitwise __wsum;
+
+/*
+ * aligned_u64 should be used in defining kernel<->userspace ABIs to avoid
+ * common 32/64-bit compat problems.
+ * 64-bit values align to 4-byte boundaries on x86_32 (and possibly other
+ * architectures) and to 8-byte boundaries on 64-bit architectures. The new
+ * aligned_64 type enforces 8-byte alignment so that structs containing
+ * aligned_64 values have the same alignment on 32-bit and 64-bit architectures.
+ * No conversions are necessary between 32-bit user-space and a 64-bit kernel.
+ */
+#define __aligned_u64 __u64 __attribute__((aligned(8)))
+#define __aligned_be64 __be64 __attribute__((aligned(8)))
+#define __aligned_le64 __le64 __attribute__((aligned(8)))
+
+typedef unsigned __bitwise __poll_t;
+
+#endif /* __ASSEMBLY__ */
+#endif /* _LINUX_TYPES_H */
diff --git a/vioc.c b/vioc.c
new file mode 100644
index 0000000..c04a6dc
--- /dev/null
+++ b/vioc.c
@@ -0,0 +1,34 @@
+/* Copyright 2006 Fabric7 Systems, Inc */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "internal.h"
+
+struct regs_line {
+ u32 addr;
+ u32 data;
+};
+
+#define VIOC_REGS_LINE_SIZE sizeof(struct regs_line)
+
+int vioc_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ unsigned int i;
+ unsigned int num_regs;
+ struct regs_line *reg_info = (struct regs_line *) regs->data;
+
+ printf("ethtool_regs\n"
+ "%-20s = %04x\n"
+ "%-20s = %04x\n",
+ "cmd", regs->cmd,
+ "version", regs->version);
+
+ num_regs = regs->len/VIOC_REGS_LINE_SIZE;
+
+ for (i = 0; i < num_regs; i++){
+ printf("%08x = %08x\n", reg_info[i].addr, reg_info[i].data);
+ }
+
+ return 0;
+}
diff --git a/vmxnet3.c b/vmxnet3.c
new file mode 100644
index 0000000..6872682
--- /dev/null
+++ b/vmxnet3.c
@@ -0,0 +1,199 @@
+/* Copyright (c) 2015 VMware Inc.*/
+#include <stdio.h>
+#include "internal.h"
+
+int
+vmxnet3_dump_regs(struct ethtool_drvinfo *info __maybe_unused,
+ struct ethtool_regs *regs)
+{
+ u32 *regs_buff = (u32 *)regs->data;
+ u32 version = regs->version;
+ int i = 0, j = 0, cnt;
+
+ if (version != 2)
+ return -1;
+
+ fprintf(stdout, "Control Registers\n");
+ fprintf(stdout, "=================\n");
+
+ fprintf(stdout,
+ " VRRS (Vmxnet3 Revision Report and Selection) 0x%x\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " UVRS (UPT Version Report and Selection) 0x%x\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " DSA (Driver Shared Address) 0x%08x%08x\n",
+ regs_buff[j+1], regs_buff[j]);
+ j += 2;
+ fprintf(stdout,
+ " CMD (Command Register) 0x%x\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " MAC (Media Access Control address) %02x:%02x:%02x:%02x:%02x:%02x\n",
+ regs_buff[j] & 0xff,
+ (regs_buff[j] >> 8) & 0xff,
+ (regs_buff[j] >> 16) & 0xff,
+ (regs_buff[j] >> 24) & 0xff,
+ regs_buff[j + 1] & 0xff,
+ (regs_buff[j + 1] >> 8) & 0xff);
+ j += 2;
+ fprintf(stdout,
+ " ICR (Interrupt Cause Register) 0x%x\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " ECR (Event Cause Register) 0x%x\n",
+ regs_buff[j++]);
+
+ fprintf(stdout, "Datapath Registers\n");
+ fprintf(stdout, "==================\n");
+
+ /* Interrupt Mask Registers */
+ cnt = regs_buff[j++];
+ for (i = 0; i < cnt; i++) {
+ fprintf(stdout,
+ " IMR (Interrupt Mask Register) %d 0x%x\n",
+ i, regs_buff[j++]);
+ }
+
+ /* Transmit Queue Registers */
+ cnt = regs_buff[j++];
+ for (i = 0; i < cnt; i++) {
+ fprintf(stdout, " Transmit Queue %d\n", i);
+ fprintf(stdout, " ----------------\n");
+ fprintf(stdout,
+ " TXPROD (Transmit Ring Producer Register) 0x%x\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " Transmit Ring\n");
+ fprintf(stdout,
+ " Base Address 0x%08x%08x\n",
+ regs_buff[j+1], regs_buff[j]);
+ j += 2;
+ fprintf(stdout,
+ " Size %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " next2fill %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " next2comp %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " gen %u\n",
+ regs_buff[j++]);
+
+ fprintf(stdout,
+ " Transmit Data Ring\n");
+ fprintf(stdout,
+ " Base Address 0x%08x%08x\n",
+ regs_buff[j+1], regs_buff[j]);
+ j += 2;
+ fprintf(stdout,
+ " Size %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " Buffer Size %u\n",
+ regs_buff[j++]);
+
+ fprintf(stdout,
+ " Transmit Completion Ring\n");
+ fprintf(stdout,
+ " Base Address 0x%08x%08x\n",
+ regs_buff[j+1], regs_buff[j]);
+ j += 2;
+ fprintf(stdout,
+ " size %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " next2proc %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " gen %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " stopped %u\n",
+ regs_buff[j++]);
+ }
+
+ /* Receive Queue Registers */
+ cnt = regs_buff[j++];
+ for (i = 0; i < cnt; i++) {
+ fprintf(stdout, " Receive Queue %d\n", i);
+ fprintf(stdout, " ----------------\n");
+ fprintf(stdout,
+ " RXPROD1 (Receive Ring Producer Register) 1 0x%x\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " RXPROD2 (Receive Ring Producer Register) 2 0x%x\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " Receive Ring 0\n");
+ fprintf(stdout,
+ " Base Address 0x%08x%08x\n",
+ regs_buff[j+1], regs_buff[j]);
+ j += 2;
+ fprintf(stdout,
+ " Size %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " next2fill %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " next2comp %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " gen %u\n",
+ regs_buff[j++]);
+
+ fprintf(stdout,
+ " Receive Ring 1\n");
+ fprintf(stdout,
+ " Base Address 0x%08x%08x\n",
+ regs_buff[j+1], regs_buff[j]);
+ j += 2;
+ fprintf(stdout,
+ " Size %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " next2fill %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " next2comp %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " gen %u\n",
+ regs_buff[j++]);
+
+ fprintf(stdout,
+ " Receive Data Ring\n");
+ fprintf(stdout,
+ " Base Address 0x%08x%08x\n",
+ regs_buff[j+1], regs_buff[j]);
+ j += 2;
+ fprintf(stdout,
+ " Size %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " Buffer Size %u\n",
+ regs_buff[j++]);
+
+ fprintf(stdout,
+ " Receive Completion Ring\n");
+ fprintf(stdout,
+ " Base Address 0x%08x%08x\n",
+ regs_buff[j+1], regs_buff[j]);
+ j += 2;
+ fprintf(stdout,
+ " size %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " next2proc %u\n",
+ regs_buff[j++]);
+ fprintf(stdout,
+ " gen %u\n",
+ regs_buff[j++]);
+ }
+
+ return 0;
+}