diff options
-rw-r--r-- | .gitignore | 34 | ||||
-rw-r--r-- | AUTHORS | 9 | ||||
-rw-r--r-- | COPYING | 339 | ||||
-rw-r--r-- | ChangeLog | 353 | ||||
-rw-r--r-- | LICENSE | 3 | ||||
-rw-r--r-- | Makefile.am | 72 | ||||
-rw-r--r-- | NEWS | 740 | ||||
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | amd8111e.c | 306 | ||||
-rw-r--r-- | at76c50x-usb.c | 32 | ||||
-rwxr-xr-x | autogen.sh | 11 | ||||
-rw-r--r-- | bnxt.c | 97 | ||||
-rw-r--r-- | cmis.c | 1041 | ||||
-rw-r--r-- | cmis.h | 252 | ||||
-rw-r--r-- | common.c | 211 | ||||
-rw-r--r-- | common.h | 49 | ||||
-rw-r--r-- | configure.ac | 86 | ||||
-rw-r--r-- | cpsw.c | 193 | ||||
-rw-r--r-- | de2104x.c | 783 | ||||
-rw-r--r-- | dsa.c | 882 | ||||
-rw-r--r-- | e100.c | 238 | ||||
-rw-r--r-- | e1000.c | 640 | ||||
-rw-r--r-- | et131x.c | 124 | ||||
-rw-r--r-- | ethtool.8.in | 1780 | ||||
-rw-r--r-- | ethtool.c | 6488 | ||||
-rw-r--r-- | ethtool.spec.in | 41 | ||||
-rw-r--r-- | fec.c | 220 | ||||
-rw-r--r-- | fec_8xx.c | 82 | ||||
-rw-r--r-- | fjes.c | 90 | ||||
-rw-r--r-- | fsl_enetc.c | 259 | ||||
-rw-r--r-- | hns3.c | 829 | ||||
-rw-r--r-- | ibm_emac.c | 334 | ||||
-rw-r--r-- | igb.c | 876 | ||||
-rw-r--r-- | igc.c | 284 | ||||
-rw-r--r-- | internal.h | 413 | ||||
-rw-r--r-- | ixgb.c | 147 | ||||
-rw-r--r-- | ixgbe.c | 1296 | ||||
-rw-r--r-- | ixgbevf.c | 181 | ||||
-rw-r--r-- | json_print.c | 228 | ||||
-rw-r--r-- | json_print.h | 67 | ||||
-rw-r--r-- | json_writer.c | 391 | ||||
-rw-r--r-- | json_writer.h | 76 | ||||
-rw-r--r-- | lan743x.c | 73 | ||||
-rw-r--r-- | lan78xx.c | 88 | ||||
-rw-r--r-- | list.h | 34 | ||||
-rw-r--r-- | m4/ax_append_flag.m4 | 71 | ||||
-rw-r--r-- | m4/ax_check_compile_flag.m4 | 74 | ||||
-rw-r--r-- | marvell.c | 455 | ||||
-rw-r--r-- | natsemi.c | 987 | ||||
-rw-r--r-- | netlink/bitset.c | 259 | ||||
-rw-r--r-- | netlink/bitset.h | 28 | ||||
-rw-r--r-- | netlink/cable_test.c | 595 | ||||
-rw-r--r-- | netlink/channels.c | 143 | ||||
-rw-r--r-- | netlink/coalesce.c | 335 | ||||
-rw-r--r-- | netlink/desc-ethtool.c | 595 | ||||
-rw-r--r-- | netlink/desc-genlctrl.c | 113 | ||||
-rw-r--r-- | netlink/desc-rtnl.c | 96 | ||||
-rw-r--r-- | netlink/eee.c | 189 | ||||
-rw-r--r-- | netlink/extapi.h | 136 | ||||
-rw-r--r-- | netlink/features.c | 569 | ||||
-rw-r--r-- | netlink/fec.c | 360 | ||||
-rw-r--r-- | netlink/mm.c | 270 | ||||
-rw-r--r-- | netlink/module-eeprom.c | 310 | ||||
-rw-r--r-- | netlink/module.c | 179 | ||||
-rw-r--r-- | netlink/monitor.c | 324 | ||||
-rw-r--r-- | netlink/msgbuff.c | 256 | ||||
-rw-r--r-- | netlink/msgbuff.h | 123 | ||||
-rw-r--r-- | netlink/netlink.c | 527 | ||||
-rw-r--r-- | netlink/netlink.h | 178 | ||||
-rw-r--r-- | netlink/nlsock.c | 405 | ||||
-rw-r--r-- | netlink/nlsock.h | 45 | ||||
-rw-r--r-- | netlink/parser.c | 1141 | ||||
-rw-r--r-- | netlink/parser.h | 153 | ||||
-rw-r--r-- | netlink/pause.c | 331 | ||||
-rw-r--r-- | netlink/permaddr.c | 114 | ||||
-rw-r--r-- | netlink/plca.c | 296 | ||||
-rw-r--r-- | netlink/prettymsg.c | 262 | ||||
-rw-r--r-- | netlink/prettymsg.h | 146 | ||||
-rw-r--r-- | netlink/privflags.c | 158 | ||||
-rw-r--r-- | netlink/pse-pd.c | 193 | ||||
-rw-r--r-- | netlink/rings.c | 237 | ||||
-rw-r--r-- | netlink/rss.c | 230 | ||||
-rw-r--r-- | netlink/settings.c | 1377 | ||||
-rw-r--r-- | netlink/stats.c | 333 | ||||
-rw-r--r-- | netlink/strset.c | 297 | ||||
-rw-r--r-- | netlink/strset.h | 25 | ||||
-rw-r--r-- | netlink/tsinfo.c | 124 | ||||
-rw-r--r-- | netlink/tunnels.c | 236 | ||||
-rw-r--r-- | pcnet32.c | 249 | ||||
-rw-r--r-- | qsfp.c | 1059 | ||||
-rw-r--r-- | qsfp.h | 636 | ||||
-rw-r--r-- | realtek.c | 689 | ||||
-rw-r--r-- | rxclass.c | 1459 | ||||
-rwxr-xr-x | scripts/ethtool-import-uapi | 67 | ||||
-rw-r--r-- | sfc.c | 3928 | ||||
-rw-r--r-- | sff-common.c | 387 | ||||
-rw-r--r-- | sff-common.h | 214 | ||||
-rw-r--r-- | sfpdiag.c | 281 | ||||
-rw-r--r-- | sfpid.c | 505 | ||||
-rw-r--r-- | shell-completion/bash/ethtool | 1281 | ||||
-rw-r--r-- | smsc911x.c | 91 | ||||
-rw-r--r-- | stmmac.c | 71 | ||||
-rw-r--r-- | test-cmdline.c | 340 | ||||
-rw-r--r-- | test-common.c | 385 | ||||
-rw-r--r-- | test-features.c | 555 | ||||
-rw-r--r-- | tg3.c | 42 | ||||
-rw-r--r-- | tse.c | 112 | ||||
-rw-r--r-- | uapi/linux/const.h | 36 | ||||
-rw-r--r-- | uapi/linux/ethtool.h | 2206 | ||||
-rw-r--r-- | uapi/linux/ethtool_netlink.h | 984 | ||||
-rw-r--r-- | uapi/linux/genetlink.h | 103 | ||||
-rw-r--r-- | uapi/linux/hdlc/ioctl.h | 94 | ||||
-rw-r--r-- | uapi/linux/if.h | 296 | ||||
-rw-r--r-- | uapi/linux/if_addr.h | 77 | ||||
-rw-r--r-- | uapi/linux/if_ether.h | 181 | ||||
-rw-r--r-- | uapi/linux/if_link.h | 1427 | ||||
-rw-r--r-- | uapi/linux/libc-compat.h | 267 | ||||
-rw-r--r-- | uapi/linux/neighbour.h | 224 | ||||
-rw-r--r-- | uapi/linux/net_tstamp.h | 205 | ||||
-rw-r--r-- | uapi/linux/netlink.h | 379 | ||||
-rw-r--r-- | uapi/linux/posix_types.h | 38 | ||||
-rw-r--r-- | uapi/linux/rtnetlink.h | 830 | ||||
-rw-r--r-- | uapi/linux/socket.h | 38 | ||||
-rw-r--r-- | uapi/linux/stddef.h | 58 | ||||
-rw-r--r-- | uapi/linux/types.h | 57 | ||||
-rw-r--r-- | vioc.c | 34 | ||||
-rw-r--r-- | vmxnet3.c | 199 |
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 @@ -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> @@ -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. + @@ -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) + @@ -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. + @@ -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 @@ -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(®, ®s->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; +} @@ -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; +} @@ -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 @@ -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; +} + @@ -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; +} @@ -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; +} + @@ -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 *)(®s->data[0] + regs->len); + regs = (struct ethtool_regs *)(®s->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 @@ -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; +} @@ -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; +} @@ -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; +} @@ -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; +} + @@ -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__ */ @@ -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; +} + @@ -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; +} @@ -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); +} + @@ -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; +} @@ -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 @@ -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 = ®->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); + } +} + @@ -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; +} @@ -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(®, ®s->data[i], sizeof(reg)); + if (reg) + fprintf(stdout, "0x%04x\t0x%08x\n", i, reg); + + } + fprintf(stdout, "\n"); + return 0; +} @@ -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 */ @@ -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; +} |