MERGE trunk -r588:681 -> branches/stable-1.1 (Bugfixes, Ack behaviour, CoE via Sysfs, PDO ranges, 2.6.17 support, improved autotools, documentation)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/AUTHORS Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,1 @@
+Florian Pose <fp@igh-essen.com>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/COPYING Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin 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 Library General
+Public License instead of this License.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ChangeLog Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,2525 @@
+2006-11-03 15:35 fp
+
+ * master/fsm.c: Fix: Configuration of process data sync managers
+ now in PREOP.
+
+2006-11-03 14:18 fp
+
+ * Makefile.am, configure.ac, devices/Makefile.am,
+ examples/mini/Makefile.am, examples/msr/Makefile.am,
+ examples/rtai/Makefile.am, master/Makefile.am: Improved
+ installation, added quick modules_install target.
+
+2006-11-03 12:18 fp
+
+ * master/fsm_coe.c: Fixed CoE Upload Request, now 10 bytes of
+ Mailbox Service Data.
+
+2006-11-03 12:17 fp
+
+ * master/fsm.c: Allow CoE requests, even if slave has error flag
+ set.
+
+2006-11-03 10:00 fp
+
+ * devices/8139too-2.6.13-ethercat.c,
+ devices/8139too-2.6.17-ethercat.c: Fixed memory barrier bug in
+ 8139too drivers.
+
+2006-11-02 19:08 fp
+
+ * devices/8139too-2.6.13-ethercat.c, devices/8139too-2.6.13-orig.c,
+ devices/8139too-2.6.17-ethercat.c, devices/8139too-2.6.17-orig.c,
+ devices/8139too.c, devices/Kbuild, devices/Makefile.am,
+ devices/original_8139too.c: Added device module for kernel
+ 2.6.17. Still slow?
+
+2006-11-02 14:20 fp
+
+ * examples/mini/mini.c: Mini example with KBUS again.
+
+2006-10-31 15:32 fp
+
+ * configure.ac, master/debug.c: Fixed debug interfaces.
+
+2006-10-31 14:25 fp
+
+ * master/fsm.c: Fixed EEPROM writing.
+
+2006-10-31 11:12 fp
+
+ * master/fsm_change.c: Fixed state change FSM again.
+
+2006-10-31 10:16 fp
+
+ * configure.ac: Removed checking for depmod in configure.
+
+2006-10-30 15:39 fp
+
+ * master/master.c, master/slave.c: Remove estimated sync manager
+ sizes when leaving operation mode.
+
+2006-10-27 15:07 fp
+
+ * examples/msr/Makefile.am: Fixed dist files for MSR example.
+
+2006-10-27 15:04 fp
+
+ * master/slave.c: BUGFIX: Remove SDO configurations when leaving
+ OPERATION mode.
+
+2006-10-27 15:01 fp
+
+ * master/slave.c: SDO configurations in Sysfs.
+
+2006-10-27 15:01 fp
+
+ * examples/mini/mini.c: minimal example with SDO configs.
+
+2006-10-27 14:41 fp
+
+ * master/fsm.c, master/fsm.h, master/master.c: New state in
+ slaveconf FSM: Clear FMMU configurations after INIT.
+
+2006-10-27 13:59 fp
+
+ * include/ecrt.h, master/fsm.c, master/fsm_coe.c, master/master.c,
+ master/slave.c: Print certain warnings only at debug_level.
+
+2006-10-27 13:58 fp
+
+ * master/ethernet.c: Minor output changes in ethernet.c
+
+2006-10-27 13:31 fp
+
+ * master/fsm.c, master/fsm_coe.c, master/master.c: Print certain
+ logs only at debug_level.
+
+2006-10-27 13:20 fp
+
+ * master/slave.c: Added slave flags to Sysfs.
+
+2006-10-27 12:45 fp
+
+ * examples/mini/mini.c: Minor changes to minimal example.
+
+2006-10-27 12:45 fp
+
+ * master/fsm.c, master/fsm.h, master/master.c: Explicit use of
+ slave configuration FSM in ecrt_master_activate() and
+ ecrt_master_release().
+
+2006-10-27 12:43 fp
+
+ * master/debug.c: Minor output changes in master/debug.c.
+
+2006-10-27 11:15 fp
+
+ * examples/mini/mini.c, master/fsm.c, master/fsm_change.c,
+ master/fsm_change.h: State acknowledgement in master state
+ machine.
+
+2006-10-27 10:41 fp
+
+ * master/globals.h, master/module.c: Changed output of
+ ec_state_string() for error flag to "+ ERROR".
+
+2006-10-27 09:57 fp
+
+ * examples/mini/mini.c, examples/msr/msr_sample.c: Minor changes in
+ example headers.
+
+2006-10-27 09:46 fp
+
+ * examples/msr/Makefile.am, examples/msr/init.sh,
+ examples/msr/msr_load, examples/msr/msr_param.h,
+ examples/msr/msr_sample.c, examples/msr/msr_unload: MSR example:
+ Replaced msr_load and msr_unload with init script, removed
+ msr_param.h
+
+2006-10-27 09:33 fp
+
+ * examples/rtai/Kbuild: Added missing RTAI include path to RTAI
+ example.
+
+2006-10-27 09:29 fp
+
+ * examples/mini/mini.c, examples/msr/msr_sample.c,
+ examples/rtai/rtai_sample.c, include/ecrt.h, master/master.c:
+ Moved functionality of ecrt_master_deactivate() (now deprecated)
+ to ecrt_master_release().
+
+2006-10-27 09:19 fp
+
+ * master/canopen.c, master/canopen.h, master/domain.c,
+ master/domain.h, master/fsm.c, master/master.c, master/master.h,
+ master/module.c, master/slave.c, master/slave.h: FIX: Introduced
+ destroy() functions for kobject-derived classes and thus fixed
+ memory leak.
+
+2006-10-26 16:45 fp
+
+ * Makefile.am: Added missing files to dist.
+
+2006-10-26 16:29 fp
+
+ * examples/mini/mini.c, master/datagram.c, master/domain.c,
+ master/domain.h, master/ethernet.c, master/fsm.c, master/fsm.h,
+ master/fsm_coe.c, master/master.c, master/master.h,
+ master/module.c, master/slave.c, master/slave.h: Persistent slave
+ lists.
+
+2006-10-25 16:53 fp
+
+ * devices/Kbuild, master/Kbuild: Minor changes in Kbuild files.
+
+2006-10-25 16:49 fp
+
+ * Makefile.am, configure.ac, devices/8139too.c, devices/Kbuild,
+ globals.h, master/Kbuild, master/globals.h, master/master.c,
+ master/module.c: Compiler flag -DSVNREV only for module.c to
+ avoid unnecessary recompiling.
+
+2006-10-25 13:32 fp
+
+ * master/fsm_change.c: Fixes in state change FSM.
+
+2006-10-25 07:41 fp
+
+ * master/fsm.c: SDO dictionary and SDO access only in IDLE mode,
+ because of kmalloc()s.
+
+2006-10-24 15:14 fp
+
+ * master/canopen.c, master/fsm.c, master/master.c, master/master.h:
+ SDO uploading via Sysfs without master FSM calling wake_up().
+
+2006-10-24 12:06 fp
+
+ * Makefile.am, configure.ac: Altered configure --with-linux
+ parameter to --with-linux-dir; removed depmod call.
+
+2006-10-24 10:37 fp
+
+ * master/fsm_change.c: Increased state change checking timeout to
+ 1s.
+
+2006-10-24 08:41 fp
+
+ * master/fsm_change.c: Output state names instead of codes in
+ change FSM.
+
+2006-10-24 08:09 fp
+
+ * master/fsm.c: Minot output changes in fsm.c
+
+2006-10-24 08:00 fp
+
+ * master/Kbuild, master/Makefile.am, master/fsm.c, master/fsm.h,
+ master/fsm_coe.c, master/fsm_coe.h: Layed out CoE state machine.
+
+2006-10-23 14:00 fp
+
+ * master/fsm.c, master/fsm.h, master/fsm_change.c,
+ master/fsm_change.h, master/fsm_sii.c, master/fsm_sii.h,
+ master/master.c: Removed state machine running() methods.
+
+2006-10-23 13:45 fp
+
+ * master/Kbuild, master/Makefile.am, master/fsm.c, master/fsm.h,
+ master/fsm_change.c, master/fsm_change.h: Layed out state change
+ state machine.
+
+2006-10-23 12:59 fp
+
+ * master/Kbuild, master/Makefile.am, master/fsm.c, master/fsm.h,
+ master/fsm_sii.c, master/fsm_sii.h: Layed out SII state machine.
+
+2006-10-20 15:35 fp
+
+ * Makefile.am, devices/Makefile.am, master/Makefile.am: Minor
+ Makefile changes.
+
+2006-10-20 13:15 fp
+
+ * master/mailbox.c: Improved output at mailbox error.
+
+2006-10-20 13:05 fp
+
+ * master/canopen.c, master/canopen.h, master/fsm.c, master/fsm.h,
+ master/master.c, master/master.h, master/module.c: Implemented
+ SDO reading via Sysfs.
+
+2006-10-20 13:03 fp
+
+ * master/fsm.c: Always begin with subindex 0 while fetching SDO
+ dictionary.
+
+2006-10-20 12:57 fp
+
+ * master/fsm.c: Set initial state of all slaves to PREOP.
+
+2006-10-20 12:54 fp
+
+ * master/fsm.c, master/globals.h: Added EC_WAIT_SDO_DICT define.
+
+2006-10-20 12:50 fp
+
+ * master/fsm.c: Fixed bug in master state machine.
+
+2006-10-20 12:45 fp
+
+ * master/fsm.c: Better debugging output for SDO dictionaries.
+
+2006-10-20 12:41 fp
+
+ * master/mailbox.c: Display debug data on mailbox error.
+
+2006-10-20 12:33 fp
+
+ * master/fsm.c, master/slave.c, master/slave.h: Better debugging
+ output for SDO dictionaries.
+
+2006-10-20 12:19 fp
+
+ * master/fsm.c: Minor output changes in fsm.c
+
+2006-10-20 10:48 fp
+
+ * master/fsm.c, master/slave.c, master/slave.h: Reconfiguration of
+ slaves, though they are in the correct state at startup.
+
+2006-10-20 10:05 fp
+
+ * master/fsm.c: Guess sync manager settings if mailbox information
+ available.
+
+2006-10-19 14:23 fp
+
+ * master/canopen.c, master/fsm.c, master/slave.c, master/slave.h:
+ Moved SDO dictionary fetching to master FSMs; added SDO parent
+ kobj.
+
+2006-10-19 14:05 fp
+
+ * master/master.c, master/slave.c: Fixed memory leak in master and
+ slave destructors.
+
+2006-10-18 13:19 fp
+
+ * Makefile.am: Added branch info to distdir.
+
+2006-10-18 13:11 fp
+
+ * examples/mini/mini.c, include/ecdb.h, include/ecrt.h,
+ master/domain.c, master/slave.c, master/slave.h:
+ ecrt_domain_register_pdo_range() implemented.
+
+2006-10-18 13:10 fp
+
+ * master/master.c: Do net reset debug level on master reset.
+
+2006-10-18 12:34 fp
+
+ * master/master.c: Added enable bit to sync manager debugging.
+
+2006-10-18 12:04 fp
+
+ * master/master.c: Improved SM and FMMU debugging output.
+
+2006-10-18 11:50 fp
+
+ * master/master.c, master/slave.c, master/slave.h: Added FMMU
+ debugging.
+
+2006-10-18 10:30 fp
+
+ * master/fsm.c: Fixed typo.
+
+2006-10-18 08:59 fp
+
+ * master/domain.c, master/slave.c, master/slave.h: Added
+ ec_slave_validate()
+
+2006-10-17 14:50 fp
+
+ * master/Kbuild, master/Makefile.am, master/canopen.c,
+ master/canopen.h, master/fsm.c, master/fsm.h, master/slave.c,
+ master/slave.h: Implemented fetching of SDO dictionary.
+
+2006-10-17 14:37 fp
+
+ * master/master.c: Minor output changes in master.c
+
+2006-10-17 14:36 fp
+
+ * master/fsm.c: BUGFIX: Fixed faulty state acknowledge behavior.
+
+2006-10-17 14:24 fp
+
+ * master/slave.h: Fixed typo.
+
+2006-10-17 14:22 fp
+
+ * master/fsm.c, master/fsm.h: Renamed fsm->sdodata to
+ fsm->coe_sdodata
+
+2006-10-17 14:19 fp
+
+ * master/domain.c, master/domain.h, master/master.c,
+ master/master.h, master/slave.c, master/slave.h: Removed clear
+ functions from headers of kobject-classes.
+
+2006-10-17 14:15 fp
+
+ * master/fsm.c, master/globals.h, master/module.c, master/slave.c:
+ Added EC_SLAVE_STATE_ACK_ERR to ec_state_string(); added
+ EC_STATE_STRING_SIZE
+
+2006-10-17 14:10 fp
+
+ * master/fsm.c, master/slave.h: Renamed EC_ACK to
+ EC_SLAVE_STATE_ACK_ERR
+
+2006-10-17 07:30 fp
+
+ * master/slave.h: Removed varsized_fields II
+
+2006-10-17 07:27 fp
+
+ * master/slave.c: Corrected subbus product code.
+
+2006-10-17 07:25 fp
+
+ * master/slave.c, master/slave.h: Removed varsized_fields
+
+2006-10-16 14:38 fp
+
+ * master/datagram.c, master/datagram.h, master/device.c,
+ master/device.h, master/fsm.c, master/fsm.h, master/master.c:
+ Fixed serveral races while starting up under high CPU load.
+
+2006-10-16 09:07 fp
+
+ * master/datagram.c, master/datagram.h, master/fsm.c, master/fsm.h,
+ master/master.c: Serveral changes to avoid timeouts under high
+ CPU load.
+
+2006-10-16 08:03 fp
+
+ * master/master.c: Minor output changes.
+
+2006-10-13 20:44 fp
+
+ * master/fsm.c: BUGFIX: Added returns after timeout in SII state
+ machine.
+
+2006-10-13 09:45 fp
+
+ * master/slave.c, master/slave.h: Added ec_slave_has_subbus()
+
+2006-10-13 09:39 fp
+
+ * Makefile.am, configure.ac, examples, examples/Makefile.am,
+ examples/mini/Makefile.am, examples/msr/Makefile.am,
+ examples/rtai/Makefile.am: Distribution makefile for examples.
+
+2006-10-13 09:38 fp
+
+ * ., bootstrap, configure.ac, master/Kbuild, master/Makefile.am,
+ master/device.c, master/device.h, master/globals.h: Debug
+ interfaces not compiled by default.
+
+2006-10-13 09:05 fp
+
+ * master/ethernet.c, master/fsm.c: Removed additional loop in
+ coe_down FSM; renamed mbox_type to mbox_prot.
+
+2006-10-12 13:49 fp
+
+ * master/ethernet.c, master/fsm.c, master/mailbox.c,
+ master/mailbox.h: Fixed mailbox bug, added mailbox error codes.
+
+2006-10-11 12:35 fp
+
+ * include/ecdb.h: Added EL5101 PDOs to ecdb.h
+
+2006-10-09 14:47 fp
+
+ * Makefile.am: Minor changes on Makefile.am
+
+2006-10-09 14:43 fp
+
+ * ., Makefile.am, bootstrap, configure.ac, devices/Makefile.am,
+ examples/mini/Makefile.am, examples/msr/Makefile.am,
+ examples/rtai/Makefile.am, include, include/Makefile.am,
+ master/Makefile.am, script, script/Makefile.am: Improved
+ autotools files.
+
+2006-10-09 13:14 ha
+
+ * script/Makefile.am: Added file script/Makefile.am
+
+2006-10-09 12:59 ha
+
+ * Makefile.am, bootstrap, configure.ac, devices/Makefile.am,
+ examples/mini/Makefile.am, examples/msr/Makefile.am,
+ examples/rtai/Makefile.am, master/Makefile.am: Added file
+ ./bootstrap; Added $(DESTDIR) to */Makefile.am make alternate
+ root installs possible
+
+2006-10-05 09:38 ab
+
+ * include/ecdb.h: EL3152 hinzugefügt
+
+2006-09-28 13:24 fp
+
+ * master/module.c: No bus time measuring on device registration.
+
+2006-09-28 13:22 fp
+
+ * devices/8139too.c, devices/Kbuild, master/Kbuild,
+ master/globals.h, master/master.c, master/module.c: Altered
+ master version string.
+
+2006-09-28 12:58 fp
+
+ * master/master.c, master/master.h, master/module.c: Fixed race on
+ duplicate device registering or device unregistering while
+ requesting master.
+
+2006-09-28 08:29 fp
+
+ * INSTALL: Updated INSTALL file.
+
+2006-09-27 15:19 fp
+
+ * documentation/ethercat_doc.tex: Updated documentation.
+
+2006-09-26 16:34 fp
+
+ * master/master.c, master/master.h, master/module.c: Replaced
+ master's reserved flag by atomic_t available.
+
+2006-09-26 16:16 fp
+
+ * master/domain.c, master/master.c, master/master.h,
+ master/module.c: Changes in ecrt_request_master(); minor output
+ changes.
+
+2006-09-26 15:25 fp
+
+ * documentation/ethercat_doc.tex: Documentation: EtherLab-CD;
+ obtaining the DEVICE_INDEX.
+
+2006-09-26 13:25 fp
+
+ * Makefile.am: Minor changes in Makefile.am
+
+2006-09-25 17:20 fp
+
+ * documentation/ethercat_doc.tex: Updated documentation concerning
+ autotools/installation.
+
+2006-09-25 16:05 fp
+
+ * configure.ac: Minor changes.
+
+2006-09-25 16:04 fp
+
+ * Makefile.am: Added documentation to distribution.
+
+2006-09-25 16:02 fp
+
+ * documentation/Makefile, documentation/ethercat_doc.tex,
+ documentation/svn.sty: Improved documentation makefile, removed
+ svn.sty, fixed LaTeX penalties.
+
+2006-09-25 15:55 fp
+
+ * Doxyfile, Makefile.am, doc, documentation: Renamed documentation
+ -> doxygen-output, doc -> documentation.
+
+2006-09-25 15:53 fp
+
+ * doc, doc: Moved doc into trunk
+
+2006-09-25 14:39 fp
+
+ * configure.ac: Fixed --with-linux switch.
+
+2006-09-25 14:13 fp
+
+ * Makefile.am, configure.ac, devices/Makefile.am,
+ examples/mini/Makefile.am, examples/msr/Makefile.am,
+ examples/rtai/Makefile.am, master/Makefile.am: Autotools
+ "--with-linux" switch; "mydist" target
+
+2006-09-25 12:11 fp
+
+ * Makefile.am, devices/Kbuild, master/Kbuild: Added SVN revision to
+ distribution.
+
+2006-09-25 11:12 fp
+
+ * ., configure.ac: Corrected autotools.
+
+2006-09-25 10:24 fp
+
+ * ., Makefile, Makefile.am, devices, devices/Kbuild,
+ devices/Makefile, devices/Makefile.am, ethercat.conf.tmpl,
+ examples/mini, examples/mini/Kbuild, examples/mini/Makefile,
+ examples/mini/Makefile.am, examples/mini/kernel.conf.tmpl,
+ examples/msr, examples/msr/Kbuild, examples/msr/Makefile,
+ examples/msr/Makefile.am, examples/msr/install.sh,
+ examples/msr/kernel.conf.tmpl, examples/rtai,
+ examples/rtai/Kbuild, examples/rtai/Makefile,
+ examples/rtai/Makefile.am, examples/rtai/kernel.conf.tmpl,
+ master, master/Kbuild, master/Makefile, master/Makefile.am:
+ EtherCAT master with Autotools.
+
+2006-09-23 10:56 fp
+
+ * master/domain.c, master/domain.h: Bugfix: ecrt_domain_state()
+ always returned -1.
+
+2006-09-19 14:08 fp
+
+ * master/xmldev.c, master/xmldev.h: Allow only one open() on XML
+ device.
+
+2006-09-19 13:28 fp
+
+ * master/Makefile, master/master.c, master/master.h,
+ master/module.c, master/xmldev.c, master/xmldev.h,
+ script/ethercat.sh: Introduced per-master character devices for
+ XML descriptions.
+
+2006-09-19 13:09 fp
+
+ * script/ethercat.sh: Changed identation in init script.
+
+2006-09-18 14:22 fp
+
+ * xmldaemon, xmldaemon/Makefile, xmldaemon/main.cpp,
+ xmldaemon/pdo.cpp, xmldaemon/pdo.hpp, xmldaemon/pdo_entry.cpp,
+ xmldaemon/pdo_entry.hpp, xmldaemon/slave_device.cpp,
+ xmldaemon/slave_device.hpp, xmldaemon/sync_manager.cpp,
+ xmldaemon/sync_manager.hpp: Added XML daemon.
+
+2006-09-11 09:22 fp
+
+ * script/lsec.pl: lsec: advanced number formatting; no-lines
+ option.
+
+2006-09-11 08:52 fp
+
+ * master/slave.c, script/lsec.pl: Coupler information in slave info
+ file, lines in lsec.
+
+2006-09-08 12:51 fp
+
+ * master/globals.h, master/master.c, master/module.c:
+ Compile/Version info in sysconfig master info file.
+
+2006-09-04 08:29 fp
+
+ * INSTALL: Improved INSTALL file.
+
+2006-09-01 12:55 fp
+
+ * Doxyfile: Updated master version in Doxyfile.
+
+2006-08-31 16:29 fp
+
+ * master/domain.c: Fixed bug in data pointer calculation.
+
+2006-08-29 16:17 fp
+
+ * include/ecdb.h: Added PDO defines to ecdb.h
+
+2006-08-23 07:36 fp
+
+ * TODO: Updated TODO list.
+
+2006-08-17 18:42 fp
+
+ * script/install.sh: Fixed bug in install script.
+
+2006-08-15 09:36 fp
+
+ * master/slave.c: Removed unused code.
+
+2006-08-15 09:22 fp
+
+ * examples/mini/mini.c: Updated mini example.
+
+2006-08-15 09:22 fp
+
+ * master/master.c, master/master.h: Removed delayed datagram
+ statistics.
+
+2006-08-15 09:21 fp
+
+ * script/install.sh: Bugfix in install script.
+
+2006-08-15 08:48 fp
+
+ * master/datagram.h, master/master.c: Improved datagram reception
+ in sync_io and frame dequeuing.
+
+2006-08-15 08:09 fp
+
+ * master/datagram.c, master/datagram.h, master/domain.c,
+ master/domain.h, master/ethernet.c, master/ethernet.h,
+ master/fsm.c, master/fsm.h, master/master.c, master/master.h:
+ Replaced longer cycle timestamps with jiffies.
+
+2006-08-09 15:00 fp
+
+ * include/ecdb.h, master/domain.c, master/fsm.c, master/slave.c,
+ master/slave.h: Added missing code documentation.
+
+2006-08-08 13:07 fp
+
+ * examples/rtai/rtai_sample.c: RTAI example with INHIBIT time.
+
+2006-08-08 13:06 fp
+
+ * master/master.c: Added EoE rate in KB/s to master info.
+
+2006-08-08 13:06 fp
+
+ * master/ethernet.c: Fixed EoE debugging.
+
+2006-08-08 12:07 fp
+
+ * TODO, master/domain.c, master/ethernet.c, master/ethernet.h,
+ master/fsm.c, master/fsm.h, master/master.c, master/slave.c:
+ Minor changes.
+
+2006-08-05 07:11 fp
+
+ * master/fsm.c: New transition in slave conf state machine: Skip
+ fmmu and SDO configuration.
+
+2006-08-04 15:37 fp
+
+ * master/master.c: Minor output fix.
+
+2006-08-04 15:31 fp
+
+ * master/ethernet.c, master/ethernet.h, master/master.c: EoE rate
+ statistics.
+
+2006-08-04 13:49 fp
+
+ * master/master.c, master/master.h: Better EoE and Idle timing
+ measurement.
+
+2006-08-04 09:53 fp
+
+ * master/master.c, master/master.h: Master information, timing.
+
+2006-08-04 09:35 fp
+
+ * master/slave.c: Minor fix: Output of SysFS accepting new slave
+ state.
+
+2006-08-03 20:11 fp
+
+ * master/domain.c, master/domain.h, master/master.c,
+ master/master.h: New statistic outputs to avoid blasting the
+ logs.
+
+2006-08-03 19:47 fp
+
+ * master/device.c, master/master.c, master/master.h: Renamed
+ ec_master_receive() to ec_master_receive_datagrams().
+
+2006-08-03 19:17 fp
+
+ * master/master.c, master/master.h, master/module.c: Measure bus
+ time.
+
+2006-08-03 16:48 fp
+
+ * examples/mini/mini.c, master/fsm.c, master/fsm.h, master/slave.c,
+ master/slave.h: SDO configuration interface, SDO download state
+ machine.
+
+2006-08-03 16:47 fp
+
+ * master/mailbox.h: Removed deprecated mailbox function headers.
+
+2006-08-03 16:46 fp
+
+ * include/ecdb.h: Added Beckhoff EL5001 to slave DB.
+
+2006-08-03 16:14 fp
+
+ * master/master.c, master/master.h: Fixed bug with
+ ec_master_eoe_start() does new coupling all the time.
+
+2006-08-03 12:51 fp
+
+ * TODO, examples/mini/mini.c, examples/msr/msr_sample.c,
+ examples/rtai/rtai_sample.c, include/ecdb.h, include/ecrt.h,
+ master/Makefile, master/canopen.c, master/datagram.c,
+ master/datagram.h, master/domain.c, master/domain.h,
+ master/ethernet.c, master/ethernet.h, master/fsm.c, master/fsm.h,
+ master/globals.h, master/mailbox.c, master/master.c,
+ master/master.h, master/module.c, master/slave.c, master/slave.h,
+ master/types.c, master/types.h, script/ethercat.sh,
+ script/lsec.pl: VERSION 1.1: New realtime interface, only state
+ machines.
+
+2006-08-02 23:16 fp
+
+ * script/ethercat.sh: Indentication in init script.
+
+2006-08-02 16:18 fp
+
+ * script/ethercat.sh: Fixed bug in start script.
+
+2006-08-01 18:37 fp
+
+ * master/ethernet.c: Improved EoE state machine.
+
+2006-08-01 15:19 fp
+
+ * master/master.c, master/module.c, script/ethercat.sh,
+ script/sysconfig: Renamed master parameter ec_eoe_devices to
+ ec_eoeif_count.
+
+2006-08-01 14:15 fp
+
+ * master/slave.c: Fixed bug with bit-sized PDOs leading to wrong
+ Sync-Manager sizes.
+
+2006-07-31 18:52 fp
+
+ * master/ethernet.c: Minor changes.
+
+2006-07-28 07:36 fp
+
+ * master/master.c: Renamed ec_master_idle() to
+ ec_master_idle_run().
+
+2006-07-25 10:19 fp
+
+ * script/install.sh: Remove old ec_list before creating link.
+
+2006-07-25 10:08 fp
+
+ * script/ec_list.pl, script/install.sh, script/lsec.pl: renamed
+ ec_list script to lsec.
+
+2006-07-25 10:05 fp
+
+ * master/domain.c, master/slave.c, script/ec_list.pl: Renamed Sysfs
+ attributes.
+
+2006-07-25 10:04 fp
+
+ * master/ethernet.c: Minor changes.
+
+2006-07-25 10:03 fp
+
+ * master/master.c: Reading of eeprom_write_enable.
+
+2006-07-20 11:59 fp
+
+ * master/fsm.c: Minor changes.
+
+2006-07-20 08:39 fp
+
+ * master/fsm.c: Renamed ACK2 state to CHECK ACK.
+
+2006-07-19 16:36 fp
+
+ * master/fsm.c: Better slave scan sub state machine.
+
+2006-07-19 16:18 fp
+
+ * master/fsm.c: Better slaveconf sub state machine.
+
+2006-07-19 15:28 fp
+
+ * master/fsm.c: Separated slave scan sub state machine and slave
+ configuration sub state machine.
+
+2006-07-19 13:18 fp
+
+ * FEATURES, README: Removed Freerun out of FEATURES and README.
+
+2006-07-19 13:15 fp
+
+ * master/fsm.c, master/master.c, master/master.h, master/module.c,
+ master/slave.c: Renamed FREERUN mode to IDLE mode.
+
+2006-07-19 13:08 fp
+
+ * master/master.c, master/master.h, master/module.c: Renamed IDLE
+ mode to ORPHANED mode.
+
+2006-07-19 12:57 fp
+
+ * master/fsm.c: Better master state machines.
+
+2006-07-18 16:48 fp
+
+ * master/master.c: SysFS entry for debug level.
+
+2006-07-18 16:46 fp
+
+ * master/slave.c: Ack timeout 100ns in simple IO.
+
+2006-07-18 16:42 fp
+
+ * master/fsm.c: Ack timeout in FSM.
+
+2006-07-18 16:40 fp
+
+ * master/fsm.c: Prefer EEPROM sync manager information also in FSM.
+
+2006-07-18 16:09 fp
+
+ * script/ethercat.sh: Sleep times in startup script.
+
+2006-07-17 13:01 fp
+
+ * master/fsm.c, master/master.c, master/master.h, master/slave.c,
+ master/slave.h: Prefer EEPROM sync manager information for
+ config.
+
+2006-07-17 12:58 fp
+
+ * master/fsm.c: Added missing AL status code messages.
+
+2006-07-06 08:39 fp
+
+ * master/slave.c: Avoided crashes on kernel 2.4 acc. to patch by M.
+ Schwerin.
+
+2006-07-06 08:38 fp
+
+ * master/mailbox.c, master/master.c: Removed warnings acc. to patch
+ by M. Schwerin.
+
+2006-07-06 08:31 fp
+
+ * master/datagram.h, master/debug.c, master/domain.c,
+ master/ethernet.c: Applied include patch by M. Schwerin.
+
+2006-07-06 08:23 fp
+
+ * master/Makefile, master/canopen.c, master/command.c,
+ master/command.h, master/datagram.c, master/datagram.h,
+ master/domain.c, master/domain.h, master/ethernet.c,
+ master/ethernet.h, master/fsm.c, master/fsm.h, master/globals.h,
+ master/mailbox.c, master/mailbox.h, master/master.c,
+ master/master.h, master/slave.c, master/slave.h: Renamed command
+ structure to datagram.
+
+2006-06-27 20:24 fp
+
+ * master/fsm.c, master/slave.c: Minor changes in fsm.c and slave.c
+
+2006-06-27 20:08 fp
+
+ * master/ethernet.c, master/fsm.c, master/master.c, master/slave.c,
+ master/slave.h: Slave: state_error -> error_flag, error_flag only
+ in slave state machine.
+
+2006-06-27 19:46 fp
+
+ * master/fsm.c, master/slave.c, master/types.c, master/types.h:
+ Introducing infrastructural slaves that do not contain process
+ data.
+
+2006-06-27 19:34 fp
+
+ * script/ethercat.sh: Enhancements and bugfixes on bridging script.
+
+2006-06-27 19:33 fp
+
+ * include/ecrt.h: Minor changes to ecrt.h.
+
+2006-06-27 19:31 fp
+
+ * Makefile, devices/Makefile, master/Makefile: Removing
+ "Modules.symvers" on make clean.
+
+2006-06-26 16:05 fp
+
+ * devices/ecdev.h, include/ecrt.h, master/canopen.c,
+ master/ethernet.c, master/ethernet.h, master/fsm.c, master/fsm.h,
+ master/master.h, master/module.c, master/types.h: Added missing
+ documentation.
+
+2006-06-26 15:33 fp
+
+ * script/ethercat.sh, script/sysconfig: EoE bridge default gateway.
+
+2006-06-26 14:53 fp
+
+ * examples/mini, examples/mini/Makefile,
+ examples/mini/kernel.conf.tmpl, examples/msr,
+ examples/msr/kernel.conf.tmpl, examples/rtai,
+ examples/rtai/Makefile, examples/rtai/kernel.conf.tmpl:
+ Consistent example makefiles.
+
+2006-06-26 14:36 fp
+
+ * script/ethercat.sh, script/sysconfig: Configuring EoE bridge at
+ startup.
+
+2006-06-26 14:35 fp
+
+ * ., devices, master: Ignoring Modules.symvers
+
+2006-06-26 14:20 fp
+
+ * Makefile, devices/Makefile, ethercat.conf.tmpl, master/Makefile:
+ Better modules_install targets.
+
+2006-06-26 11:28 fp
+
+ * examples/mini/mini.c, master/types.c: types.c: BK1120 outputs and
+ EL1004.
+
+2006-06-21 10:09 fp
+
+ * master/canopen.c, master/ethernet.c, master/ethernet.h,
+ master/mailbox.c, master/mailbox.h, master/slave.c,
+ master/slave.h: Removed mbox_command out of slave.
+
+2006-06-21 10:08 fp
+
+ * master/master.c: Removed debugging output.
+
+2006-06-21 09:11 fp
+
+ * TODO: Modified TODO.
+
+2006-06-21 09:09 fp
+
+ * include/ecrt.h, master/mailbox.c: Minor changes.
+
+2006-06-20 14:40 fp
+
+ * examples/mini/mini.c, include/ecrt.h, master/domain.c,
+ master/fsm.c, master/master.c, master/master.h, master/slave.c,
+ master/slave.h, master/types.c: Variable-sized data fields,
+ BK1120.
+
+2006-06-14 10:23 fp
+
+ * master/master.c: Bugfix: Freerun mode not stopped cleanly on
+ device unloading.
+
+2006-06-12 15:14 fp
+
+ * devices/8139too.c: Minor changes in 8139too driver.
+
+2006-06-12 14:37 fp
+
+ * Makefile, devices/8139too.c, devices/Makefile, master/Makefile,
+ script/install.sh: Applied patches by Maximilian Schwerin.
+
+2006-06-06 11:59 fp
+
+ * TODO: Updated things to do.
+
+2006-06-06 09:54 fp
+
+ * FEATURES, README: Added FEATURES file.
+
+2006-06-02 14:25 fp
+
+ * master/fsm.c, master/fsm.h, master/slave.c, master/slave.h:
+ EEPROM writing via SysFS.
+
+2006-06-02 12:01 fp
+
+ * master/master.c, master/master.h: EEPROM write enable SysFS
+ entry.
+
+2006-06-02 09:02 fp
+
+ * devices/Makefile, examples/mini/Makefile, examples/msr/Makefile,
+ examples/rtai/Makefile, master/Makefile: Consistent Makefiles.
+
+2006-06-02 08:38 fp
+
+ * master/fsm.c, master/fsm.h, master/slave.c, master/slave.h: Read
+ complete eeprom data from slave and map it into SysFS.
+
+2006-05-29 12:17 fp
+
+ * examples/mini, examples/mini/Makefile,
+ examples/mini/ethercat.conf.tmpl, examples/mini/kernel.conf.tmpl,
+ examples/rtai/Makefile, examples/rtai/kernel.conf.tmpl: MERGE
+ branches/srable-1.0 -r426:427 -> trunk (config files)
+
+2006-05-29 07:48 fp
+
+ * master/slave.c, master/slave.h, script/ec_list.pl: Changed EEPROM
+ string/property names and changed sii_desc property to sii_name.
+
+2006-05-29 07:45 fp
+
+ * TODO: Updated TODO list.
+
+2006-05-26 14:35 fp
+
+ * master/fsm.c: Corrected output at slave count change.
+
+2006-05-26 14:28 fp
+
+ * master/ethernet.c, master/master.c: Better state-dependent
+ behaviour for EoE-capable slaves.
+
+2006-05-26 14:26 fp
+
+ * master/fsm.c, master/fsm.h, master/slave.c, master/slave.h:
+ Correct behaviour of master state machine in RT mode.
+
+2006-05-22 09:16 fp
+
+ * examples/rtai/rtai_sample.c, master/fsm.c, master/globals.h,
+ master/master.c: Added some documentation.
+
+2006-05-22 08:38 fp
+
+ * devices/8139too.c, devices/Makefile, devices/ecdev.h,
+ devices/original_8139too.c, examples/mini/Makefile,
+ examples/mini/ethercat.conf.tmpl, examples/mini/mini.c,
+ examples/msr/Makefile, examples/msr/install.sh,
+ examples/msr/kernel.conf.tmpl, examples/msr/msr_load,
+ examples/msr/msr_param.h, examples/msr/msr_sample.c,
+ examples/msr/msr_unload, examples/msr/msrserv.pl,
+ examples/rtai/Makefile, examples/rtai/kernel.conf.tmpl,
+ examples/rtai/rtai_sample.c, include/ecrt.h, master/Makefile,
+ master/canopen.c, master/command.c, master/command.h,
+ master/device.c, master/device.h, master/domain.c,
+ master/domain.h, master/doxygen.c, master/ethernet.c,
+ master/ethernet.h, master/globals.h, master/mailbox.c,
+ master/mailbox.h, master/master.c, master/master.h,
+ master/module.c, master/slave.c, master/slave.h, master/types.c,
+ master/types.h, script/ec_list.pl, script/ethercat.sh,
+ script/install.sh, script/sysconfig: Aligned property values on
+ all files.
+
+2006-05-22 07:50 fp
+
+ * script/ec_list.pl: ec_list shows SII device description on
+ demand.
+
+2006-05-22 07:34 fp
+
+ * master/globals.h, master/slave.c: SysFS write access for slave
+ state.
+
+2006-05-19 14:05 fp
+
+ * master/fsm.c: Read current AL status of slaves while scanning
+ bus.
+
+2006-05-19 13:58 fp
+
+ * master/fsm.c: Link down treated as topology change in state
+ machine.
+
+2006-05-19 13:39 fp
+
+ * master/master.c: Fixed bug with coupling multiple EoE handlers.
+
+2006-05-19 13:38 fp
+
+ * master/master.c: Changed free-run frequency to HZ.
+
+2006-05-19 13:23 fp
+
+ * master/ethernet.c, master/ethernet.h, master/fsm.c, master/fsm.h,
+ master/globals.h, master/master.c, master/master.h,
+ master/module.c, master/slave.c, master/slave.h,
+ script/ethercat.sh, script/sysconfig: EoE in Free-Run mode;
+ Finished slave configuration state machine.
+
+2006-05-19 09:56 fp
+
+ * Makefile, ethercat.conf.tmpl, ethercat.sh, install.sh, script,
+ script/ethercat.sh, script/install.sh, script/sysconfig, tools:
+ Created scripts directory and improved install and init scripts.
+
+2006-05-18 17:49 fp
+
+ * master/module.c: Minor changes in module.c
+
+2006-05-18 12:47 fp
+
+ * ethercat.sh: Minor changes in init script 2.
+
+2006-05-18 12:45 fp
+
+ * ethercat.sh: Minor changes in init script.
+
+2006-05-18 12:35 fp
+
+ * Makefile, README, devices/8139too.c, devices/Makefile,
+ devices/ecdev.h, ethercat.sh, examples/mini/Makefile,
+ examples/mini/mini.c, examples/msr/Makefile,
+ examples/msr/install.sh, examples/msr/msr_param.h,
+ examples/msr/msr_sample.c, examples/msr/msrserv.pl,
+ examples/rtai/Makefile, examples/rtai/rtai_sample.c,
+ include/ecrt.h, install.sh, master/Makefile, master/canopen.c,
+ master/command.c, master/command.h, master/debug.c,
+ master/debug.h, master/device.c, master/device.h,
+ master/domain.c, master/domain.h, master/doxygen.c,
+ master/ethernet.c, master/ethernet.h, master/fsm.c, master/fsm.h,
+ master/globals.h, master/mailbox.c, master/mailbox.h,
+ master/master.c, master/master.h, master/module.c,
+ master/slave.c, master/slave.h, master/types.c, master/types.h,
+ tools/ec_list.pl: Changed license headers and added EtherCAT
+ license notice.
+
+2006-05-17 12:15 fp
+
+ * ethercat.sh: Added dependencies to RC script.
+
+2006-05-17 11:16 fp
+
+ * ethercat.sh: Added status function to RC script.
+
+2006-05-17 10:52 fp
+
+ * ethercat.sh: Better RC script.
+
+2006-05-17 09:28 fp
+
+ * Makefile, install.sh: Set permissions in install script.
+
+2006-05-17 09:22 fp
+
+ * master/master.h: Minor changes in master.h
+
+2006-05-16 14:23 fp
+
+ * examples/msr/msr_sample.c: SSI sensor changes in MSR example
+
+2006-05-16 13:03 fp
+
+ * master/master.c, master/master.h: Removed EoE workqueue option.
+
+2006-05-16 11:57 fp
+
+ * master/Makefile, master/command.h, master/fsm.c, master/fsm.h,
+ master/master.c, master/master.h, master/slave.c, master/slave.h:
+ Added finite state machine (FSM) processing.
+
+2006-05-15 12:57 fp
+
+ * master/command.c: Removed unnecessary includes.
+
+2006-05-12 14:48 fp
+
+ * master/master.c, master/master.h: Started freerun state machine
+ development.
+
+2006-05-12 12:40 fp
+
+ * master/ethernet.c, master/ethernet.h, master/master.c: No master
+ locking, if no EoE devices are "up".
+
+2006-05-12 12:38 fp
+
+ * examples/msr/msr_sample.c: Corrected MSR example.
+
+2006-05-12 10:13 fp
+
+ * examples/rtai/rtai_sample.c: Smaller fixes on RTAI example.
+
+2006-05-12 10:12 fp
+
+ * examples/msr, examples/msr/Makefile,
+ examples/msr/kernel.conf.tmpl, examples/msr/msr_load,
+ examples/msr/msr_rt.c, examples/msr/msr_sample.c,
+ examples/msr/msr_unload, examples/msr/rt.conf.tmpl: MSR example
+ finished.
+
+2006-05-11 13:29 fp
+
+ * devices/8139too.c, master/Makefile, master/debug.c,
+ master/debug.h, master/device.c, master/device.h: Added debug
+ interface for network monitors.
+
+2006-05-11 09:24 fp
+
+ * examples/rtai/Makefile, examples/rtai/rtai_sample.c: RTAI sample
+ with optimized jitter.
+
+2006-05-11 09:23 fp
+
+ * master/master.h: EoE processing with kernel timer again.
+
+2006-05-10 13:56 fp
+
+ * master/globals.h, master/master.c: Added EC_EOE_FREQUENCY
+
+2006-05-10 11:51 fp
+
+ * examples/rtai/rtai_sample.c: EoE works with RTAI.
+
+2006-05-10 11:51 fp
+
+ * examples/mini/mini.c, master/master.c, master/master.h: EoE with
+ workqueue; bugfix in ec_master_init()
+
+2006-05-10 11:33 fp
+
+ * examples/rtai/Makefile, examples/rtai/rtai_sample.c: Applied RTAI
+ interface to RTAI example.
+
+2006-05-10 07:58 fp
+
+ * examples/msr, examples/msr/msr_rt.c, examples/rt: Renamed rt
+ example to msr.
+
+2006-05-10 07:57 fp
+
+ * examples/mini/mini.c, examples/rt/msr_rt.c,
+ examples/rtai/rtai_sample.c: Applied new path information to
+ example modules.
+
+2006-05-10 07:53 fp
+
+ * examples/rtai, examples/rtai/Makefile,
+ examples/rtai/ethercat.conf.tmpl, examples/rtai/kernel.conf.tmpl,
+ examples/rtai/mini.c, examples/rtai/rtai_sample.c: Changed RTAI
+ sample naming.
+
+2006-05-10 07:44 fp
+
+ * examples/rtai: Added rtai example.
+
+2006-05-10 07:41 fp
+
+ * examples, examples/mini, examples/rt, mini, rt: Added examples
+ directory.
+
+2006-05-09 10:13 fp
+
+ * master/ethernet.c: EoE documentation updates.
+
+2006-05-09 09:45 fp
+
+ * master/ethernet.c, master/ethernet.h: EoE: State machine with
+ function pointers, documentation.
+
+2006-05-08 16:46 fp
+
+ * master/ethernet.c, master/ethernet.h: EoE - TX fragmenting works.
+ TCP traffic possible.
+
+2006-05-05 14:45 fp
+
+ * master/ethernet.c: EoE: Corrected faulty MAC address.
+
+2006-05-05 13:07 fp
+
+ * master/ethernet.c: EoE: TCP traffic possible; TX fragmenting to
+ do.
+
+2006-05-05 07:20 fp
+
+ * master/ethernet.c, master/ethernet.h, master/globals.h,
+ master/master.c, master/master.h: EoE frame queuing, receiving of
+ fragmented frames; no TCP possible yet.
+
+2006-05-03 08:01 fp
+
+ * master/ethernet.c: removed unnecessary private structure in eoe
+ net_device.
+
+2006-05-03 07:47 fp
+
+ * master/ethernet.c, master/ethernet.h, master/globals.h: moved
+ stats into eoe struct; persistent rx-skb; tx queue.
+
+2006-05-03 07:19 fp
+
+ * master/device.c, master/globals.h, master/master.c: Using kernel
+ Ethernet constants.
+
+2006-04-26 16:43 fp
+
+ * master/ethernet.c, master/ethernet.h: EoE frame receiving;
+ regarding open/stop commands.
+
+2006-04-26 12:12 fp
+
+ * devices/8139too.c: removed MODULE_DEVICE_TABLE in ec_8139too
+ driver (no automatic loading).
+
+2006-04-26 10:15 fp
+
+ * master/command.c, master/command.h, master/ethernet.c,
+ master/master.c, master/master.h, mini/mini.c: command timeout,
+ EoE processing with kernel timer.
+
+2006-04-25 13:45 fp
+
+ * devices/8139too.c: corrected ec_8139too driver name.
+
+2006-04-25 13:43 fp
+
+ * include/ecrt.h, master/master.c, master/master.h, mini/mini.c,
+ rt/msr_rt.c: Prepared EoE processing with workqueue.
+
+2006-04-25 12:15 fp
+
+ * master/doxygen.c: documentation update.
+
+2006-04-25 12:04 fp
+
+ * include/ecrt.h, master/master.c, master/master.h: Locking
+ callback interface.
+
+2006-04-25 11:39 fp
+
+ * master/ethernet.c, master/ethernet.h, master/master.c: EoE
+ net_device implementation.
+
+2006-04-25 11:37 fp
+
+ * mini/mini.c: new topology in mini.c
+
+2006-04-24 12:30 fp
+
+ * master/module.c: Restart free run mode after requesting failed.
+
+2006-04-24 10:28 fp
+
+ * Doxyfile, include/ecrt.h: Documentation update + Doxygen generate
+ LaTeX reference manual.
+
+2006-04-24 10:10 fp
+
+ * ., Doxyfile, devices/8139too.c, devices/ecdev.h, documentation,
+ include/ecrt.h, master/canopen.c, master/command.c,
+ master/command.h, master/device.c, master/device.h,
+ master/domain.c, master/domain.h, master/doxygen.c,
+ master/ethernet.c, master/ethernet.h, master/globals.h,
+ master/mailbox.c, master/mailbox.h, master/master.c,
+ master/master.h, master/module.c, master/slave.c, master/slave.h,
+ master/types.c, master/types.h: Doxygen added interface modules
+ and file documentation.
+
+2006-04-21 13:05 fp
+
+ * INSTALL, README: Separate INSTALL file.
+
+2006-04-21 12:35 fp
+
+ * ., Doxyfile, LICENSE, Makefile, README, devices/8139too.c,
+ devices/Makefile, devices/ecdev.h, documentation, ethercat.sh,
+ include/ecrt.h, install.sh, master/Makefile, master/canopen.c,
+ master/command.c, master/command.h, master/device.c,
+ master/device.h, master/domain.c, master/domain.h,
+ master/doxygen.c, master/ethernet.c, master/ethernet.h,
+ master/globals.h, master/mailbox.c, master/mailbox.h,
+ master/master.c, master/master.h, master/module.c,
+ master/slave.c, master/slave.h, master/types.c, master/types.h,
+ mini/Makefile, mini/mini.c, rt/Makefile, rt/install.sh,
+ rt/msr_param.h, rt/msr_rt.c, rt/msrserv.pl, tools/ec_list.pl:
+ GPLv2 License and enhanced Doxygen output.
+
+2006-04-20 14:34 fp
+
+ * devices/Makefile, master/Makefile, master/globals.h,
+ master/module.c: Introduced version number define.
+
+2006-04-20 13:31 fp
+
+ * Makefile, devices/8139too.c, devices/ecdev.h, include/ecrt.h,
+ master/canopen.c, master/command.c, master/command.h,
+ master/device.c, master/device.h, master/domain.c,
+ master/domain.h, master/ethernet.c, master/ethernet.h,
+ master/globals.h, master/mailbox.c, master/mailbox.h,
+ master/master.c, master/master.h, master/module.c,
+ master/slave.c, master/slave.h, master/types.c, master/types.h,
+ mini/Makefile, mini/mini.c, rt/Makefile, rt/msrserv.pl:
+ Translated all comments and documentation to english language.
+
+2006-04-20 13:19 fp
+
+ * TODO, fragen.txt, todo.txt: Moved fragen.txt outside the source
+ tree, renamed todo.txt to TODO
+
+2006-04-18 13:41 fp
+
+ * README: README: Requirements and realtime.
+
+2006-04-12 12:04 fp
+
+ * devices/8139too.c, devices/Makefile: Fixed devices Makefile.
+
+2006-04-12 10:40 fp
+
+ * devices/8139too.c, devices/Makefile, devices/ecdev.h,
+ master/Makefile, master/device.c, master/master.c,
+ master/master.h, master/module.c, rt/msr_rt.c: Prepared Free-Run
+ mode
+
+2006-04-11 14:39 fp
+
+ * master/slave.c, master/slave.h: Now fetching port physical layer
+ from EEPROM
+
+2006-04-11 14:38 fp
+
+ * todo.txt: Updated todo.txt
+
+2006-04-11 14:12 fp
+
+ * master/master.c, master/slave.c, master/slave.h, rt/msr_rt.c,
+ tools/ec_list.pl: Better calc. of coupler address; coupler
+ address in SysFS; better output of ec_list
+
+2006-04-11 13:03 fp
+
+ * todo.txt: Updated todo.txt
+
+2006-04-11 12:54 fp
+
+ * install.sh, tools, tools/ec_list.pl: ec_list tool for listing the
+ bus in user space
+
+2006-04-11 12:51 fp
+
+ * master/slave.c: Slave (special) type in SysFS
+
+2006-04-11 10:17 fp
+
+ * master/domain.c, master/globals.h, master/master.c,
+ master/module.c, master/slave.c: Macro for SysFS attribute
+ definition
+
+2006-04-11 10:05 fp
+
+ * master/slave.c, master/slave.h: More slave fields in SysFS; DL
+ link status for 4 ports
+
+2006-04-11 09:12 fp
+
+ * master/domain.c, master/master.c, master/master.h,
+ master/slave.c, master/slave.h: Slaves stored in list, slaves in
+ SysFS
+
+2006-04-11 09:08 fp
+
+ * rt, rt/Makefile, rt/ethercat.conf.tmpl, rt/install.sh,
+ rt/msr_load, rt/msr_module.c, rt/msr_rt.c, rt/msr_unload,
+ rt/rt.conf.tmpl: Install script for realtime software
+
+2006-04-11 08:26 fp
+
+ * ., Doxyfile, Makefile, devices, devices/Makefile, master,
+ master/Doxyfile, master/Makefile: Advanced build system
+
+2006-04-10 15:00 fp
+
+ * master/domain.c, master/domain.h, master/master.c,
+ master/master.h: Sourced SysFS attribute show method prototypes
+ out of headers.
+
+2006-04-10 14:25 fp
+
+ * master/domain.c, master/domain.h, master/master.c,
+ master/master.h, master/module.c: MERGE branches/sysfs -> trunk
+ (whole SysFS implementation)
+
+2006-04-10 11:18 fp
+
+ * todo.txt: Updated todo.txt
+
+2006-04-10 10:53 fp
+
+ * master/master.c, todo.txt: Sending of multiple frames
+
+2006-04-10 10:50 fp
+
+ * master/canopen.c, master/slave.c, master/slave.h: Altered CoE
+ "Get object description response" fields
+
+2006-04-10 07:29 fp
+
+ * ethercat.sh: Restart action in RC script
+
+2006-04-07 14:35 fp
+
+ * master/master.c: Fixed bug concerning spuriously shown
+ EoE-Errors.
+
+2006-04-07 14:23 fp
+
+ * Makefile, README, ec_reload.sh, ec_unload.sh: Removed ec-Scripts.
+
+2006-04-07 14:19 fp
+
+ * README, install.sh: Added README file and altered outputs of
+ install.sh
+
+2006-04-07 14:16 fp
+
+ * rt/msr_module.c: Testing with SSI sensor
+
+2006-04-07 10:38 fp
+
+ * Makefile, ethercat.conf.tmpl, ethercat.sh, install.sh: Better
+ installer and startup scripts.
+
+2006-04-07 08:29 fp
+
+ * install.sh: Better installer script
+
+2006-04-07 08:22 fp
+
+ * Makefile, ethercat.conf.tmpl, install.sh: Added install script
+
+2006-04-06 09:32 fp
+
+ * master/domain.c, master/master.c: Better outputs concerning
+ responding slaves.
+
+2006-04-06 09:16 fp
+
+ * include/ecrt.h: Fixed syntax error in EC_READ_BIT macro.
+
+2006-04-05 14:02 fp
+
+ * master/canopen.c, master/globals.h, master/master.c,
+ master/slave.c: AL status code reading after failed state
+ transition.
+
+2006-04-05 13:57 fp
+
+ * master/slave.c: Fixed state acknowledgement.
+
+2006-04-05 13:38 fp
+
+ * fragen.txt: Fragen aktualisiert.
+
+2006-04-05 11:46 fp
+
+ * master/canopen.c, master/master.c, master/module.c,
+ master/slave.c: Bugfix: Discovered memory leak.
+
+2006-04-05 11:44 fp
+
+ * master/domain.c, master/slave.c, master/slave.h: Renamed
+ slave_set_fmmu() to slave_prepare_fmmu().
+
+2006-04-05 09:53 fp
+
+ * master/canopen.c: Fixed invalid format string.
+
+2006-04-05 09:52 fp
+
+ * rt/msr_module.c: Adjusted rt module.
+
+2006-04-05 09:50 fp
+
+ * master/canopen.c, master/slave.c: Bugfix: Crash after failing to
+ fetch SDO dictionary.
+
+2006-04-05 09:04 fp
+
+ * mini/mini.c, rt/msr_module.c: Plugfest: rt und mini angepasst.
+
+2006-04-05 08:58 fp
+
+ * master/master.c, master/slave.c: Plugfest: Configure mailbox for
+ unknown slaves.
+
+2006-04-05 08:47 fp
+
+ * master/mailbox.c, master/mailbox.h: Plugfest:
+ Schönheitskorrekturen an Mailbox.
+
+2006-04-05 08:45 fp
+
+ * master/types.c: Plugfest: Neue Slave-Typen.
+
+2006-04-05 08:23 fp
+
+ * master/device.c, master/globals.h, master/module.c: Plugfest:
+ Globale Debug-Funktionen für Frame-Daten.
+
+2006-04-05 07:55 fp
+
+ * master/master.c, master/master.h, master/module.c: Plugfest:
+ master_open()/close() ausgelagert.
+
+2006-04-05 07:47 fp
+
+ * include/ecrt.h, master/domain.c: Plugfest: domain_process mit
+ const-Zeiger.
+
+2006-04-03 19:19 fp
+
+ * Makefile: Keywords 2
+
+2006-04-03 19:16 fp
+
+ * Makefile, todo.txt: Neue Keywords
+
+2006-04-03 19:13 fp
+
+ * master/slave.c, mini/Makefile, mini/mini.c: Ausgaben verbessert,
+ Mini-Modul angepasst.
+
+2006-04-03 15:47 fp
+
+ * master/ethernet.c: Versuche mit EoE.
+
+2006-04-03 14:12 fp
+
+ * master/Makefile, master/canopen.c, master/ethernet.c,
+ master/ethernet.h, master/mailbox.c, master/mailbox.h,
+ master/master.c, master/master.h, master/slave.c, master/slave.h,
+ master/types.c, master/types.h: Mailbox-Interface ausgelagert,
+ erster EOE-Ansatz.
+
+2006-04-03 10:03 fp
+
+ * include/ecrt.h, master/canopen.c, master/command.c,
+ master/command.h, master/domain.c, master/domain.h,
+ master/master.c, master/master.h, master/slave.c, master/slave.h,
+ rt/msr_module.c, todo.txt: Dynamischer Kommandospeicher,
+ Domain-Kommandos als Liste.
+
+2006-04-02 09:26 fp
+
+ * todo.txt: TO-DO-Liste geaendert.
+
+2006-03-31 09:27 fp
+
+ * master/slave.c, master/slave.h, rt/msr_module.c: Link Status
+ ausgelesen.
+
+2006-03-29 15:45 fp
+
+ * fragen.txt, include/ecrt.h, master/globals.h, todo.txt: TO-DO und
+ Fragen aktualisiert, kleine Schönheitskorrekturen.
+
+2006-03-29 15:23 fp
+
+ * include/ecrt.h, master/master.c, master/slave.c, master/slave.h,
+ mini/mini.c, rt/msr_module.c: Verbosity level in
+ ecrt_master_print().
+
+2006-03-29 15:10 fp
+
+ * master/canopen.c, master/slave.c, master/slave.h,
+ rt/msr_module.c: Laden der SDO-Subindizes.
+
+2006-03-29 10:30 fp
+
+ * include/ecrt.h, master/canopen.c, master/domain.c,
+ master/master.c, master/master.h, master/slave.c, mini/mini.c,
+ rt/msr_module.c, todo.txt: SDO-Schnittstelle verbessert.
+
+2006-03-28 17:40 fp
+
+ * include/ecrt.h, master/canopen.c, rt/msr_module.c: SDO-Zugriff
+ aufgeteilt in Expedited und Normal. Noch nicht fertig...
+
+2006-03-28 15:45 fp
+
+ * master/master.c, master/slave.c, master/slave.h, rt/msr_module.c:
+ Dynamische Mailbox-Kommunikation, auch mit unbekannten Slaves.
+
+2006-03-28 13:42 fp
+
+ * include/ecrt.h, master/canopen.c, master/master.c,
+ master/slave.c, master/slave.h, mini/mini.c, rt/msr_module.c:
+ Laden der SDO Dictionaries funktioniert.
+
+2006-03-28 12:38 fp
+
+ * master/canopen.c, rt/msr_module.c: Bugfix: Größerer Puffer für
+ CoE-Daten beim SDO upload.
+
+2006-03-27 15:52 fp
+
+ * master/canopen.c, master/master.c, master/slave.c,
+ master/slave.h, rt/msr_module.c: Unterstützte Prot. auslesen,
+ Mailbox in Slave ausgelagert, Bugfix in Anzeige des Watch-Frames
+ und erste SDO-List-Abfrage.
+
+2006-03-27 12:04 fp
+
+ * master/slave.c, todo.txt: CRC-Counter nach ESC-Datasheet
+ implementiert.
+
+2006-03-27 10:47 fp
+
+ * fragen.txt: Neue Fragen
+
+2006-03-27 09:53 fp
+
+ * master/master.c, master/master.h, mini/mini.c, todo.txt:
+ Watch-Kommando
+
+2006-03-24 08:56 fp
+
+ * master/master.c: Bugfix: Absturz bei unbekannter Klemme.
+
+2006-03-24 08:39 fp
+
+ * todo.txt: TO-DO-Liste aktualisiert.
+
+2006-03-24 08:35 fp
+
+ * master/module.c, master/slave.c: Code aufgeräumt und kleines
+ Speicherleck entdeckt.
+
+2006-03-22 11:42 fp
+
+ * master/slave.c, master/slave.h: Restliche EEPROM-Daten
+ ausgelesen.
+
+2006-03-21 20:25 fp
+
+ * include/ecrt.h: Schönheitskorrekturen.
+
+2006-03-21 18:28 fp
+
+ * master/slave.c: Nachmals verfälschte Umlaute korrigiert.
+
+2006-03-21 15:45 fp
+
+ * master/slave.c, master/slave.h: Weitere EEPROM-Daten.
+
+2006-03-21 15:10 fp
+
+ * master/slave.c: "kaputte" Umlaute korrigiert.
+
+2006-03-21 15:00 fp
+
+ * master/slave.c, master/slave.h: Bugfix: Absturz behoben,
+ EEPROM-Infos.
+
+2006-03-21 14:35 fp
+
+ * master/master.c: 64bit-Division vermieden.
+
+2006-03-21 14:23 fp
+
+ * master/master.c, master/slave.c: Verschiedene Timeouts
+ verlängert.
+
+2006-03-21 13:57 fp
+
+ * master/slave.c, master/slave.h: Kategorie-Daten aus EEPROM lesen.
+
+2006-03-21 10:25 fp
+
+ * mini/mini.c: Neue Mini-Konfiguration.
+
+2006-03-20 15:40 fp
+
+ * master/master.c, mini/mini.c: Slave-Alias korrigiert.
+
+2006-03-20 15:29 fp
+
+ * todo.txt: TO-DO-Liste aktualisiert.
+
+2006-03-20 15:28 fp
+
+ * include/ecrt.h, master/master.c, master/slave.c, master/slave.h,
+ mini/mini.c: Slave alias implementiert.
+
+2006-03-20 13:36 fp
+
+ * master/canopen.c, master/master.c, master/slave.c: Do-Schleifen
+ ersetzt.
+
+2006-03-20 12:54 fp
+
+ * master/domain.c, mini/mini.c, rt/msr_module.c: Feldregistrierung:
+ NULL als data_ptr möglich, field_count 0 = 1.
+
+2006-03-20 11:02 fp
+
+ * master/canopen.c, master/master.c, master/module.c, mini/mini.c:
+ Bugfix: SDO download. SDO abort codes ausgeben.
+
+2006-03-20 08:36 fp
+
+ * include/ecrt.h: Integer-Typen in ecrt.h.
+
+2006-03-20 08:24 fp
+
+ * master/master.c, mini/mini.c: Bugfix: EXPORT_SYMBOL für
+ ec_master_prepare_async_io() vergessen.
+
+2006-03-19 15:21 fp
+
+ * master/domain.c: Bugfix: Registrieren von mehreren Feldern
+ gleichzeitig geht jetzt.
+
+2006-03-19 14:58 fp
+
+ * master/types.c: Bugfix: EL3162 - "Product Name" war falsch.
+
+2006-03-17 16:25 fp
+
+ * include/ecrt.h, master/master.c, master/master.h, mini/mini.c,
+ rt/msr_module.c: ecrt_master_prepare_async_io() hinzugefügt.
+
+2006-03-17 15:18 fp
+
+ * include/ecrt.h, master/domain.c: Domänen-Status
+ (ecrt_domain_state) hinzugefügt.
+
+2006-03-17 14:21 fp
+
+ * devices/8139too.c, devices/ecdev.h, include/EtherCAT_dev.h,
+ include/EtherCAT_rt.h, include/EtherCAT_si.h, include/ecrt.h,
+ master/canopen.c, master/command.c, master/device.c,
+ master/device.h, master/domain.c, master/domain.h,
+ master/master.c, master/module.c, master/slave.c, master/types.c,
+ master/types.h, mini/mini.c, rt/msr_module.c: MERGE
+ branches/async -> trunk (alle Unterschiede übernommen)
+
+2006-03-15 20:19 fp
+
+ * Makefile, ethercat.conf.tmpl, mini/Makefile,
+ mini/ethercat.conf.tmpl, rt/Makefile, rt/ethercat.conf.tmpl:
+ Konfigurations-Template.
+
+2006-03-15 20:17 fp
+
+ * fragen.txt, todo.txt: TODO- und Fragenliste aktualisiert.
+
+2006-03-08 13:23 fp
+
+ * master/canopen.c, master/device.c, master/domain.c,
+ master/domain.h, master/master.c, master/module.c,
+ master/slave.c, master/slave.h, master/types.h: MERGE
+ branches/async -r243:244 -> trunk (intypes).
+
+2006-03-06 16:25 fp
+
+ * ec_reload.sh, ec_unload.sh: Script zum Entladen hinzugefügt.
+
+2006-03-06 16:18 fp
+
+ * ec_reload.sh: Reload-Script hinzugefügt.
+
+2006-03-06 15:12 fp
+
+ * devices/8139too.c, include/EtherCAT_dev.h, include/EtherCAT_rt.h,
+ master/Makefile, master/canopen.c, master/command.c,
+ master/command.h, master/device.c, master/device.h,
+ master/domain.c, master/domain.h, master/frame.c, master/frame.h,
+ master/globals.h, master/master.c, master/master.h,
+ master/module.c, master/slave.c, master/slave.h, mini/mini.c,
+ rt/Makefile, rt/msr_module.c: MERGE branches/async 222:233 ->
+ trunk (Kommando-Warteschlangen).
+
+2006-03-02 13:08 fp
+
+ * devices/8139too.c, master/device.c, master/domain.c:
+ Link-Down-Verhalten verbessert.
+
+2006-03-02 11:19 fp
+
+ * devices/8139too.c, include/EtherCAT_dev.h, master/device.c,
+ master/device.h, master/domain.c, master/frame.c: Link-State im
+ Device.
+
+2006-02-28 13:07 fp
+
+ * master/domain.h, master/globals.h, master/master.c,
+ master/master.h, todo.txt: Domains als Liste verwaltet.
+
+2006-02-28 11:36 fp
+
+ * master/domain.c, master/master.c, master/master.h: Zyklische
+ Ausgaben um "Verzögerte" Rahmen erweitert.
+
+2006-02-28 11:35 fp
+
+ * fragen.txt: Frage hinzugefügt.
+
+2006-02-28 11:14 fp
+
+ * master/master.h: Code-Dokumentation verbessert.
+
+2006-02-28 11:10 fp
+
+ * master/canopen.c, master/device.c, master/domain.c,
+ master/frame.c, master/master.c, master/module.c, master/slave.c,
+ master/types.h, rt/msr_module.c: Code-Dokumentation angepasst.
+
+2006-02-28 09:25 fp
+
+ * master/canopen.c, master/domain.c, master/frame.c,
+ master/frame.h, master/globals.h, master/master.c: Rahmen jetzt
+ zustandslos.
+
+2006-02-28 09:09 fp
+
+ * master/canopen.c, master/frame.c, master/master.c,
+ master/slave.c: Wiederholtes Senden, wenn keine Antwort.
+
+2006-02-26 12:26 fp
+
+ * master/module.c, rt/msr_module.c: Bessere Ausgaben beim Starten
+ und beenden.
+
+2006-02-25 14:25 fp
+
+ * TODO, todo.txt: TODO-Liste aktualisiert.
+
+2006-02-25 14:25 fp
+
+ * fragen.txt: Liste mit offenen Fragen an Beckhoff.
+
+2006-02-24 17:29 fp
+
+ * master/module.c: Master wird nach nicht erfolgreichem Request
+ resettet.
+
+2006-02-24 16:10 fp
+
+ * Makefile, master/canopen.c, master/device.c, master/domain.c,
+ master/frame.c, master/globals.h, master/master.c,
+ master/module.c, master/slave.c: printk durch Makros ersetzt.
+
+2006-02-24 14:09 fp
+
+ * mini/mini.c, rt/msr_module.c: Beispiel-Echtzeitcode angepasst.
+
+2006-02-24 13:54 fp
+
+ * master/master.c, master/types.c, master/types.h, rt/msr_module.c:
+ Buskoppler werden jetzt gesondert behandelt.
+
+2006-02-24 13:34 fp
+
+ * master/master.c, rt/msr_module.c: Nicht registrierte Slaves in
+ PREOP schalten.
+
+2006-02-24 13:14 fp
+
+ * include/EtherCAT_rt.h, master/canopen.c, master/canopen.h,
+ rt/msr_module.c: CANopen SDO read implementiert.
+
+2006-02-24 13:14 fp
+
+ * devices/8139too.c, master/module.c: Compile-Warnings mit ADEOS
+ behoben.
+
+2006-02-24 13:09 fp
+
+ * include/EtherCAT_dev.h, master/device.c, master/device.h,
+ master/frame.c, master/frame.h, master/module.c: Frame-Debugging
+ ins Device ausgelagert und verbessert.
+
+2006-02-24 10:19 fp
+
+ * include/EtherCAT_rt.h, include/EtherCAT_si.h, master/canopen.c,
+ master/frame.c, master/frame.h, master/master.c, master/slave.c:
+ EC_READ/WRITE-Makros verwenden Makros aud asm/byteorder.h und
+ werden konsequent verwendet.
+
+2006-02-23 14:51 fp
+
+ * master/domain.c, master/types.c: EL31XX-Typ korrigiert.
+
+2006-02-23 13:54 fp
+
+ * master/types.c, master/types.h: Feature-Flag der Klemmentypen
+ nicht mehr benötigt.
+
+2006-02-23 13:38 fp
+
+ * include/EtherCAT_si.h, master/master.c, master/master.h,
+ master/slave.c, master/slave.h: Neues Slave-Interface,
+ CRC-Prüfung und mehrfaches Scannen nun ungefährlich.
+
+2006-02-23 09:58 fp
+
+ * Makefile, TODO, devices/8139too.c, include/EtherCAT_dev.h,
+ include/EtherCAT_rt.h, master/Makefile, master/canopen.c,
+ master/canopen.h, master/command.c, master/command.h,
+ master/device.c, master/device.h, master/domain.c,
+ master/domain.h, master/frame.c, master/frame.h,
+ master/globals.h, master/master.c, master/master.h,
+ master/module.c, master/slave.c, master/slave.h, master/types.c,
+ master/types.h, mini/mini.c, rt, rt/Makefile, rt/msr_jitter.c,
+ rt/msr_jitter.h, rt/msr_module.c: Dynamische FMMU-Konfiguration,
+ zwei Kopieroperationen eingespart, Einrückungen angepasst.
+
+2006-02-22 17:36 fp
+
+ * master/types.c: Klemme Beckhoff EL2032 hinzugefügt.
+
+2006-02-20 08:36 fp
+
+ * Makefile, include/EtherCAT_si.h: Slave interface und Makefile
+ geändert.
+
+2006-02-20 08:30 fp
+
+ * devices/8139too.c, master/device.c: Bug: NULL pointer dereference
+ in master/device.c behoben.
+
+2006-02-14 14:53 fp
+
+ * master/Doxyfile: Keywords für Doxyfile.
+
+2006-02-14 14:50 fp
+
+ * TODO, include/EtherCAT_rt.h, master/Doxyfile, master/Makefile,
+ master/command.h, master/master.c, master/master.h, mini/mini.c,
+ rt/msr_module.c: Neue ASCII-Adressierung und Code-Dokumantation.
+
+2006-02-14 14:40 fp
+
+ * include/EtherCAT_si.h, master/types.c:
+ EL5101-Inkrementalgeberklemme hinzugefügt.
+
+2006-02-13 14:11 fp
+
+ * include/EtherCAT_si.h, master/canopen.c, master/master.c,
+ master/types.c: SSI-Klemmen-Interface und kleinere Änderungen.
+
+2006-02-03 16:38 fp
+
+ * mini/mini.c: Fehler in mini korrigiert.
+
+2006-02-03 16:23 fp
+
+ * include/EtherCAT_rt.h, master/Makefile, master/canopen.c,
+ master/canopen.h, master/master.c, master/master.h,
+ master/module.c, mini/mini.c: CANopen over EtherCAT.
+
+2006-02-03 10:46 fp
+
+ * master/master.c, master/types.c: Unbekannte Klemmen erlaubt,
+ EL5001 integriert.
+
+2006-01-26 15:52 fp
+
+ * mini/mini.c, rt/msr_module.c: Kleinere Korrekturen an rt und
+ mini.
+
+2006-01-26 13:41 fp
+
+ * include/EtherCAT_rt.h, master/master.c, master/types.h,
+ mini/mini.c, rt/msr_module.c: register_slave_list() und Bugfix in
+ deactivate_slaves()
+
+2006-01-26 11:06 fp
+
+ * mini/mini.c: Neue Schnittstellen in Mini übertragen.
+
+2006-01-26 10:48 fp
+
+ * Makefile, include/EtherCAT_si.h, libec, rt/Makefile,
+ rt/libec.o_shipped, rt/msr_module.c: EtherCAT-Slave-Interface als
+ Makros implementiert.
+
+2006-01-26 09:12 fp
+
+ * Doxyfile, Makefile, master, master/Doxyfile, master/Makefile,
+ master/domain.c, master/master.c, master/module.c:
+ Code-Dokumentation mit Doxygen aufgearbeitet.
+
+2006-01-20 17:50 fp
+
+ * Makefile, libec, libec/Makefile, libec/libec.c, libec/libec.h,
+ mini/mini.c, rt/Makefile, rt/libec.o_shipped, rt/msr_module.c:
+ LibEC
+
+2006-01-20 16:04 fp
+
+ * include/EtherCAT_rt.h, master/domain.c, master/domain.h,
+ master/master.c, master/master.h, master/module.c, rt/Makefile,
+ rt/msr_module.c: Bugfix im Master, ec_master_reset() und
+ laufendes Beispiel in rt.
+
+2006-01-20 13:32 fp
+
+ * devices/8139too.c, include/EtherCAT_dev.h, include/EtherCAT_rt.h,
+ master/master.c, master/master.h, master/module.c,
+ master/slave.c, master/slave.h, master/types.c, master/types.h,
+ mini/mini.c, rt/msr_module.c: Mit neuer Schnittstelle wieder
+ lauffähig.
+
+2006-01-17 18:28 fp
+
+ * Makefile, devices, devices/8139too.c, devices/Makefile,
+ devices/original_8139too.c, drivers, include,
+ include/EtherCAT_dev.h, include/EtherCAT_rt.h, master,
+ master/Makefile, master/command.c, master/command.h,
+ master/device.c, master/device.h, master/domain.c,
+ master/domain.h, master/globals.h, master/master.c,
+ master/master.h, master/module.c, master/slave.c, master/slave.h,
+ master/types.c, master/types.h, mini, mini/Makefile,
+ mini/ec_mini.c, mini/mini.c, rt, rt/msr_module.c:
+ Vereinheitlichte Schnittstellen, Include-Verzeichnis und Module
+ getrennt.
+
+2006-01-13 15:47 fp
+
+ * drivers/EtherCAT.h: Neu: EtherCAT.h
+
+2006-01-13 15:39 fp
+
+ * drivers/ec_domain.c, drivers/ec_domain.h, drivers/ec_master.c,
+ drivers/ec_master.h, drivers/ec_module.c, drivers/ec_module.h,
+ drivers/ec_slave.c, drivers/ec_slave.h: Neues Interface.
+
+2006-01-13 13:44 fp
+
+ * drivers/8139too.c, rt/msr_module.c, rt/msr_param.h: 8139too:
+ Immer alle Frames empfangen.
+
+2006-01-06 16:36 fp
+
+ * Makefile, drivers, drivers/Makefile, mini, mini/Makefile, rt,
+ rt/Makefile: Makefiles verbessert.
+
+2006-01-06 16:19 fp
+
+ * drivers/8139too.c: Dirty-TX-Meldung aus 8139too-ecat.c entfernt.
+
+2006-01-06 16:01 fp
+
+ * drivers/ec_domain.c, drivers/ec_domain.h, drivers/ec_master.c,
+ drivers/ec_master.h, rt/msr_module.c: Sinnvolle Meldung
+ verlorener Frames, Zustand antwortender Slaves.
+
+2006-01-06 13:20 fp
+
+ * drivers/ec_master.c, drivers/ec_master.h, mini/ec_mini.c,
+ rt/msr_module.c: Prozessdatentimeout, Buszeit und weniger
+ Klemmen.
+
+2006-01-06 10:17 fp
+
+ * Makefile, mini/Makefile, rt/Makefile: Makefiles nochmals
+ verbessert.
+
+2006-01-06 09:49 fp
+
+ * Makefile, drivers/Makefile, rt, rt/msr_module.c, rt/rt_lib:
+ Verbesserte Makefiles (MODPOST-Warnungen entfernt).
+
+2006-01-05 14:13 fp
+
+ * TODO: TODO-Liste ergänzt.
+
+2006-01-05 14:11 fp
+
+ * drivers/ec_master.c, rt/msr_jitter.c, rt/msr_jitter.h,
+ rt/msr_module.c, rt/msr_param.h: Schönheitskorrekturen.
+
+2006-01-05 13:39 fp
+
+ * drivers/Makefile, drivers/ec_device.c, drivers/ec_domain.c,
+ drivers/ec_domain.h, drivers/ec_globals.h, drivers/ec_master.c,
+ drivers/ec_master.h, drivers/ec_module.c, drivers/ec_slave.c,
+ drivers/ec_slave.h, drivers/ec_types.h, mini/ec_mini.c,
+ rt/msr_jitter.c, rt/msr_module.c, rt/msr_param.h: Domains, Warten
+ beim Senden, 10kHz.
+
+2005-12-23 08:23 fp
+
+ * TODO, drivers/Makefile, drivers/ec_command.h,
+ drivers/ec_device.h, drivers/ec_globals.h, drivers/ec_types.h:
+ Schoenheitskorrekturen.
+
+2005-12-23 08:20 fp
+
+ * rt/msr_module.c, rt/msr_param.h: 20kHz, Wilhelm
+
+2005-12-19 08:13 fp
+
+ * drivers/8139too.c, drivers/ec_command.c, drivers/ec_command.h,
+ drivers/ec_device.c, drivers/ec_device.h, drivers/ec_globals.h,
+ drivers/ec_master.c, drivers/ec_master.h, drivers/ec_module.c,
+ drivers/ec_module.h, drivers/ec_slave.c, drivers/ec_slave.h,
+ drivers/ec_types.c, drivers/ec_types.h,
+ drivers/original_8139too.c: likely/unlikely, Fehlermeldungen in
+ zyklischem Code begrenzt und Kommentare geÀndert.
+
+2005-12-16 16:21 fp
+
+ * rt/msr_module.c, user: user-Implementation aus aktueller
+ Entwicklung entfernt.
+
+2005-12-16 15:41 fp
+
+ * branches/kernel2.6, .: Pfad korrigiert 4.
+
+2005-12-16 15:25 fp
+
+ * branches/kernel2.6/mini/ec_mini.c,
+ branches/kernel2.6/rt/msr_module.c: EtherCAT_release() im
+ Fehlerfall beim Laden von Modulen mini und rt.
+
+2005-12-16 15:23 fp
+
+ * branches/kernel2.6/drivers/ec_master.c,
+ branches/kernel2.6/drivers/ec_types.c,
+ branches/kernel2.6/drivers/ec_types.h: Klemme Beckhoff EL4132
+ hinzugefügt.
+
+2005-12-16 14:16 fp
+
+ * branches/kernel2.6, branches/kernel2.6/drivers/8139too.c,
+ branches/kernel2.6/drivers/Makefile,
+ branches/kernel2.6/drivers/ec_module.c: Compile-Informationen
+ hinzugefügt.
+
+2005-12-16 12:04 fp
+
+ * branches/kernel2.6/drivers/8139too.c,
+ branches/kernel2.6/drivers/ec_device.c,
+ branches/kernel2.6/drivers/ec_device.h,
+ branches/kernel2.6/drivers/ec_master.c,
+ branches/kernel2.6/drivers/ec_master.h,
+ branches/kernel2.6/drivers/ec_module.c,
+ branches/kernel2.6/drivers/ec_module.h,
+ branches/kernel2.6/mini/ec_mini.c,
+ branches/kernel2.6/rt/msr_module.c: Master-Reservierung und
+ Use-Count für NIC-Treiber.
+
+2005-12-16 09:44 fp
+
+ * branches/kernel2.6/rt, branches/kernel2.6/rt/TAGS,
+ branches/kernel2.6/rt/aip_com.c, branches/kernel2.6/rt/aip_com.h,
+ branches/kernel2.6/rt/aip_comP.h,
+ branches/kernel2.6/rt/cif-rtai-io.h,
+ branches/kernel2.6/rt/msr_io.c, branches/kernel2.6/rt/msr_io.h,
+ branches/kernel2.6/rt/tmp: rt-Verzeichnis aufgeräumt.
+
+2005-12-16 09:23 hm
+
+ * branches/kernel2.6/rt/libm.o_shipped:
+
+2005-12-16 09:17 hm
+
+ * branches/kernel2.6/rt/msr_jitter.h: jitter.h aufgenommen
+
+2005-12-16 09:04 hm
+
+ * branches/kernel2.6/drivers/ec_master.c: tries left auf 20
+ geaendert
+
+2005-12-16 08:15 hm
+
+ * branches/kernel2.6/mini/ec_mini.c,
+ branches/kernel2.6/rt/Makefile,
+ branches/kernel2.6/rt/msr_jitter.c,
+ branches/kernel2.6/rt/msr_load,
+ branches/kernel2.6/rt/msr_module.c,
+ branches/kernel2.6/rt/msr_param.h,
+ branches/kernel2.6/rt/msrserv.pl, branches/kernel2.6/rt/rt_lib:
+ IPIPE,floatpoint,rtlib
+
+2005-12-02 15:35 fp
+
+ * branches/kernel2.6/drivers/8139too.c,
+ branches/kernel2.6/drivers/Makefile,
+ branches/kernel2.6/drivers/ec_device.c,
+ branches/kernel2.6/drivers/ec_master.c,
+ branches/kernel2.6/drivers/ec_master.h,
+ branches/kernel2.6/drivers/ec_module.c,
+ branches/kernel2.6/drivers/ec_module.h,
+ branches/kernel2.6/mini/ec_mini.c: EtherCAT-Master in eigenes
+ Modul ausgelagert.
+
+2005-12-02 12:59 fp
+
+ * branches/kernel2.6/Makefile,
+ branches/kernel2.6/drivers/ec_command.c,
+ branches/kernel2.6/drivers/ec_dbg.h,
+ branches/kernel2.6/drivers/ec_device.c,
+ branches/kernel2.6/drivers/ec_master.c,
+ branches/kernel2.6/drivers/ec_slave.c,
+ branches/kernel2.6/mini/ec_mini.c, branches/kernel2.6/rs232dbg:
+ Serial-Debugger entfernt.
+
+2005-12-02 11:37 fp
+
+ * branches/kernel2.6/drivers/8139too.c,
+ branches/kernel2.6/drivers/ec_command.h,
+ branches/kernel2.6/drivers/ec_device.c,
+ branches/kernel2.6/drivers/ec_device.h: IF's im
+ rtl8139too-Treiber vereinfacht und kein Polling mehr.
+
+2005-12-02 09:03 fp
+
+ * branches/kernel2.6/drivers/8139too.c,
+ branches/kernel2.6/drivers/ec_device.c,
+ branches/kernel2.6/drivers/ec_master.c,
+ branches/kernel2.6/drivers/ec_slave.c,
+ branches/kernel2.6/drivers/ec_types.c,
+ branches/kernel2.6/drivers/ec_types.h,
+ branches/kernel2.6/mini/Makefile,
+ branches/kernel2.6/mini/ec_mini.c: Wilhelms Änderungen
+ übernommen.
+
+2005-11-25 16:43 fp
+
+ * branches/kernel2.6/drivers/8139too.c,
+ branches/kernel2.6/drivers/ec_device.c,
+ branches/kernel2.6/drivers/ec_master.c,
+ branches/kernel2.6/drivers/ec_slave.c,
+ branches/kernel2.6/drivers/ec_types.c,
+ branches/kernel2.6/drivers/ec_types.h,
+ branches/kernel2.6/mini/ec_mini.c: EXPORT_SYMBOLS im
+ EtherCAT-Treiber.
+
+2005-11-25 14:52 fp
+
+ * branches/kernel2.6/Makefile, branches/kernel2.6/drivers,
+ branches/kernel2.6/drivers/8139too.c,
+ branches/kernel2.6/drivers/Makefile,
+ branches/kernel2.6/drivers/drv_8139too.c,
+ branches/kernel2.6/drivers/ec_device.c,
+ branches/kernel2.6/drivers/original_8139too.c,
+ branches/kernel2.6/mini, branches/kernel2.6/mini/Makefile,
+ branches/kernel2.6/mini/ec_mini.c: Portierung der Module in
+ drivers/ und mini/ nach Kernel 2.6.
+
+2005-11-25 10:16 fp
+
+ * branches/kernel2.6: Branch für 2.6er Kernel erstellt.
+
+2005-11-18 11:48 fp
+
+ * drivers/ec_master.c: Dokumentation angeglichen.
+
+2005-11-18 11:46 fp
+
+ * drivers/ec_command.c, drivers/ec_device.c, drivers/ec_device.h,
+ drivers/ec_globals.h, drivers/ec_master.c, drivers/ec_master.h:
+ Warten beim Empfangen der Prozessdaten, Bugfix und kleinere
+ Verbesserungen.
+
+2005-11-18 10:30 fp
+
+ * drivers/drv_8139too.c: Überflüssige Debug-Messages im
+ Treiber-Code entfernt.
+
+2005-11-18 09:51 fp
+
+ * drivers/ec_command.c, drivers/ec_command.h, drivers/ec_device.c,
+ drivers/ec_master.c, drivers/ec_master.h, drivers/ec_types.c:
+ Code zum Senden/Empfangen mehrerer Kommandos in einem Frame
+ vorerst ganz entfernt.
+
+2005-11-18 09:35 fp
+
+ * ., Makefile, drivers/Makefile, mini/Makefile, rs232dbg/Makefile,
+ rt/Makefile: Änderungen in den Makefiles.
+
+2005-11-11 13:52 fp
+
+ * drivers/ec_master.c: Addresse der Revisionsnummer im SII
+ korrigiert.
+
+2005-11-11 13:51 fp
+
+ * drivers/ec_command.c, drivers/ec_command.h, drivers/ec_master.c,
+ drivers/ec_master.h: Kommandoring und Co. entfernt.
+
+2005-11-11 13:46 fp
+
+ * Doxyfile, drivers/Makefile, drivers/ec_command.c,
+ drivers/ec_command.h, drivers/ec_device.h, drivers/ec_master.c,
+ drivers/ec_master.h, mini/ec_mini.c: Simple Send/Receive
+ Funktionen.
+
+2005-11-11 11:20 fp
+
+ * Makefile: "make kerneldirs" erstellt die Default-Datei
+ kerneldirs.mk zum Editieren.
+
+2005-11-11 11:07 fp
+
+ * drivers/ec_device.c, drivers/ec_master.c, drivers/ec_master.h:
+ EtherCAT_send_receive_command() zum Senden eines einzelnen
+ Kommandos hinzugefügt.
+
+2005-11-11 10:55 fp
+
+ * drivers/Makefile, mini/Makefile, rs232dbg/Makefile, rt/Makefile:
+ depend-Sektion in den Makefiles korrigiert.
+
+2005-11-11 10:15 fp
+
+ * ., drivers/Makefile, mini/Makefile, rs232dbg/Makefile,
+ rt/Makefile, user/Makefile: Verbesserte Makefiles
+ (Kernel-Verzeichnisse nicht mehr im SVN).
+
+2005-11-04 17:51 fp
+
+ * drivers/ec_master.c: Spaces am Zeilenende entfernt.
+
+2005-11-04 17:16 fp
+
+ * drivers/ec_device.c: Doppeltes Senden ist kein Fehler mehr.
+
+2005-11-04 17:05 hm
+
+ * drivers/Makefile, drivers/ec_device.c, drivers/ec_master.c,
+ rt/msr_io.c: ISR-Aufruf in read_process_data
+
+2005-11-04 16:47 fp
+
+ * drivers/Makefile, drivers/drv_8139too.c, drivers/ec_dbg.h,
+ drivers/ec_device.c, drivers/ec_device.h, drivers/ec_master.c,
+ drivers/ec_slave.c, drivers/ec_slave.h, mini/Makefile,
+ mini/ec_mini.c, rs232dbg/Makefile, rs232dbg/aip_com.c: Alle
+ Änderungen aus den Branches no_rtai und no_int nach Trunk
+ portiert.
+
+2005-11-04 09:38 fp
+
+ * drivers/drv_8139too.c: Weitere Kommentare in drv_8139too.c
+ portiert.
+
+2005-11-04 09:10 fp
+
+ * drivers/drv_8139too.c: Änderungen der Kommentare und Einrückungen
+ von drv_8139.c aus no_rtai 108:110 portiert.
+
+2005-10-28 15:12 fp
+
+ * drivers/ec_command.c, drivers/ec_command.h, drivers/ec_dbg.h,
+ drivers/ec_device.c, drivers/ec_master.c, drivers/ec_master.h,
+ drivers/ec_slave.c: Änderungen an no_rtai r110:110 in drivers
+ gemergt.
+
+2005-10-21 11:44 fp
+
+ * drivers/drv_8139too.c: Kommentare im 8139-Treiber geändert.
+
+2005-10-21 11:21 fp
+
+ * Doxyfile, Makefile, TODO, branches, drivers, mini, rs232dbg, rt,
+ tags, ., Doxyfile, Makefile, TODO, drivers, mini, rs232dbg, rt,
+ user, user: trunk, tags und branches
+
--- a/Doxyfile Fri Oct 13 10:07:10 2006 +0000
+++ b/Doxyfile Tue Nov 07 12:13:30 2006 +0000
@@ -23,7 +23,7 @@
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER = 1.1
+PROJECT_NUMBER = 1.1.1
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
--- a/INSTALL Fri Oct 13 10:07:10 2006 +0000
+++ b/INSTALL Tue Nov 07 12:13:30 2006 +0000
@@ -12,7 +12,7 @@
The building and installation procedure is described in section 2.1 in the
EtherCAT master documentation:
- documentation/ethercat-doc.pdf
+documentation/ethercat-doc.pdf
-------------------------------------------------------------------------------
@@ -20,11 +20,15 @@
The procedure mainly consists of calling
- ./configure
- make
- make install
+$ ./configure
+$ make modules
-...and copying the init script and sysconfig files from $prefix/etc to the
+(and as root)
+
+# make modules_install
+# make install
+
+...and copying the init script and sysconfig file from $prefix/etc to the
appropriate locations and customizing the sysconfig file.
-------------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Kbuild Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,36 @@
+#------------------------------------------------------------------------------
+#
+# $Id$
+#
+# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+#
+# This file is part of the IgH EtherCAT Master.
+#
+# The IgH EtherCAT Master 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.
+#
+# The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# The right to use EtherCAT Technology is granted and comes free of
+# charge under condition of compatibility of product made by
+# Licensee. People intending to distribute/sell products based on the
+# code, have to sign an agreement to guarantee that products using
+# software based on IgH EtherCAT master stay compatible with the actual
+# EtherCAT specification (which are released themselves as an open
+# standard) as the (only) precondition to have the right to use EtherCAT
+# Technology, IP and trade marks.
+#
+#------------------------------------------------------------------------------
+
+obj-m := master/ devices/
+
+#------------------------------------------------------------------------------
--- a/LICENSE Fri Oct 13 10:07:10 2006 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,340 +0,0 @@
- 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.
--- a/Makefile.am Fri Oct 13 10:07:10 2006 +0000
+++ b/Makefile.am Tue Nov 07 12:13:30 2006 +0000
@@ -1,9 +1,5 @@
#------------------------------------------------------------------------------
#
-# Makefile.am
-#
-# IgH EtherCAT master
-#
# $Id$
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -39,19 +35,50 @@
DIST_SUBDIRS = master devices script include examples
-EXTRA_DIST = documentation/ethercat_doc.pdf
+EXTRA_DIST = \
+ bootstrap \
+ documentation/ethercat_doc.pdf \
+ Doxyfile \
+ FEATURES \
+ globals.h \
+ LICENSE
+
+modules:
+ $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" modules
+
+modules_install:
+ $(MAKE) -C master modules_install
+ $(MAKE) -C devices modules_install
+
+clean-local:
+ $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" clean
+ @rm -f Modules.symvers
mydist:
@SVNREV=`svnversion $(srcdir)` && \
- $(MAKE) dist-bzip2 distdir=$(PACKAGE)-$(VERSION)-r$${SVNREV}
+ $(MAKE) dist-bzip2 \
+ distdir=$(PACKAGE)-$(VERSION)-$(BRANCH)-r$${SVNREV}
dist-hook:
if which svnversion >/dev/null 2>&1; then \
svnversion $(srcdir) 2>/dev/null >$(distdir)/svnrevision; \
fi
-install-data-local:
- $(DEPMOD) -b "$(DESTDIR)" $(LINUX_KERNEL_VERSION)
+mrproper: clean cleandoc
+ rm -rf \
+ aclocal.m4 \
+ autoconf \
+ autom4te.cache \
+ config.h \
+ config.h.in \
+ config.log \
+ config.status \
+ config.kbuild \
+ configure.in \
+ configure \
+ Makefile \
+ Makefile.in \
+ stamp-h1
doc:
doxygen Doxyfile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NEWS Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,32 @@
+-------------------------------------------------------------------------------
+
+$Id$
+
+-------------------------------------------------------------------------------
+
+* State change FSM: Clearing of sync managers before PREOP.
+* Added modules_install make target.
+* Device modules for kernel 2.6.17.
+* SDO configurations available in Sysfs.
+* FMMU configurations cleared when going to INIT.
+* Slave-specific logs only at debug level 1 (for large busses).
+* Slave flags (error, online) available in Sysfs.
+* Acknowledging of spontaneous state changes in master FSMs.
+* ecrt_master_deactivate() deprecated.
+* Persistent slave lists. Slave structures are only regenerated on topology
+ change in IDLE mode.
+* SDO reading via Sysfs.
+* Added ecrt_domain_register_pdo_range()
+* Fetching of SDO dictionary.
+* Better timing behaviour when starting up under high CPU load.
+* Applied Autotools.
+* Improved output of lsec.
+* SDO download state machine.
+
+-------------------------------------------------------------------------------
+
+Changes in version 1.1:
+
+* Improved realtime interface.
+
+-------------------------------------------------------------------------------
--- a/TODO Fri Oct 13 10:07:10 2006 +0000
+++ b/TODO Tue Nov 07 12:13:30 2006 +0000
@@ -8,33 +8,19 @@
Important things to do:
-* Add Kobjects in constructors.
-
* Read AL Status Code with AL Control Response (if possible?)
-
* Coupling of EoE handlers before configuring slaves.
(avoiding duplicate configuration)
-
-* SysFS interface
- - Add secondary slave address
- - Add SDO dictionary
-
* Implement all EtherCAT commands
-
* Determine number of frames the NIC can handle
-
-* Implement eepro100 driver
-
-* Progress bar in ecrt.
+* Implement e100 driver
-------------------------------------------------------------------------------
Not-so-important things to do:
* Calculate bus topology
-
* File access over EtherCAT (FoE)
-
* Allow VLAN tagging
-------------------------------------------------------------------------------
--- a/bootstrap Fri Oct 13 10:07:10 2006 +0000
+++ b/bootstrap Tue Nov 07 12:13:30 2006 +0000
@@ -4,5 +4,5 @@
mkdir -p autoconf
aclocal -I autoconf
autoheader
-automake --add-missing
+automake --add-missing --copy
autoconf
--- a/configure.ac Fri Oct 13 10:07:10 2006 +0000
+++ b/configure.ac Tue Nov 07 12:13:30 2006 +0000
@@ -3,77 +3,204 @@
#------------------------------------------------------------------------------
AC_PREREQ(2.59)
-AC_INIT([ethercat],[1.1],[fp@igh-essen.com])
+AC_INIT([ethercat],[1.1.1],[fp@igh-essen.com])
AC_CONFIG_AUX_DIR([autoconf])
-AM_INIT_AUTOMAKE([-Wall -Werror foreign dist-bzip2])
+AM_INIT_AUTOMAKE([-Wall -Werror dist-bzip2])
AC_PREFIX_DEFAULT([/opt/etherlab])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([config.h.in])
#------------------------------------------------------------------------------
+# Global
+#------------------------------------------------------------------------------
+
+branch=stable
+
+AC_DEFINE_UNQUOTED(BRANCH, ["$branch"], [Subversion branch])
+AC_SUBST(BRANCH, [$branch])
+
+#------------------------------------------------------------------------------
# Linux sources
#------------------------------------------------------------------------------
-AC_ARG_WITH([linux],
- AC_HELP_STRING(
- [--with-linux=<version>],
- [Linux kernel version @<:@running kernel@:>@]
- ),
- [version=[$withval]],
- [version=[`uname -r`]]
-)
-
-AC_MSG_CHECKING([for Linux kernel])
-modulesdir=/lib/modules/${version}
-if test \! -d ${modulesdir} || test \! -d ${modulesdir}/build; then
- echo
- AC_MSG_ERROR([Invalid modules directory ${modulesdir}])
-fi
-sourcedir=`cd ${modulesdir}/build && pwd -P`
-if test \! -r ${sourcedir}/Makefile; then
- echo
- AC_MSG_ERROR([No Linux kernel sources in $sourcedir])
-fi
-
-AC_SUBST(LINUX_KERNEL_VERSION,[$version])
+AC_ARG_WITH([linux-dir],
+ AC_HELP_STRING(
+ [--with-linux-dir=<DIR>],
+ [Linux kernel sources @<:@running kernel@:>@]
+ ),
+ [
+ sourcedir=[$withval]
+ ],
+ [
+ version=[`uname -r`]
+ modulesdir=/lib/modules/${version}
+ if test \! -d ${modulesdir} || test \! -d ${modulesdir}/build; then
+ echo
+ AC_MSG_ERROR([Failed to find Linux sources. Use --with-linux-dir!])
+ fi
+ sourcedir=`cd ${modulesdir}/build && pwd -P`
+ ]
+)
+
+AC_MSG_CHECKING([for Linux kernel sources])
+
+if test \! -r ${sourcedir}/.config; then
+ echo
+ AC_MSG_ERROR([No configured Linux kernel sources in $sourcedir])
+fi
+
+# Try to get kernel release string
+if test -r ${sourcedir}/.kernelrelease; then
+ kernelrelease=`cat $sourcedir/.kernelrelease`
+elif test -r ${sourcedir}/include/linux/version.h; then
+ hdr=$sourcedir/include/linux/version.h
+ kernelrelease=`grep UTS_RELEASE $hdr | cut -d " " -f 3- | tr -d \"`
+fi
+
+if test -z "$kernelrelease"; then
+ echo
+ AC_MSG_ERROR([Failed to extract Linux kernel version!])
+fi
+
+# Extract three numbers from kernel release string
+linuxversion=`echo $kernelrelease | grep -oE [[0-9]]+\.[[0-9]]+\.[[0-9]]+`
+
AC_SUBST(LINUX_SOURCE_DIR,[$sourcedir])
-AC_SUBST(LINUX_MODULES_DIR,[$modulesdir])
-
-AC_MSG_RESULT($LINUX_KERNEL_VERSION)
-
-#------------------------------------------------------------------------------
-# Depmod
-#------------------------------------------------------------------------------
-
-AC_PATH_PROG([DEPMOD], [depmod],, [$PATH:/sbin:/usr/sbin:/usr/local/sbin])
-
-if test -z "$DEPMOD"; then
- AC_MSG_WARN([depmod was not found!]);
+AC_SUBST(LINUX_KERNEL_RELEASE,[$kernelrelease])
+AC_SUBST(LINUX_KERNEL_VERSION,[$linuxversion])
+AC_SUBST(LINUX_MOD_PATH,[/lib/modules/$kernelrelease/ethercat])
+AC_MSG_RESULT([$LINUX_SOURCE_DIR (Kernel $LINUX_KERNEL_RELEASE)])
+
+#------------------------------------------------------------------------------
+# 8139too Kernel
+#------------------------------------------------------------------------------
+
+AC_ARG_WITH([8139too-kernel],
+ AC_HELP_STRING(
+ [--with-8139too-kernel=<X.Y.Z>],
+ [8139too kernel (only if differing)]
+ ),
+ [
+ kernel8139too=[$withval]
+ ],
+ [
+ kernel8139too=$linuxversion
+ ]
+)
+
+AC_MSG_CHECKING([for kernel for 8139too driver])
+
+kernels=`ls -1 devices/ | grep -oE "^8139too-.*-" | cut -d "-" -f 2 | uniq`
+found=0
+for k in $kernels; do
+ if test "$kernel8139too" = "$k"; then
+ found=1
+ fi
+done
+if test $found -ne 1; then
+ AC_MSG_ERROR([kernel $kernel8139too not available for 8139too driver!])
+fi
+
+AC_MSG_RESULT([$kernel8139too])
+
+#------------------------------------------------------------------------------
+# RTAI path (optional)
+#------------------------------------------------------------------------------
+
+AC_ARG_WITH([rtai-dir],
+ AC_HELP_STRING(
+ [--with-rtai-dir=<DIR>],
+ [RTAI path (only for RTAI examples)]
+ ),
+ [
+ rtaidir=[$withval]
+ ],
+ [
+ rtaidir=""
+ ]
+)
+
+AC_MSG_CHECKING([for RTAI path])
+
+if test -z "${rtaidir}"; then
+ AC_MSG_RESULT([not specified.])
+else
+ if test \! -r ${rtaidir}/include/rtai.h; then
+ AC_MSG_ERROR([no RTAI installation found in ${rtaidir}!])
+ fi
+ AC_MSG_RESULT([$rtaidir])
+ AC_SUBST(RTAI_DIR,[$rtaidir])
+fi
+
+#------------------------------------------------------------------------------
+# MSR path (optional)
+#------------------------------------------------------------------------------
+
+AC_ARG_WITH([msr-dir],
+ AC_HELP_STRING(
+ [--with-msr-dir=<DIR>],
+ [MSR path (only for MSR example)]
+ ),
+ [
+ msrdir=[$withval]
+ ],
+ [
+ msrdir=""
+ ]
+)
+
+AC_MSG_CHECKING([for MSR path])
+
+if test -z "${msrdir}"; then
+ AC_MSG_RESULT([not specified.])
+else
+ if test \! -r ${msrdir}/include/msr.h; then
+ AC_MSG_ERROR([no MSR installation found in ${msrdir}!])
+ fi
+ AC_MSG_RESULT([$msrdir])
+ AC_SUBST(MSR_DIR,[$msrdir])
fi
#------------------------------------------------------------------------------
# Debug interface
#------------------------------------------------------------------------------
-AC_ARG_ENABLE([debug-if],
- AS_HELP_STRING([--enable-dbg-if],
- [Create a debug interface for each master @<:@NO@:>@]),
- [case "${enableval}" in
- yes) dbg=1
- AC_DEFINE([EC_DBG_IF], [1], [Debug interfaces enabled])
- ;;
- no) dbg=0
- ;;
- *) AC_MSG_ERROR([Invalid value for --enable-dbg-if])
- ;;
- esac],
- [dbg=0]
-)
+AC_ARG_ENABLE([dbg-if],
+ AS_HELP_STRING([--enable-dbg-if],
+ [Create a debug interface for each master @<:@NO@:>@]),
+ [
+ case "${enableval}" in
+ yes) dbg=1
+ AC_DEFINE([EC_DBG_IF], [1], [Debug interfaces enabled])
+ ;;
+ no) dbg=0
+ ;;
+ *) AC_MSG_ERROR([Invalid value for --enable-dbg-if])
+ ;;
+ esac
+ ],
+ [dbg=0]
+)
+
AM_CONDITIONAL(EC_DBG_IF, test "x$dbg" = x1)
AC_SUBST([EC_DBG_IF],${dbg})
#------------------------------------------------------------------------------
+# Create config.kbuild
+
+echo configure: creating config.kbuild...
+
+cat > config.kbuild <<EOF
+# config.kbuild - created by configure
+EC_DBG_IF := ${dbg}
+EC_8139TOO_KERNEL := ${kernel8139too}
+EC_RTAI_DIR := "${rtaidir}"
+EC_MSR_DIR := "${msrdir}"
+EOF
+
+#------------------------------------------------------------------------------
+
AC_CONFIG_FILES([
Makefile
master/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devices/8139too-2.6.13-ethercat.c Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,2956 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT driver for RTL8139-compatible NICs.
+*/
+
+/*****************************************************************************/
+
+/*
+ Former documentation:
+
+ 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux.
+
+ Maintained by Jeff Garzik <jgarzik@pobox.com>
+ Copyright 2000-2002 Jeff Garzik
+
+ Much code comes from Donald Becker's rtl8139.c driver,
+ versions 1.13 and older. This driver was originally based
+ on rtl8139.c version 1.07. Header of rtl8139.c version 1.13:
+
+ -----<snip>-----
+
+ Written 1997-2001 by Donald Becker.
+ This software may be used and distributed according to the
+ terms of the GNU General Public License (GPL), incorporated
+ herein by reference. Drivers based on or derived from this
+ code fall under the GPL and must retain the authorship,
+ copyright and license notice. This file is not a complete
+ program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ This driver is for boards based on the RTL8129 and RTL8139
+ PCI ethernet chips.
+
+ The author may be reached as becker@scyld.com, or C/O Scyld
+ Computing Corporation 410 Severn Ave., Suite 210 Annapolis
+ MD 21403
+
+ Support and updates available at
+ http://www.scyld.com/network/rtl8139.html
+
+ Twister-tuning table provided by Kinston
+ <shangh@realtek.com.tw>.
+
+ -----<snip>-----
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ Contributors:
+
+ Donald Becker - he wrote the original driver, kudos to him!
+ (but please don't e-mail him for support, this isn't his driver)
+
+ Tigran Aivazian - bug fixes, skbuff free cleanup
+
+ Martin Mares - suggestions for PCI cleanup
+
+ David S. Miller - PCI DMA and softnet updates
+
+ Ernst Gill - fixes ported from BSD driver
+
+ Daniel Kobras - identified specific locations of
+ posted MMIO write bugginess
+
+ Gerard Sharp - bug fix, testing and feedback
+
+ David Ford - Rx ring wrap fix
+
+ Dan DeMaggio - swapped RTL8139 cards with me, and allowed me
+ to find and fix a crucial bug on older chipsets.
+
+ Donald Becker/Chris Butterworth/Marcus Westergren -
+ Noticed various Rx packet size-related buglets.
+
+ Santiago Garcia Mantinan - testing and feedback
+
+ Jens David - 2.2.x kernel backports
+
+ Martin Dennett - incredibly helpful insight on undocumented
+ features of the 8139 chips
+
+ Jean-Jacques Michel - bug fix
+
+ Tobias Ringström - Rx interrupt status checking suggestion
+
+ Andrew Morton - Clear blocked signals, avoid
+ buffer overrun setting current->comm.
+
+ Kalle Olavi Niemitalo - Wake-on-LAN ioctls
+
+ Robert Kuebel - Save kernel thread from dying on any signal.
+
+ Submitting bug reports:
+
+ "rtl8139-diag -mmmaaavvveefN" output
+ enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log
+
+*/
+
+#define DRV_NAME "ec_8139too"
+#define DRV_VERSION "0.9.27"
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/completion.h>
+#include <linux/crc32.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+#include "../globals.h"
+#include "ecdev.h"
+
+#define RTL8139_DRIVER_NAME DRV_NAME \
+ " EtherCAT-capable Fast Ethernet driver " \
+ DRV_VERSION ", master " EC_MASTER_VERSION
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+#define PFX DRV_NAME ": "
+
+/* Default Message level */
+#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK)
+
+
+/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */
+#ifdef CONFIG_8139TOO_PIO
+#define USE_IO_OPS 1
+#endif
+
+/* define to 1, 2 or 3 to enable copious debugging info */
+#define RTL8139_DEBUG 0
+
+/* define to 1 to disable lightweight runtime debugging checks */
+#undef RTL8139_NDEBUG
+
+
+#if RTL8139_DEBUG
+/* note: prints function name for you */
+# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+# define DPRINTK(fmt, args...)
+#endif
+
+#ifdef RTL8139_NDEBUG
+# define assert(expr) do {} while (0)
+#else
+# define assert(expr) \
+ if(unlikely(!(expr))) { \
+ printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr,__FILE__,__FUNCTION__,__LINE__); \
+ }
+#endif
+
+
+/* A few user-configurable values. */
+/* media options */
+#define MAX_UNITS 8
+static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ The RTL chips use a 64 element hash table based on the Ethernet CRC. */
+static int multicast_filter_limit = 32;
+
+/* bitmapped message enable number */
+static int debug = -1;
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+static int ec_device_index = -1;
+static int ec_device_master_index = 0;
+static ec_device_t *rtl_ec_dev;
+struct net_device *rtl_ec_net_dev = NULL;
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+/*
+ * Receive ring size
+ * Warning: 64K ring has hardware issues and may lock up.
+ */
+#if defined(CONFIG_SH_DREAMCAST)
+#define RX_BUF_IDX 1 /* 16K ring */
+#else
+#define RX_BUF_IDX 2 /* 32K ring */
+#endif
+#define RX_BUF_LEN (8192 << RX_BUF_IDX)
+#define RX_BUF_PAD 16
+#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
+
+#if RX_BUF_LEN == 65536
+#define RX_BUF_TOT_LEN RX_BUF_LEN
+#else
+#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
+#endif
+
+/* Number of Tx descriptor registers. */
+#define NUM_TX_DESC 4
+
+/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/
+#define MAX_ETH_FRAME_SIZE 1536
+
+/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
+#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE
+#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC)
+
+/* PCI Tuning Parameters
+ Threshold is bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
+
+/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */
+#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */
+#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
+#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (6*HZ)
+
+
+enum {
+ HAS_MII_XCVR = 0x010000,
+ HAS_CHIP_XCVR = 0x020000,
+ HAS_LNK_CHNG = 0x040000,
+};
+
+#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */
+#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */
+#define RTL_MIN_IO_SIZE 0x80
+#define RTL8139B_IO_SIZE 256
+
+#define RTL8129_CAPS HAS_MII_XCVR
+#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG
+
+typedef enum {
+ RTL8139 = 0,
+ RTL8129,
+} board_t;
+
+
+/* indexed by board_t, above */
+static struct {
+ const char *name;
+ u32 hw_flags;
+} board_info[] __devinitdata = {
+ { "RealTek RTL8139", RTL8139_CAPS },
+ { "RealTek RTL8129", RTL8129_CAPS },
+};
+
+
+static struct pci_device_id rtl8139_pci_tbl[] = {
+ {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+
+#ifdef CONFIG_SH_SECUREEDGE5410
+ /* Bogus 8139 silicon reports 8129 without external PROM :-( */
+ {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+#endif
+#ifdef CONFIG_8139TOO_8129
+ {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
+#endif
+
+ /* some crazy cards report invalid vendor ids like
+ * 0x0001 here. The other ids are valid and constant,
+ * so we simply don't match on the main vendor id.
+ */
+ {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
+ {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },
+ {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },
+
+ {0,}
+};
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+/* prevent driver from being loaded automatically */
+//MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+static struct {
+ const char str[ETH_GSTRING_LEN];
+} ethtool_stats_keys[] = {
+ { "early_rx" },
+ { "tx_buf_mapped" },
+ { "tx_timeouts" },
+ { "rx_lost_in_ring" },
+};
+
+/* The rest of these values should never change. */
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+ MAC0 = 0, /* Ethernet hardware address. */
+ MAR0 = 8, /* Multicast filter. */
+ TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */
+ TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
+ RxBuf = 0x30,
+ ChipCmd = 0x37,
+ RxBufPtr = 0x38,
+ RxBufAddr = 0x3A,
+ IntrMask = 0x3C,
+ IntrStatus = 0x3E,
+ TxConfig = 0x40,
+ RxConfig = 0x44,
+ Timer = 0x48, /* A general-purpose counter. */
+ RxMissed = 0x4C, /* 24 bits valid, write clears. */
+ Cfg9346 = 0x50,
+ Config0 = 0x51,
+ Config1 = 0x52,
+ FlashReg = 0x54,
+ MediaStatus = 0x58,
+ Config3 = 0x59,
+ Config4 = 0x5A, /* absent on RTL-8139A */
+ HltClk = 0x5B,
+ MultiIntr = 0x5C,
+ TxSummary = 0x60,
+ BasicModeCtrl = 0x62,
+ BasicModeStatus = 0x64,
+ NWayAdvert = 0x66,
+ NWayLPAR = 0x68,
+ NWayExpansion = 0x6A,
+ /* Undocumented registers, but required for proper operation. */
+ FIFOTMS = 0x70, /* FIFO Control and test. */
+ CSCR = 0x74, /* Chip Status and Configuration Register. */
+ PARA78 = 0x78,
+ PARA7c = 0x7c, /* Magic transceiver parameter register. */
+ Config5 = 0xD8, /* absent on RTL-8139A */
+};
+
+enum ClearBitMasks {
+ MultiIntrClear = 0xF000,
+ ChipCmdClear = 0xE2,
+ Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
+};
+
+enum ChipCmdBits {
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08,
+ CmdTxEnb = 0x04,
+ RxBufEmpty = 0x01,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+ PCIErr = 0x8000,
+ PCSTimeout = 0x4000,
+ RxFIFOOver = 0x40,
+ RxUnderrun = 0x20,
+ RxOverflow = 0x10,
+ TxErr = 0x08,
+ TxOK = 0x04,
+ RxErr = 0x02,
+ RxOK = 0x01,
+
+ RxAckBits = RxFIFOOver | RxOverflow | RxOK,
+};
+
+enum TxStatusBits {
+ TxHostOwns = 0x2000,
+ TxUnderrun = 0x4000,
+ TxStatOK = 0x8000,
+ TxOutOfWindow = 0x20000000,
+ TxAborted = 0x40000000,
+ TxCarrierLost = 0x80000000,
+};
+enum RxStatusBits {
+ RxMulticast = 0x8000,
+ RxPhysical = 0x4000,
+ RxBroadcast = 0x2000,
+ RxBadSymbol = 0x0020,
+ RxRunt = 0x0010,
+ RxTooLong = 0x0008,
+ RxCRCErr = 0x0004,
+ RxBadAlign = 0x0002,
+ RxStatusOK = 0x0001,
+};
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+};
+
+/* Bits in TxConfig. */
+enum tx_config_bits {
+
+ /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
+ TxIFGShift = 24,
+ TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
+ TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
+ TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
+ TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
+
+ TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
+ TxClearAbt = (1 << 0), /* Clear abort (WO) */
+ TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */
+ TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */
+
+ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+/* Bits in Config1 */
+enum Config1Bits {
+ Cfg1_PM_Enable = 0x01,
+ Cfg1_VPD_Enable = 0x02,
+ Cfg1_PIO = 0x04,
+ Cfg1_MMIO = 0x08,
+ LWAKE = 0x10, /* not on 8139, 8139A */
+ Cfg1_Driver_Load = 0x20,
+ Cfg1_LED0 = 0x40,
+ Cfg1_LED1 = 0x80,
+ SLEEP = (1 << 1), /* only on 8139, 8139A */
+ PWRDN = (1 << 0), /* only on 8139, 8139A */
+};
+
+/* Bits in Config3 */
+enum Config3Bits {
+ Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */
+ Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
+ Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
+ Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */
+ Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */
+ Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
+ Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */
+ Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
+};
+
+/* Bits in Config4 */
+enum Config4Bits {
+ LWPTN = (1 << 2), /* not on 8139, 8139A */
+};
+
+/* Bits in Config5 */
+enum Config5Bits {
+ Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */
+ Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */
+ Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */
+ Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
+ Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */
+ Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */
+ Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */
+};
+
+enum RxConfigBits {
+ /* rx fifo threshold */
+ RxCfgFIFOShift = 13,
+ RxCfgFIFONone = (7 << RxCfgFIFOShift),
+
+ /* Max DMA burst */
+ RxCfgDMAShift = 8,
+ RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
+
+ /* rx ring buffer length */
+ RxCfgRcv8K = 0,
+ RxCfgRcv16K = (1 << 11),
+ RxCfgRcv32K = (1 << 12),
+ RxCfgRcv64K = (1 << 11) | (1 << 12),
+
+ /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
+ RxNoWrap = (1 << 7),
+};
+
+/* Twister tuning parameters from RealTek.
+ Completely undocumented, but required to tune bad links on some boards. */
+enum CSCRBits {
+ CSCR_LinkOKBit = 0x0400,
+ CSCR_LinkChangeBit = 0x0800,
+ CSCR_LinkStatusBits = 0x0f000,
+ CSCR_LinkDownOffCmd = 0x003c0,
+ CSCR_LinkDownCmd = 0x0f3c0,
+};
+
+enum Cfg9346Bits {
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xC0,
+};
+
+typedef enum {
+ CH_8139 = 0,
+ CH_8139_K,
+ CH_8139A,
+ CH_8139A_G,
+ CH_8139B,
+ CH_8130,
+ CH_8139C,
+ CH_8100,
+ CH_8100B_8139D,
+ CH_8101,
+} chip_t;
+
+enum chip_flags {
+ HasHltClk = (1 << 0),
+ HasLWake = (1 << 1),
+};
+
+#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
+ (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
+#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1)
+
+/* directly indexed by chip_t, above */
+const static struct {
+ const char *name;
+ u32 version; /* from RTL8139C/RTL8139D docs */
+ u32 flags;
+} rtl_chip_info[] = {
+ { "RTL-8139",
+ HW_REVID(1, 0, 0, 0, 0, 0, 0),
+ HasHltClk,
+ },
+
+ { "RTL-8139 rev K",
+ HW_REVID(1, 1, 0, 0, 0, 0, 0),
+ HasHltClk,
+ },
+
+ { "RTL-8139A",
+ HW_REVID(1, 1, 1, 0, 0, 0, 0),
+ HasHltClk, /* XXX undocumented? */
+ },
+
+ { "RTL-8139A rev G",
+ HW_REVID(1, 1, 1, 0, 0, 1, 0),
+ HasHltClk, /* XXX undocumented? */
+ },
+
+ { "RTL-8139B",
+ HW_REVID(1, 1, 1, 1, 0, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8130",
+ HW_REVID(1, 1, 1, 1, 1, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8139C",
+ HW_REVID(1, 1, 1, 0, 1, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8100",
+ HW_REVID(1, 1, 1, 1, 0, 1, 0),
+ HasLWake,
+ },
+
+ { "RTL-8100B/8139D",
+ HW_REVID(1, 1, 1, 0, 1, 0, 1),
+ HasLWake,
+ },
+
+ { "RTL-8101",
+ HW_REVID(1, 1, 1, 0, 1, 1, 1),
+ HasLWake,
+ },
+};
+
+struct rtl_extra_stats {
+ unsigned long early_rx;
+ unsigned long tx_buf_mapped;
+ unsigned long tx_timeouts;
+ unsigned long rx_lost_in_ring;
+};
+
+struct rtl8139_private {
+ void __iomem *mmio_addr;
+ int drv_flags;
+ struct pci_dev *pci_dev;
+ u32 msg_enable;
+ struct net_device_stats stats;
+ unsigned char *rx_ring;
+ unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */
+ unsigned int tx_flag;
+ unsigned long cur_tx;
+ unsigned long dirty_tx;
+ unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */
+ unsigned char *tx_bufs; /* Tx bounce buffer region. */
+ dma_addr_t rx_ring_dma;
+ dma_addr_t tx_bufs_dma;
+ signed char phys[4]; /* MII device addresses. */
+ char twistie, twist_row, twist_col; /* Twister tune state. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ spinlock_t lock;
+ spinlock_t rx_lock;
+ chip_t chipset;
+ pid_t thr_pid;
+ wait_queue_head_t thr_wait;
+ struct completion thr_exited;
+ u32 rx_config;
+ struct rtl_extra_stats xstats;
+ int time_to_die;
+ struct mii_if_info mii;
+ unsigned int regs_len;
+ unsigned long fifo_copy_timeout;
+};
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+MODULE_AUTHOR("Florian Pose <fp@igh-essen.com>");
+MODULE_DESCRIPTION("RealTek RTL-8139 EtherCAT driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(EC_MASTER_VERSION);
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+module_param(multicast_filter_limit, int, 0);
+module_param_array(media, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+module_param(debug, int, 0);
+MODULE_PARM_DESC (debug, "8139too bitmapped message enable number");
+MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses");
+MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps");
+MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)");
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+module_param(ec_device_index, int, -1);
+module_param(ec_device_master_index, int, 0);
+MODULE_PARM_DESC(ec_device_index,
+ "Index of the device reserved for EtherCAT.");
+MODULE_PARM_DESC(ec_device_master_index,
+ "Index of the EtherCAT master to register the device.");
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+static int read_eeprom (void __iomem *ioaddr, int location, int addr_len);
+static int rtl8139_open (struct net_device *dev);
+static int mdio_read (struct net_device *dev, int phy_id, int location);
+static void mdio_write (struct net_device *dev, int phy_id, int location,
+ int val);
+static void rtl8139_start_thread(struct net_device *dev);
+static void rtl8139_tx_timeout (struct net_device *dev);
+static void rtl8139_init_ring (struct net_device *dev);
+static int rtl8139_start_xmit (struct sk_buff *skb,
+ struct net_device *dev);
+static int rtl8139_poll(struct net_device *dev, int *budget);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void rtl8139_poll_controller(struct net_device *dev);
+#endif
+irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs);
+static int rtl8139_close (struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
+static void rtl8139_set_rx_mode (struct net_device *dev);
+static void __set_rx_mode (struct net_device *dev);
+static void rtl8139_hw_start (struct net_device *dev);
+static struct ethtool_ops rtl8139_ethtool_ops;
+
+/* write MMIO register, with flush */
+/* Flush avoids rtl8139 bug w/ posted MMIO writes */
+#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0)
+#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0)
+#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0)
+
+
+#define MMIO_FLUSH_AUDIT_COMPLETE 1
+#if MMIO_FLUSH_AUDIT_COMPLETE
+
+/* write MMIO register */
+#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg))
+
+#else
+
+/* write MMIO register, then flush */
+#define RTL_W8 RTL_W8_F
+#define RTL_W16 RTL_W16_F
+#define RTL_W32 RTL_W32_F
+
+#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
+
+/* read MMIO register */
+#define RTL_R8(reg) ioread8 (ioaddr + (reg))
+#define RTL_R16(reg) ioread16 (ioaddr + (reg))
+#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg)))
+
+
+static const u16 rtl8139_intr_mask =
+ PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
+ TxErr | TxOK | RxErr | RxOK;
+
+static const u16 rtl8139_norx_intr_mask =
+ PCIErr | PCSTimeout | RxUnderrun |
+ TxErr | TxOK | RxErr ;
+
+#if RX_BUF_IDX == 0
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv8K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 1
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv16K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 2
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv32K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 3
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv64K |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#else
+#error "Invalid configuration for 8139_RXBUF_IDX"
+#endif
+
+static const unsigned int rtl8139_tx_config =
+ TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift);
+
+static void __rtl8139_cleanup_dev (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ struct pci_dev *pdev;
+
+ assert (dev != NULL);
+ assert (tp->pci_dev != NULL);
+ pdev = tp->pci_dev;
+
+#ifdef USE_IO_OPS
+ if (tp->mmio_addr)
+ ioport_unmap (tp->mmio_addr);
+#else
+ if (tp->mmio_addr)
+ pci_iounmap (pdev, tp->mmio_addr);
+#endif /* USE_IO_OPS */
+
+ /* it's ok to call this even if we have no regions to free */
+ pci_release_regions (pdev);
+
+ free_netdev(dev);
+ pci_set_drvdata (pdev, NULL);
+}
+
+
+static void rtl8139_chip_reset (void __iomem *ioaddr)
+{
+ int i;
+
+ /* Soft reset the chip. */
+ RTL_W8 (ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--) {
+ barrier();
+ if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
+ break;
+ udelay (10);
+ }
+}
+
+
+static int __devinit rtl8139_init_board (struct pci_dev *pdev,
+ struct net_device **dev_out)
+{
+ void __iomem *ioaddr;
+ struct net_device *dev;
+ struct rtl8139_private *tp;
+ u8 tmp8;
+ int rc, disable_dev_on_err = 0;
+ unsigned int i;
+ unsigned long pio_start, pio_end, pio_flags, pio_len;
+ unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
+ u32 version;
+
+ assert (pdev != NULL);
+
+ *dev_out = NULL;
+
+ /* dev and priv zeroed in alloc_etherdev */
+ dev = alloc_etherdev (sizeof (*tp));
+ if (dev == NULL) {
+ printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev));
+ return -ENOMEM;
+ }
+ SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ tp = netdev_priv(dev);
+ tp->pci_dev = pdev;
+
+ /* enable device (incl. PCI PM wakeup and hotplug setup) */
+ rc = pci_enable_device (pdev);
+ if (rc)
+ goto err_out;
+
+ pio_start = pci_resource_start (pdev, 0);
+ pio_end = pci_resource_end (pdev, 0);
+ pio_flags = pci_resource_flags (pdev, 0);
+ pio_len = pci_resource_len (pdev, 0);
+
+ mmio_start = pci_resource_start (pdev, 1);
+ mmio_end = pci_resource_end (pdev, 1);
+ mmio_flags = pci_resource_flags (pdev, 1);
+ mmio_len = pci_resource_len (pdev, 1);
+
+ /* set this immediately, we need to know before
+ * we talk to the chip directly */
+ DPRINTK("PIO region size == 0x%02X\n", pio_len);
+ DPRINTK("MMIO region size == 0x%02lX\n", mmio_len);
+
+#ifdef USE_IO_OPS
+ /* make sure PCI base addr 0 is PIO */
+ if (!(pio_flags & IORESOURCE_IO)) {
+ printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+ /* check for weird/broken PCI region reporting */
+ if (pio_len < RTL_MIN_IO_SIZE) {
+ printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+#else
+ /* make sure PCI base addr 1 is MMIO */
+ if (!(mmio_flags & IORESOURCE_MEM)) {
+ printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+ if (mmio_len < RTL_MIN_IO_SIZE) {
+ printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+#endif
+
+ rc = pci_request_regions (pdev, "8139too");
+ if (rc)
+ goto err_out;
+ disable_dev_on_err = 1;
+
+ /* enable PCI bus-mastering */
+ pci_set_master (pdev);
+
+#ifdef USE_IO_OPS
+ ioaddr = ioport_map(pio_start, pio_len);
+ if (!ioaddr) {
+ printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+ dev->base_addr = pio_start;
+ tp->mmio_addr = ioaddr;
+ tp->regs_len = pio_len;
+#else
+ /* ioremap MMIO region */
+ ioaddr = pci_iomap(pdev, 1, 0);
+ if (ioaddr == NULL) {
+ printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+ dev->base_addr = (long) ioaddr;
+ tp->mmio_addr = ioaddr;
+ tp->regs_len = mmio_len;
+#endif /* USE_IO_OPS */
+
+ /* Bring old chips out of low-power mode. */
+ RTL_W8 (HltClk, 'R');
+
+ /* check for missing/broken hardware */
+ if (RTL_R32 (TxConfig) == 0xFFFFFFFF) {
+ printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n",
+ pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+
+ /* identify chip attached to board */
+ version = RTL_R32 (TxConfig) & HW_REVID_MASK;
+ for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++)
+ if (version == rtl_chip_info[i].version) {
+ tp->chipset = i;
+ goto match;
+ }
+
+ /* if unknown chip, assume array element #0, original RTL-8139 in this case */
+ printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n",
+ pci_name(pdev));
+ printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig));
+ tp->chipset = 0;
+
+match:
+ DPRINTK ("chipset id (%d) == index %d, '%s'\n",
+ version, i, rtl_chip_info[i].name);
+
+ if (tp->chipset >= CH_8139B) {
+ u8 new_tmp8 = tmp8 = RTL_R8 (Config1);
+ DPRINTK("PCI PM wakeup\n");
+ if ((rtl_chip_info[tp->chipset].flags & HasLWake) &&
+ (tmp8 & LWAKE))
+ new_tmp8 &= ~LWAKE;
+ new_tmp8 |= Cfg1_PM_Enable;
+ if (new_tmp8 != tmp8) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config1, tmp8);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ }
+ if (rtl_chip_info[tp->chipset].flags & HasLWake) {
+ tmp8 = RTL_R8 (Config4);
+ if (tmp8 & LWPTN) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config4, tmp8 & ~LWPTN);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ }
+ }
+ } else {
+ DPRINTK("Old chip wakeup\n");
+ tmp8 = RTL_R8 (Config1);
+ tmp8 &= ~(SLEEP | PWRDN);
+ RTL_W8 (Config1, tmp8);
+ }
+
+ rtl8139_chip_reset (ioaddr);
+
+ *dev_out = dev;
+ return 0;
+
+err_out:
+ __rtl8139_cleanup_dev (dev);
+ if (disable_dev_on_err)
+ pci_disable_device (pdev);
+ return rc;
+}
+
+
+static int __devinit rtl8139_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev = NULL;
+ struct rtl8139_private *tp;
+ int i, addr_len, option;
+ void __iomem *ioaddr;
+ static int board_idx = -1;
+ u8 pci_rev;
+
+ assert (pdev != NULL);
+ assert (ent != NULL);
+
+ board_idx++;
+
+ /* when we're built into the kernel, the driver version message
+ * is only printed if at least one 8139 board has been found
+ */
+#ifndef MODULE
+ {
+ static int printed_version;
+ if (!printed_version++)
+ printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
+ }
+#endif
+
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev);
+
+ if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
+ pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) {
+ printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n",
+ pci_name(pdev), pdev->vendor, pdev->device, pci_rev);
+ printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n");
+ }
+
+ i = rtl8139_init_board (pdev, &dev);
+ if (i < 0)
+ return i;
+
+ assert (dev != NULL);
+ tp = netdev_priv(dev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (board_idx == ec_device_index) {
+ rtl_ec_net_dev = dev;
+ strcpy(dev->name, "ec0");
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ ioaddr = tp->mmio_addr;
+ assert (ioaddr != NULL);
+
+ addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6;
+ for (i = 0; i < 3; i++)
+ ((u16 *) (dev->dev_addr))[i] =
+ le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len));
+
+ /* The Rtl8139-specific entries in the device structure. */
+ dev->open = rtl8139_open;
+ dev->hard_start_xmit = rtl8139_start_xmit;
+ dev->poll = rtl8139_poll;
+ dev->weight = 64;
+ dev->stop = rtl8139_close;
+ dev->get_stats = rtl8139_get_stats;
+ dev->set_multicast_list = rtl8139_set_rx_mode;
+ dev->do_ioctl = netdev_ioctl;
+ dev->ethtool_ops = &rtl8139_ethtool_ops;
+ dev->tx_timeout = rtl8139_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = rtl8139_poll_controller;
+#endif
+
+ /* note: the hardware is not capable of sg/csum/highdma, however
+ * through the use of skb_copy_and_csum_dev we enable these
+ * features
+ */
+ dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;
+
+ dev->irq = pdev->irq;
+
+ /* tp zeroed and aligned in alloc_etherdev */
+ tp = netdev_priv(dev);
+
+ /* note: tp->chipset set in rtl8139_init_board */
+ tp->drv_flags = board_info[ent->driver_data].hw_flags;
+ tp->mmio_addr = ioaddr;
+ tp->msg_enable =
+ (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1));
+ spin_lock_init (&tp->lock);
+ spin_lock_init (&tp->rx_lock);
+ init_waitqueue_head (&tp->thr_wait);
+ init_completion (&tp->thr_exited);
+ tp->mii.dev = dev;
+ tp->mii.mdio_read = mdio_read;
+ tp->mii.mdio_write = mdio_write;
+ tp->mii.phy_id_mask = 0x3f;
+ tp->mii.reg_num_mask = 0x1f;
+
+ /* dev is fully set up and ready to use now */
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ DPRINTK("about to register device named %s (%p)...\n", dev->name, dev);
+ i = register_netdev (dev);
+ if (i) goto err_out;
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ pci_set_drvdata (pdev, dev);
+
+ printk (KERN_INFO "%s: %s at 0x%lx, "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+ "IRQ %d\n",
+ dev->name,
+ board_info[ent->driver_data].name,
+ dev->base_addr,
+ dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3],
+ dev->dev_addr[4], dev->dev_addr[5],
+ dev->irq);
+
+ printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n",
+ dev->name, rtl_chip_info[tp->chipset].name);
+
+ /* Find the connected MII xcvrs.
+ Doing this in open() would allow detecting external xcvrs later, but
+ takes too much time. */
+#ifdef CONFIG_8139TOO_8129
+ if (tp->drv_flags & HAS_MII_XCVR) {
+ int phy, phy_idx = 0;
+ for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) {
+ int mii_status = mdio_read(dev, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ u16 advertising = mdio_read(dev, phy, 4);
+ tp->phys[phy_idx++] = phy;
+ printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x "
+ "advertising %4.4x.\n",
+ dev->name, phy, mii_status, advertising);
+ }
+ }
+ if (phy_idx == 0) {
+ printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM "
+ "transceiver.\n",
+ dev->name);
+ tp->phys[0] = 32;
+ }
+ } else
+#endif
+ tp->phys[0] = 32;
+ tp->mii.phy_id = tp->phys[0];
+
+ /* The lower four bits are the media type. */
+ option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx];
+ if (option > 0) {
+ tp->mii.full_duplex = (option & 0x210) ? 1 : 0;
+ tp->default_port = option & 0xFF;
+ if (tp->default_port)
+ tp->mii.force_media = 1;
+ }
+ if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0)
+ tp->mii.full_duplex = full_duplex[board_idx];
+ if (tp->mii.full_duplex) {
+ printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
+ /* Changing the MII-advertised media because might prevent
+ re-connection. */
+ tp->mii.force_media = 1;
+ }
+ if (tp->default_port) {
+ printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n",
+ (option & 0x20 ? 100 : 10),
+ (option & 0x10 ? "full" : "half"));
+ mdio_write(dev, tp->phys[0], 0,
+ ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */
+ ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+ }
+
+ /* Put the chip into low-power mode. */
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
+
+ return 0;
+
+err_out:
+ __rtl8139_cleanup_dev (dev);
+ pci_disable_device (pdev);
+ return i;
+}
+
+
+static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ assert (dev != NULL);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ unregister_netdev (dev);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ __rtl8139_cleanup_dev (dev);
+ pci_disable_device (pdev);
+}
+
+
+/* Serial EEPROM section. */
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x08 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x00
+#define EE_WRITE_1 0x02
+#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
+#define EE_ENB (0x80 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
+ */
+
+#define eeprom_delay() RTL_R32(Cfg9346)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5)
+#define EE_READ_CMD (6)
+#define EE_ERASE_CMD (7)
+
+static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len)
+{
+ int i;
+ unsigned retval = 0;
+ int read_cmd = location | (EE_READ_CMD << addr_len);
+
+ RTL_W8 (Cfg9346, EE_ENB & ~EE_CS);
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+
+ /* Shift the read command bits out. */
+ for (i = 4 + addr_len; i >= 0; i--) {
+ int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ RTL_W8 (Cfg9346, EE_ENB | dataval);
+ eeprom_delay ();
+ RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK);
+ eeprom_delay ();
+ }
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+
+ for (i = 16; i > 0; i--) {
+ RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK);
+ eeprom_delay ();
+ retval =
+ (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 :
+ 0);
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+ }
+
+ /* Terminate the EEPROM access. */
+ RTL_W8 (Cfg9346, ~EE_CS);
+ eeprom_delay ();
+
+ return retval;
+}
+
+/* MII serial management: mostly bogus for now. */
+/* Read and write the MII management registers using software-generated
+ serial MDIO protocol.
+ The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+ "overclocking" issues. */
+#define MDIO_DIR 0x80
+#define MDIO_DATA_OUT 0x04
+#define MDIO_DATA_IN 0x02
+#define MDIO_CLK 0x01
+#define MDIO_WRITE0 (MDIO_DIR)
+#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
+
+#define mdio_delay() RTL_R8(Config4)
+
+
+static char mii_2_8139_map[8] = {
+ BasicModeCtrl,
+ BasicModeStatus,
+ 0,
+ 0,
+ NWayAdvert,
+ NWayLPAR,
+ NWayExpansion,
+ 0
+};
+
+
+#ifdef CONFIG_8139TOO_8129
+/* Syncronize the MII management interface by shifting 32 one bits out. */
+static void mdio_sync (void __iomem *ioaddr)
+{
+ int i;
+
+ for (i = 32; i >= 0; i--) {
+ RTL_W8 (Config4, MDIO_WRITE1);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK);
+ mdio_delay ();
+ }
+}
+#endif
+
+static int mdio_read (struct net_device *dev, int phy_id, int location)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int retval = 0;
+#ifdef CONFIG_8139TOO_8129
+ void __iomem *ioaddr = tp->mmio_addr;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i;
+#endif
+
+ if (phy_id > 31) { /* Really a 8139. Use internal registers. */
+ void __iomem *ioaddr = tp->mmio_addr;
+ return location < 8 && mii_2_8139_map[location] ?
+ RTL_R16 (mii_2_8139_map[location]) : 0;
+ }
+
+#ifdef CONFIG_8139TOO_8129
+ mdio_sync (ioaddr);
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
+
+ RTL_W8 (Config4, MDIO_DIR | dataval);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK);
+ mdio_delay ();
+ }
+
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ RTL_W8 (Config4, 0);
+ mdio_delay ();
+ retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0);
+ RTL_W8 (Config4, MDIO_CLK);
+ mdio_delay ();
+ }
+#endif
+
+ return (retval >> 1) & 0xffff;
+}
+
+
+static void mdio_write (struct net_device *dev, int phy_id, int location,
+ int value)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+#ifdef CONFIG_8139TOO_8129
+ void __iomem *ioaddr = tp->mmio_addr;
+ int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
+ int i;
+#endif
+
+ if (phy_id > 31) { /* Really a 8139. Use internal registers. */
+ void __iomem *ioaddr = tp->mmio_addr;
+ if (location == 0) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W16 (BasicModeCtrl, value);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ } else if (location < 8 && mii_2_8139_map[location])
+ RTL_W16 (mii_2_8139_map[location], value);
+ return;
+ }
+
+#ifdef CONFIG_8139TOO_8129
+ mdio_sync (ioaddr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval =
+ (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+ RTL_W8 (Config4, dataval);
+ mdio_delay ();
+ RTL_W8 (Config4, dataval | MDIO_CLK);
+ mdio_delay ();
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ RTL_W8 (Config4, 0);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_CLK);
+ mdio_delay ();
+ }
+#endif
+}
+
+
+static int rtl8139_open (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int retval;
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ retval = request_irq(dev->irq, rtl8139_interrupt,
+ SA_SHIRQ, dev->name, dev);
+ if (retval)
+ return retval;
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ &tp->tx_bufs_dma);
+ tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ &tp->rx_ring_dma);
+ if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ free_irq(dev->irq, dev);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ if (tp->tx_bufs)
+ pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ tp->tx_bufs, tp->tx_bufs_dma);
+ if (tp->rx_ring)
+ pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ tp->rx_ring, tp->rx_ring_dma);
+
+ return -ENOMEM;
+
+ }
+
+ tp->mii.full_duplex = tp->mii.force_media;
+ tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
+
+ rtl8139_init_ring (dev);
+ rtl8139_hw_start (dev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ netif_start_queue (dev);
+
+ if (netif_msg_ifup(tp))
+ printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d"
+ " GP Pins %2.2x %s-duplex.\n",
+ dev->name, pci_resource_start (tp->pci_dev, 1),
+ dev->irq, RTL_R8 (MediaStatus),
+ tp->mii.full_duplex ? "full" : "half");
+
+ rtl8139_start_thread(dev);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ return 0;
+}
+
+
+static void rtl_check_media (struct net_device *dev, unsigned int init_media)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ if (tp->phys[0] >= 0) {
+ mii_check_media(&tp->mii, netif_msg_link(tp), init_media);
+ }
+ } else {
+ void __iomem *ioaddr = tp->mmio_addr;
+ uint16_t state = RTL_R16(BasicModeStatus) & BMSR_LSTATUS;
+ ecdev_link_state(rtl_ec_dev, state ? 1 : 0);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+/* Start the hardware at open or resume. */
+static void rtl8139_hw_start (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u32 i;
+ u8 tmp;
+
+ /* Bring old chips out of low-power mode. */
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'R');
+
+ rtl8139_chip_reset (ioaddr);
+
+ /* unlock Config[01234] and BMCR register writes */
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+ /* Restore our idea of the MAC address. */
+ RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
+ RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
+
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
+ RTL_W32 (RxConfig, tp->rx_config);
+ RTL_W32 (TxConfig, rtl8139_tx_config);
+
+ tp->cur_rx = 0;
+
+ rtl_check_media (dev, 1);
+
+ if (tp->chipset >= CH_8139B) {
+ /* Disable magic packet scanning, which is enabled
+ * when PM is enabled in Config1. It can be reenabled
+ * via ETHTOOL_SWOL if desired. */
+ RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic);
+ }
+
+ DPRINTK("init buffer addresses\n");
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+ /* init Tx buffer DMA addresses */
+ for (i = 0; i < NUM_TX_DESC; i++)
+ RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
+
+ RTL_W32 (RxMissed, 0);
+
+ rtl8139_set_rx_mode (dev);
+
+ /* no early-rx interrupts */
+ RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear);
+
+ /* make sure RxTx has started */
+ tmp = RTL_R8 (ChipCmd);
+ if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb)))
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ /* Enable all known interrupts by setting the interrupt mask. */
+ RTL_W16 (IntrMask, rtl8139_intr_mask);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void rtl8139_init_ring (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int i;
+
+ tp->cur_rx = 0;
+ tp->cur_tx = 0;
+ tp->dirty_tx = 0;
+
+ for (i = 0; i < NUM_TX_DESC; i++)
+ tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE];
+}
+
+
+/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */
+static int next_tick = 3 * HZ;
+
+#ifndef CONFIG_8139TOO_TUNE_TWISTER
+static inline void rtl8139_tune_twister (struct net_device *dev,
+ struct rtl8139_private *tp) {}
+#else
+enum TwisterParamVals {
+ PARA78_default = 0x78fa8388,
+ PARA7c_default = 0xcb38de43, /* param[0][3] */
+ PARA7c_xxx = 0xcb38de43,
+};
+
+static const unsigned long param[4][4] = {
+ {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
+};
+
+static void rtl8139_tune_twister (struct net_device *dev,
+ struct rtl8139_private *tp)
+{
+ int linkcase;
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ /* This is a complicated state machine to configure the "twister" for
+ impedance/echos based on the cable length.
+ All of this is magic and undocumented.
+ */
+ switch (tp->twistie) {
+ case 1:
+ if (RTL_R16 (CSCR) & CSCR_LinkOKBit) {
+ /* We have link beat, let us tune the twister. */
+ RTL_W16 (CSCR, CSCR_LinkDownOffCmd);
+ tp->twistie = 2; /* Change to state 2. */
+ next_tick = HZ / 10;
+ } else {
+ /* Just put in some reasonable defaults for when beat returns. */
+ RTL_W16 (CSCR, CSCR_LinkDownCmd);
+ RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */
+ RTL_W32 (PARA78, PARA78_default);
+ RTL_W32 (PARA7c, PARA7c_default);
+ tp->twistie = 0; /* Bail from future actions. */
+ }
+ break;
+ case 2:
+ /* Read how long it took to hear the echo. */
+ linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits;
+ if (linkcase == 0x7000)
+ tp->twist_row = 3;
+ else if (linkcase == 0x3000)
+ tp->twist_row = 2;
+ else if (linkcase == 0x1000)
+ tp->twist_row = 1;
+ else
+ tp->twist_row = 0;
+ tp->twist_col = 0;
+ tp->twistie = 3; /* Change to state 2. */
+ next_tick = HZ / 10;
+ break;
+ case 3:
+ /* Put out four tuning parameters, one per 100msec. */
+ if (tp->twist_col == 0)
+ RTL_W16 (FIFOTMS, 0);
+ RTL_W32 (PARA7c, param[(int) tp->twist_row]
+ [(int) tp->twist_col]);
+ next_tick = HZ / 10;
+ if (++tp->twist_col >= 4) {
+ /* For short cables we are done.
+ For long cables (row == 3) check for mistune. */
+ tp->twistie =
+ (tp->twist_row == 3) ? 4 : 0;
+ }
+ break;
+ case 4:
+ /* Special case for long cables: check for mistune. */
+ if ((RTL_R16 (CSCR) &
+ CSCR_LinkStatusBits) == 0x7000) {
+ tp->twistie = 0;
+ break;
+ } else {
+ RTL_W32 (PARA7c, 0xfb38de03);
+ tp->twistie = 5;
+ next_tick = HZ / 10;
+ }
+ break;
+ case 5:
+ /* Retune for shorter cable (column 2). */
+ RTL_W32 (FIFOTMS, 0x20);
+ RTL_W32 (PARA78, PARA78_default);
+ RTL_W32 (PARA7c, PARA7c_default);
+ RTL_W32 (FIFOTMS, 0x00);
+ tp->twist_row = 2;
+ tp->twist_col = 0;
+ tp->twistie = 3;
+ next_tick = HZ / 10;
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+}
+#endif /* CONFIG_8139TOO_TUNE_TWISTER */
+
+static inline void rtl8139_thread_iter (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr)
+{
+ int mii_lpa;
+
+ mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA);
+
+ if (!tp->mii.force_media && mii_lpa != 0xffff) {
+ int duplex = (mii_lpa & LPA_100FULL)
+ || (mii_lpa & 0x01C0) == 0x0040;
+ if (tp->mii.full_duplex != duplex) {
+ tp->mii.full_duplex = duplex;
+
+ if (mii_lpa) {
+ printk (KERN_INFO
+ "%s: Setting %s-duplex based on MII #%d link"
+ " partner ability of %4.4x.\n",
+ dev->name,
+ tp->mii.full_duplex ? "full" : "half",
+ tp->phys[0], mii_lpa);
+ } else {
+ printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n",
+ dev->name);
+ }
+#if 0
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+#endif
+ }
+ }
+
+ next_tick = HZ * 60;
+
+ rtl8139_tune_twister (dev, tp);
+
+ DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n",
+ dev->name, RTL_R16 (NWayLPAR));
+ DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n",
+ dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus));
+ DPRINTK ("%s: Chip config %2.2x %2.2x.\n",
+ dev->name, RTL_R8 (Config0),
+ RTL_R8 (Config1));
+}
+
+static int rtl8139_thread (void *data)
+{
+ struct net_device *dev = data;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ unsigned long timeout;
+
+ daemonize("%s", dev->name);
+ allow_signal(SIGTERM);
+
+ while (1) {
+ timeout = next_tick;
+ do {
+ timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout);
+ /* make swsusp happy with our thread */
+ try_to_freeze();
+ } while (!signal_pending (current) && (timeout > 0));
+
+ if (signal_pending (current)) {
+ flush_signals(current);
+ }
+
+ if (tp->time_to_die)
+ break;
+
+ if (rtnl_lock_interruptible ())
+ break;
+ rtl8139_thread_iter (dev, tp, tp->mmio_addr);
+ rtnl_unlock ();
+ }
+
+ complete_and_exit (&tp->thr_exited, 0);
+}
+
+static void rtl8139_start_thread(struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ tp->thr_pid = -1;
+ tp->twistie = 0;
+ tp->time_to_die = 0;
+ if (tp->chipset == CH_8139_K)
+ tp->twistie = 1;
+ else if (tp->drv_flags & HAS_LNK_CHNG)
+ return;
+
+ tp->thr_pid = kernel_thread(rtl8139_thread, dev, CLONE_FS|CLONE_FILES);
+ if (tp->thr_pid < 0) {
+ printk (KERN_WARNING "%s: unable to start kernel thread\n",
+ dev->name);
+ }
+}
+
+static inline void rtl8139_tx_clear (struct rtl8139_private *tp)
+{
+ tp->cur_tx = 0;
+ tp->dirty_tx = 0;
+
+ /* XXX account for unsent Tx packets in tp->stats.tx_dropped */
+}
+
+
+static void rtl8139_tx_timeout (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int i;
+ u8 tmp8;
+ unsigned long flags;
+
+ printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x "
+ "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd),
+ RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus));
+ /* Emit info to figure out what went wrong. */
+ printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n",
+ dev->name, tp->cur_tx, tp->dirty_tx);
+ for (i = 0; i < NUM_TX_DESC; i++)
+ printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n",
+ dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
+ i == tp->dirty_tx % NUM_TX_DESC ?
+ " (queue head)" : "");
+
+ tp->xstats.tx_timeouts++;
+
+ /* disable Tx ASAP, if not already */
+ tmp8 = RTL_R8 (ChipCmd);
+ if (tmp8 & CmdTxEnb)
+ RTL_W8 (ChipCmd, CmdRxEnb);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ spin_lock(&tp->rx_lock);
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0x0000);
+
+ /* Stop a shared interrupt from scavenging while we are. */
+ spin_lock_irqsave (&tp->lock, flags);
+ rtl8139_tx_clear (tp);
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ /* ...and finally, reset everything */
+ if (netif_running(dev)) {
+ rtl8139_hw_start (dev);
+ netif_wake_queue (dev);
+ }
+ spin_unlock(&tp->rx_lock);
+ } else {
+ rtl8139_tx_clear (tp);
+ rtl8139_hw_start (dev);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+
+static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned int entry;
+ unsigned int len = skb->len;
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = tp->cur_tx % NUM_TX_DESC;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ /* Note: the chip doesn't have auto-pad! */
+ if (likely(len < TX_BUF_SIZE)) {
+ if (len < ETH_ZLEN)
+ memset(tp->tx_buf[entry], 0, ETH_ZLEN);
+ skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
+ if (dev != rtl_ec_net_dev) {
+ dev_kfree_skb(skb);
+ }
+ } else {
+ if (dev != rtl_ec_net_dev) {
+ dev_kfree_skb(skb);
+ }
+ tp->stats.tx_dropped++;
+ return 0;
+ }
+
+ if (dev != rtl_ec_net_dev) {
+ spin_lock_irq(&tp->lock);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
+ tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
+
+ dev->trans_start = jiffies;
+
+ tp->cur_tx++;
+ wmb();
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
+ netif_stop_queue (dev);
+ spin_unlock_irq(&tp->lock);
+
+ if (netif_msg_tx_queued(tp))
+ printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n",
+ dev->name, len, entry);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ return 0;
+}
+
+
+static void rtl8139_tx_interrupt (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr)
+{
+ unsigned long dirty_tx, tx_left;
+
+ assert (dev != NULL);
+ assert (ioaddr != NULL);
+
+ dirty_tx = tp->dirty_tx;
+ tx_left = tp->cur_tx - dirty_tx;
+ while (tx_left > 0) {
+ int entry = dirty_tx % NUM_TX_DESC;
+ int txstatus;
+
+ txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32)));
+
+ if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
+ break; /* It still hasn't been Txed */
+
+ /* Note: TxCarrierLost is always asserted at 100mbps. */
+ if (txstatus & (TxOutOfWindow | TxAborted)) {
+ /* There was an major error, log it. */
+ if (netif_msg_tx_err(tp))
+ printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+ dev->name, txstatus);
+ tp->stats.tx_errors++;
+ if (txstatus & TxAborted) {
+ tp->stats.tx_aborted_errors++;
+ RTL_W32 (TxConfig, TxClearAbt);
+ RTL_W16 (IntrStatus, TxErr);
+ wmb();
+ }
+ if (txstatus & TxCarrierLost)
+ tp->stats.tx_carrier_errors++;
+ if (txstatus & TxOutOfWindow)
+ tp->stats.tx_window_errors++;
+ } else {
+ if (txstatus & TxUnderrun) {
+ /* Add 64 to the Tx FIFO threshold. */
+ if (tp->tx_flag < 0x00300000)
+ tp->tx_flag += 0x00020000;
+ tp->stats.tx_fifo_errors++;
+ }
+ tp->stats.collisions += (txstatus >> 24) & 15;
+ tp->stats.tx_bytes += txstatus & 0x7ff;
+ tp->stats.tx_packets++;
+ }
+
+ dirty_tx++;
+ tx_left--;
+ }
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+#ifndef RTL8139_NDEBUG
+ if (dev != rtl_ec_net_dev && tp->cur_tx - dirty_tx > NUM_TX_DESC) {
+ printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n",
+ dev->name, dirty_tx, tp->cur_tx);
+ dirty_tx += NUM_TX_DESC;
+ }
+#endif /* RTL8139_NDEBUG */
+
+ /* only wake the queue if we did work, and the queue is stopped */
+ if (tp->dirty_tx != dirty_tx) {
+ tp->dirty_tx = dirty_tx;
+ mb();
+
+ if (dev != rtl_ec_net_dev) {
+ netif_wake_queue (dev);
+ }
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+
+/* TODO: clean this up! Rx reset need not be this intensive */
+static void rtl8139_rx_err (u32 rx_status, struct net_device *dev,
+ struct rtl8139_private *tp, void __iomem *ioaddr)
+{
+ u8 tmp8;
+#ifdef CONFIG_8139_OLD_RX_RESET
+ int tmp_work;
+#endif
+
+ if (netif_msg_rx_err (tp))
+ printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n",
+ dev->name, rx_status);
+ tp->stats.rx_errors++;
+ if (!(rx_status & RxStatusOK)) {
+ if (rx_status & RxTooLong) {
+ DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
+ dev->name, rx_status);
+ /* A.C.: The chip hangs here. */
+ }
+ if (rx_status & (RxBadSymbol | RxBadAlign))
+ tp->stats.rx_frame_errors++;
+ if (rx_status & (RxRunt | RxTooLong))
+ tp->stats.rx_length_errors++;
+ if (rx_status & RxCRCErr)
+ tp->stats.rx_crc_errors++;
+ } else {
+ tp->xstats.rx_lost_in_ring++;
+ }
+
+#ifndef CONFIG_8139_OLD_RX_RESET
+ tmp8 = RTL_R8 (ChipCmd);
+ RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb);
+ RTL_W8 (ChipCmd, tmp8);
+ RTL_W32 (RxConfig, tp->rx_config);
+ tp->cur_rx = 0;
+#else
+ /* Reset the receiver, based on RealTek recommendation. (Bug?) */
+
+ /* disable receive */
+ RTL_W8_F (ChipCmd, CmdTxEnb);
+ tmp_work = 200;
+ while (--tmp_work > 0) {
+ udelay(1);
+ tmp8 = RTL_R8 (ChipCmd);
+ if (!(tmp8 & CmdRxEnb))
+ break;
+ }
+ if (tmp_work <= 0)
+ printk (KERN_WARNING PFX "rx stop wait too long\n");
+ /* restart receive */
+ tmp_work = 200;
+ while (--tmp_work > 0) {
+ RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb);
+ udelay(1);
+ tmp8 = RTL_R8 (ChipCmd);
+ if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
+ break;
+ }
+ if (tmp_work <= 0)
+ printk (KERN_WARNING PFX "tx/rx enable wait too long\n");
+
+ /* and reinitialize all rx related registers */
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
+ RTL_W32 (RxConfig, tp->rx_config);
+ tp->cur_rx = 0;
+
+ DPRINTK("init buffer addresses\n");
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+ /* A.C.: Reset the multicast list. */
+ __set_rx_mode (dev);
+#endif
+}
+
+#if RX_BUF_IDX == 3
+static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring,
+ u32 offset, unsigned int size)
+{
+ u32 left = RX_BUF_LEN - offset;
+
+ if (size > left) {
+ memcpy(skb->data, ring + offset, left);
+ memcpy(skb->data+left, ring, size - left);
+ } else
+ memcpy(skb->data, ring + offset, size);
+}
+#endif
+
+static void rtl8139_isr_ack(struct rtl8139_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 status;
+
+ status = RTL_R16 (IntrStatus) & RxAckBits;
+
+ /* Clear out errors and receive interrupts */
+ if (likely(status != 0)) {
+ if (unlikely(status & (RxFIFOOver | RxOverflow))) {
+ tp->stats.rx_errors++;
+ if (status & RxFIFOOver)
+ tp->stats.rx_fifo_errors++;
+ }
+ RTL_W16_F (IntrStatus, RxAckBits);
+ }
+}
+
+static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
+ int budget)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ int received = 0;
+ unsigned char *rx_ring = tp->rx_ring;
+ unsigned int cur_rx = tp->cur_rx;
+ unsigned int rx_size = 0;
+
+ DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+ " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx,
+ RTL_R16 (RxBufAddr),
+ RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ while ((dev == rtl_ec_net_dev || netif_running(dev))
+ && received < budget
+ && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ u32 ring_offset = cur_rx % RX_BUF_LEN;
+ u32 rx_status;
+ unsigned int pkt_size;
+ struct sk_buff *skb;
+
+ rmb();
+
+ /* read size+status of next frame from DMA ring buffer */
+ rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
+ rx_size = rx_status >> 16;
+ pkt_size = rx_size - 4;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ if (netif_msg_rx_status(tp))
+ printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x,"
+ " cur %4.4x.\n", dev->name, rx_status,
+ rx_size, cur_rx);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+#if RTL8139_DEBUG > 2
+ {
+ int i;
+ DPRINTK ("%s: Frame contents ", dev->name);
+ for (i = 0; i < 70; i++)
+ printk (" %2.2x",
+ rx_ring[ring_offset + i]);
+ printk (".\n");
+ }
+#endif
+
+ /* Packet copy from FIFO still in progress.
+ * Theoretically, this should never happen
+ * since EarlyRx is disabled.
+ */
+ if (unlikely(rx_size == 0xfff0)) {
+ if (!tp->fifo_copy_timeout)
+ tp->fifo_copy_timeout = jiffies + 2;
+ else if (time_after(jiffies, tp->fifo_copy_timeout)) {
+ DPRINTK ("%s: hung FIFO. Reset.", dev->name);
+ rx_size = 0;
+ goto no_early_rx;
+ }
+ if (netif_msg_intr(tp)) {
+ printk(KERN_DEBUG "%s: fifo copy in progress.",
+ dev->name);
+ }
+ tp->xstats.early_rx++;
+ break;
+ }
+
+no_early_rx:
+ tp->fifo_copy_timeout = 0;
+
+ /* If Rx err or invalid rx_size/rx_status received
+ * (which happens if we get lost in the ring),
+ * Rx process gets reset, so we abort any further
+ * Rx processing.
+ */
+ if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
+ (rx_size < 8) ||
+ (!(rx_status & RxStatusOK)))) {
+ rtl8139_rx_err (rx_status, dev, tp, ioaddr);
+ received = -1;
+ goto out;
+ }
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ /* Malloc up new buffer, compatible with net-2e. */
+ /* Omit the four octet CRC from the length. */
+
+ skb = dev_alloc_skb (pkt_size + 2);
+ if (likely(skb)) {
+ skb->dev = dev;
+ skb_reserve (skb, 2); /* 16 byte align the IP fields. */
+#if RX_BUF_IDX == 3
+ wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
+#else
+ eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
+#endif
+ skb_put (skb, pkt_size);
+
+ skb->protocol = eth_type_trans (skb, dev);
+
+ dev->last_rx = jiffies;
+ tp->stats.rx_bytes += pkt_size;
+ tp->stats.rx_packets++;
+
+ netif_receive_skb (skb);
+ } else {
+ if (net_ratelimit())
+ printk(KERN_WARNING
+ "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ tp->stats.rx_dropped++;
+ }
+ } else {
+ ecdev_receive(rtl_ec_dev,
+ &rx_ring[ring_offset + 4], pkt_size);
+ dev->last_rx = jiffies;
+ tp->stats.rx_bytes += pkt_size;
+ tp->stats.rx_packets++;
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ received++;
+
+ cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
+ RTL_W16 (RxBufPtr, (u16) (cur_rx - 16));
+
+ rtl8139_isr_ack(tp);
+ }
+
+ if (unlikely(!received || rx_size == 0xfff0))
+ rtl8139_isr_ack(tp);
+
+#if RTL8139_DEBUG > 1
+ DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+ " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
+ RTL_R16 (RxBufAddr),
+ RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+#endif
+
+ tp->cur_rx = cur_rx;
+
+ /*
+ * The receive buffer should be mostly empty.
+ * Tell NAPI to reenable the Rx irq.
+ */
+ if (tp->fifo_copy_timeout)
+ received = budget;
+
+out:
+ return received;
+}
+
+
+static void rtl8139_weird_interrupt (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr,
+ int status, int link_changed)
+{
+ DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",
+ dev->name, status);
+
+ assert (dev != NULL);
+ assert (tp != NULL);
+ assert (ioaddr != NULL);
+
+ /* Update the error count. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ if ((status & RxUnderrun) && link_changed &&
+ (tp->drv_flags & HAS_LNK_CHNG)) {
+ rtl_check_media(dev, 0);
+ status &= ~RxUnderrun;
+ }
+
+ if (status & (RxUnderrun | RxErr))
+ tp->stats.rx_errors++;
+
+ if (status & PCSTimeout)
+ tp->stats.rx_length_errors++;
+ if (status & RxUnderrun)
+ tp->stats.rx_fifo_errors++;
+ if (status & PCIErr) {
+ u16 pci_cmd_status;
+ pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status);
+ pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status);
+
+ printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",
+ dev->name, pci_cmd_status);
+ }
+}
+
+static int rtl8139_poll(struct net_device *dev, int *budget)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int orig_budget = min(*budget, dev->quota);
+ int done = 1;
+
+ spin_lock(&tp->rx_lock);
+ if (likely(RTL_R16(IntrStatus) & RxAckBits)) {
+ int work_done;
+
+ work_done = rtl8139_rx(dev, tp, orig_budget);
+ if (likely(work_done > 0)) {
+ *budget -= work_done;
+ dev->quota -= work_done;
+ done = (work_done < orig_budget);
+ }
+ }
+
+ if (done) {
+ /*
+ * Order is important since data can get interrupted
+ * again when we think we are done.
+ */
+ local_irq_disable();
+ RTL_W16_F(IntrMask, rtl8139_intr_mask);
+ __netif_rx_complete(dev);
+ local_irq_enable();
+ }
+ spin_unlock(&tp->rx_lock);
+
+ return !done;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_instance;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 status, ackstat;
+ int link_changed = 0; /* avoid bogus "uninit" warning */
+ int handled = 0;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ spin_lock (&tp->lock);
+ status = RTL_R16 (IntrStatus);
+
+ /* shared irq? */
+ if (unlikely((status & rtl8139_intr_mask) == 0))
+ goto out;
+ } else {
+ status = RTL_R16 (IntrStatus);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ handled = 1;
+
+ /* h/w no longer present (hotplug?) or major error, bail */
+ if (unlikely(status == 0xFFFF))
+ goto out;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ /* close possible race's with dev_close */
+ if (unlikely(!netif_running(dev))) {
+ RTL_W16 (IntrMask, 0);
+ goto out;
+ }
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ /* Acknowledge all of the current interrupt sources ASAP, but
+ an first get an additional status bit from CSCR. */
+ if (unlikely(status & RxUnderrun))
+ link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
+
+ ackstat = status & ~(RxAckBits | TxErr);
+ if (ackstat)
+ RTL_W16 (IntrStatus, ackstat);
+
+ /* Receive packets are processed by poll routine.
+ If not running start it now. */
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (status & RxAckBits){
+ if (dev != rtl_ec_net_dev) {
+ /* Mark for polling */
+ if (netif_rx_schedule_prep(dev)) {
+ RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
+ __netif_rx_schedule (dev);
+ }
+ } else {
+ /* EtherCAT device: Just receive all frames */
+ rtl8139_rx(dev, tp, 100); // FIXME
+ }
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ /* Check uncommon events with one test. */
+ if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr)))
+ rtl8139_weird_interrupt (dev, tp, ioaddr,
+ status, link_changed);
+
+ if (status & (TxOK | TxErr)) {
+ rtl8139_tx_interrupt (dev, tp, ioaddr);
+ if (status & TxErr)
+ RTL_W16 (IntrStatus, TxErr);
+ }
+ out:
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ spin_unlock (&tp->lock);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
+ dev->name, RTL_R16 (IntrStatus));
+ return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void rtl8139_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ rtl8139_interrupt(dev->irq, dev, NULL);
+ enable_irq(dev->irq);
+}
+#endif
+
+static int rtl8139_close (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int ret = 0;
+ unsigned long flags;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ netif_stop_queue (dev);
+
+ if (tp->thr_pid >= 0) {
+ tp->time_to_die = 1;
+ wmb();
+ ret = kill_proc (tp->thr_pid, SIGTERM, 1);
+ if (ret) {
+ printk (KERN_ERR "%s: unable to signal thread\n", dev->name);
+ return ret;
+ }
+ wait_for_completion (&tp->thr_exited);
+ }
+
+ if (netif_msg_ifdown(tp))
+ printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n",
+ dev->name, RTL_R16 (IntrStatus));
+
+ spin_lock_irqsave (&tp->lock, flags);
+
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8 (ChipCmd, 0);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ synchronize_irq (dev->irq); /* racy, but that's ok here */
+ free_irq (dev->irq, dev);
+ } else {
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8 (ChipCmd, 0);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ rtl8139_tx_clear (tp);
+
+ pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ tp->rx_ring, tp->rx_ring_dma);
+ pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ tp->tx_bufs, tp->tx_bufs_dma);
+ tp->rx_ring = NULL;
+ tp->tx_bufs = NULL;
+
+ /* Green! Put the chip in low-power mode. */
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
+
+ return 0;
+}
+
+
+/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to
+ kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and
+ other threads or interrupts aren't messing with the 8139. */
+static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->mmio_addr;
+
+ spin_lock_irq(&np->lock);
+ if (rtl_chip_info[np->chipset].flags & HasLWake) {
+ u8 cfg3 = RTL_R8 (Config3);
+ u8 cfg5 = RTL_R8 (Config5);
+
+ wol->supported = WAKE_PHY | WAKE_MAGIC
+ | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST;
+
+ wol->wolopts = 0;
+ if (cfg3 & Cfg3_LinkUp)
+ wol->wolopts |= WAKE_PHY;
+ if (cfg3 & Cfg3_Magic)
+ wol->wolopts |= WAKE_MAGIC;
+ /* (KON)FIXME: See how netdev_set_wol() handles the
+ following constants. */
+ if (cfg5 & Cfg5_UWF)
+ wol->wolopts |= WAKE_UCAST;
+ if (cfg5 & Cfg5_MWF)
+ wol->wolopts |= WAKE_MCAST;
+ if (cfg5 & Cfg5_BWF)
+ wol->wolopts |= WAKE_BCAST;
+ }
+ spin_unlock_irq(&np->lock);
+}
+
+
+/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes
+ that wol points to kernel memory and other threads or interrupts
+ aren't messing with the 8139. */
+static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->mmio_addr;
+ u32 support;
+ u8 cfg3, cfg5;
+
+ support = ((rtl_chip_info[np->chipset].flags & HasLWake)
+ ? (WAKE_PHY | WAKE_MAGIC
+ | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)
+ : 0);
+ if (wol->wolopts & ~support)
+ return -EINVAL;
+
+ spin_lock_irq(&np->lock);
+ cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic);
+ if (wol->wolopts & WAKE_PHY)
+ cfg3 |= Cfg3_LinkUp;
+ if (wol->wolopts & WAKE_MAGIC)
+ cfg3 |= Cfg3_Magic;
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config3, cfg3);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF);
+ /* (KON)FIXME: These are untested. We may have to set the
+ CRC0, Wakeup0 and LSBCRC0 registers too, but I have no
+ documentation. */
+ if (wol->wolopts & WAKE_UCAST)
+ cfg5 |= Cfg5_UWF;
+ if (wol->wolopts & WAKE_MCAST)
+ cfg5 |= Cfg5_MWF;
+ if (wol->wolopts & WAKE_BCAST)
+ cfg5 |= Cfg5_BWF;
+ RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */
+ spin_unlock_irq(&np->lock);
+
+ return 0;
+}
+
+static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, pci_name(np->pci_dev));
+ info->regdump_len = np->regs_len;
+}
+
+static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ spin_lock_irq(&np->lock);
+ mii_ethtool_gset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+ return 0;
+}
+
+static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ int rc;
+ spin_lock_irq(&np->lock);
+ rc = mii_ethtool_sset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+ return rc;
+}
+
+static int rtl8139_nway_reset(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return mii_nway_restart(&np->mii);
+}
+
+static u32 rtl8139_get_link(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return mii_link_ok(&np->mii);
+}
+
+static u32 rtl8139_get_msglevel(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return np->msg_enable;
+}
+
+static void rtl8139_set_msglevel(struct net_device *dev, u32 datum)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ np->msg_enable = datum;
+}
+
+/* TODO: we are too slack to do reg dumping for pio, for now */
+#ifdef CONFIG_8139TOO_PIO
+#define rtl8139_get_regs_len NULL
+#define rtl8139_get_regs NULL
+#else
+static int rtl8139_get_regs_len(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return np->regs_len;
+}
+
+static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+
+ regs->version = RTL_REGS_VER;
+
+ spin_lock_irq(&np->lock);
+ memcpy_fromio(regbuf, np->mmio_addr, regs->len);
+ spin_unlock_irq(&np->lock);
+}
+#endif /* CONFIG_8139TOO_MMIO */
+
+static int rtl8139_get_stats_count(struct net_device *dev)
+{
+ return RTL_NUM_STATS;
+}
+
+static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+
+ data[0] = np->xstats.early_rx;
+ data[1] = np->xstats.tx_buf_mapped;
+ data[2] = np->xstats.tx_timeouts;
+ data[3] = np->xstats.rx_lost_in_ring;
+}
+
+static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys));
+}
+
+static struct ethtool_ops rtl8139_ethtool_ops = {
+ .get_drvinfo = rtl8139_get_drvinfo,
+ .get_settings = rtl8139_get_settings,
+ .set_settings = rtl8139_set_settings,
+ .get_regs_len = rtl8139_get_regs_len,
+ .get_regs = rtl8139_get_regs,
+ .nway_reset = rtl8139_nway_reset,
+ .get_link = rtl8139_get_link,
+ .get_msglevel = rtl8139_get_msglevel,
+ .set_msglevel = rtl8139_set_msglevel,
+ .get_wol = rtl8139_get_wol,
+ .set_wol = rtl8139_set_wol,
+ .get_strings = rtl8139_get_strings,
+ .get_stats_count = rtl8139_get_stats_count,
+ .get_ethtool_stats = rtl8139_get_ethtool_stats,
+};
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ int rc;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev == rtl_ec_net_dev || !netif_running(dev))
+ return -EINVAL;
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ spin_lock_irq(&np->lock);
+ rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
+ spin_unlock_irq(&np->lock);
+
+ return rc;
+}
+
+
+static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev == rtl_ec_net_dev || netif_running(dev)) {
+ spin_lock_irqsave (&tp->lock, flags);
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+ spin_unlock_irqrestore (&tp->lock, flags);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ return &tp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ This routine is not state sensitive and need not be SMP locked. */
+
+static void __set_rx_mode (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u32 mc_filter[2]; /* Multicast hash filter */
+ int i, rx_mode;
+ u32 tmp;
+
+ DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n",
+ dev->name, dev->flags, RTL_R32 (RxConfig));
+
+ /* Note: do not reorder, GCC is clever about common statements. */
+ if (dev->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n",
+ dev->name);
+ rx_mode =
+ AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+ AcceptAllPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else if ((dev->mc_count > multicast_filter_limit)
+ || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else {
+ struct dev_mc_list *mclist;
+ rx_mode = AcceptBroadcast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0;
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+
+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+ rx_mode |= AcceptMulticast;
+ }
+ }
+
+ /* We can safely update without stopping the chip. */
+ tmp = rtl8139_rx_config | rx_mode;
+ if (tp->rx_config != tmp) {
+ RTL_W32_F (RxConfig, tmp);
+ tp->rx_config = tmp;
+ }
+ RTL_W32_F (MAR0 + 0, mc_filter[0]);
+ RTL_W32_F (MAR0 + 4, mc_filter[1]);
+}
+
+static void rtl8139_set_rx_mode (struct net_device *dev)
+{
+ unsigned long flags;
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ spin_lock_irqsave (&tp->lock, flags);
+ __set_rx_mode(dev);
+ spin_unlock_irqrestore (&tp->lock, flags);
+}
+
+#ifdef CONFIG_PM
+
+static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ pci_save_state (pdev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev == rtl_ec_net_dev || !netif_running (dev))
+ return 0;
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ netif_device_detach (dev);
+
+ spin_lock_irqsave (&tp->lock, flags);
+
+ /* Disable interrupts, stop Tx and Rx. */
+ RTL_W16 (IntrMask, 0);
+ RTL_W8 (ChipCmd, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ pci_set_power_state (pdev, PCI_D3hot);
+
+ return 0;
+}
+
+
+static int rtl8139_resume (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ pci_restore_state (pdev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev == rtl_ec_net_dev || !netif_running (dev))
+ return 0;
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ pci_set_power_state (pdev, PCI_D0);
+ rtl8139_init_ring (dev);
+ rtl8139_hw_start (dev);
+ netif_device_attach (dev);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver rtl8139_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = rtl8139_pci_tbl,
+ .probe = rtl8139_init_one,
+ .remove = __devexit_p(rtl8139_remove_one),
+#ifdef CONFIG_PM
+ .suspend = rtl8139_suspend,
+ .resume = rtl8139_resume,
+#endif /* CONFIG_PM */
+};
+
+
+static int __init rtl8139_init_module (void)
+{
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ printk(KERN_INFO RTL8139_DRIVER_NAME "\n");
+ printk(KERN_INFO "ec_device_index is %i\n", ec_device_index);
+
+ if (pci_module_init(&rtl8139_pci_driver) < 0) {
+ printk(KERN_ERR "Failed to init PCI module.\n");
+ goto out_return;
+ }
+
+ if (rtl_ec_net_dev) {
+ printk(KERN_INFO "Registering EtherCAT device...\n");
+ if (!(rtl_ec_dev = ecdev_register(ec_device_master_index,
+ rtl_ec_net_dev, rtl8139_interrupt, THIS_MODULE))) {
+ printk(KERN_ERR "Failed to register EtherCAT device!\n");
+ goto out_pci;
+ }
+
+ printk(KERN_INFO "Starting EtherCAT device...\n");
+ if (ecdev_start(ec_device_master_index)) {
+ printk(KERN_ERR "Failed to start EtherCAT device!\n");
+ goto out_unregister;
+ }
+ } else {
+ printk(KERN_WARNING "No EtherCAT device registered!\n");
+ }
+
+ return 0;
+
+ out_unregister:
+ ecdev_unregister(ec_device_master_index, rtl_ec_dev);
+ out_pci:
+ pci_unregister_driver(&rtl8139_pci_driver);
+ out_return:
+ return -1;
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+
+static void __exit rtl8139_cleanup_module (void)
+{
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ printk(KERN_INFO "Cleaning up RTL8139-EtherCAT module...\n");
+
+ if (rtl_ec_net_dev) {
+ printk(KERN_INFO "Stopping device...\n");
+ ecdev_stop(ec_device_master_index);
+ printk(KERN_INFO "Unregistering device...\n");
+ ecdev_unregister(ec_device_master_index, rtl_ec_dev);
+ rtl_ec_dev = NULL;
+ }
+
+ pci_unregister_driver(&rtl8139_pci_driver);
+
+ printk(KERN_INFO "RTL8139-EtherCAT module cleaned up.\n");
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+
+module_init(rtl8139_init_module);
+module_exit(rtl8139_cleanup_module);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devices/8139too-2.6.13-orig.c Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,2646 @@
+/*
+
+ 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux.
+
+ Maintained by Jeff Garzik <jgarzik@pobox.com>
+ Copyright 2000-2002 Jeff Garzik
+
+ Much code comes from Donald Becker's rtl8139.c driver,
+ versions 1.13 and older. This driver was originally based
+ on rtl8139.c version 1.07. Header of rtl8139.c version 1.13:
+
+ -----<snip>-----
+
+ Written 1997-2001 by Donald Becker.
+ This software may be used and distributed according to the
+ terms of the GNU General Public License (GPL), incorporated
+ herein by reference. Drivers based on or derived from this
+ code fall under the GPL and must retain the authorship,
+ copyright and license notice. This file is not a complete
+ program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ This driver is for boards based on the RTL8129 and RTL8139
+ PCI ethernet chips.
+
+ The author may be reached as becker@scyld.com, or C/O Scyld
+ Computing Corporation 410 Severn Ave., Suite 210 Annapolis
+ MD 21403
+
+ Support and updates available at
+ http://www.scyld.com/network/rtl8139.html
+
+ Twister-tuning table provided by Kinston
+ <shangh@realtek.com.tw>.
+
+ -----<snip>-----
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ Contributors:
+
+ Donald Becker - he wrote the original driver, kudos to him!
+ (but please don't e-mail him for support, this isn't his driver)
+
+ Tigran Aivazian - bug fixes, skbuff free cleanup
+
+ Martin Mares - suggestions for PCI cleanup
+
+ David S. Miller - PCI DMA and softnet updates
+
+ Ernst Gill - fixes ported from BSD driver
+
+ Daniel Kobras - identified specific locations of
+ posted MMIO write bugginess
+
+ Gerard Sharp - bug fix, testing and feedback
+
+ David Ford - Rx ring wrap fix
+
+ Dan DeMaggio - swapped RTL8139 cards with me, and allowed me
+ to find and fix a crucial bug on older chipsets.
+
+ Donald Becker/Chris Butterworth/Marcus Westergren -
+ Noticed various Rx packet size-related buglets.
+
+ Santiago Garcia Mantinan - testing and feedback
+
+ Jens David - 2.2.x kernel backports
+
+ Martin Dennett - incredibly helpful insight on undocumented
+ features of the 8139 chips
+
+ Jean-Jacques Michel - bug fix
+
+ Tobias Ringström - Rx interrupt status checking suggestion
+
+ Andrew Morton - Clear blocked signals, avoid
+ buffer overrun setting current->comm.
+
+ Kalle Olavi Niemitalo - Wake-on-LAN ioctls
+
+ Robert Kuebel - Save kernel thread from dying on any signal.
+
+ Submitting bug reports:
+
+ "rtl8139-diag -mmmaaavvveefN" output
+ enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log
+
+*/
+
+#define DRV_NAME "8139too"
+#define DRV_VERSION "0.9.27"
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/completion.h>
+#include <linux/crc32.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+
+#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION
+#define PFX DRV_NAME ": "
+
+/* Default Message level */
+#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK)
+
+
+/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */
+#ifdef CONFIG_8139TOO_PIO
+#define USE_IO_OPS 1
+#endif
+
+/* define to 1, 2 or 3 to enable copious debugging info */
+#define RTL8139_DEBUG 0
+
+/* define to 1 to disable lightweight runtime debugging checks */
+#undef RTL8139_NDEBUG
+
+
+#if RTL8139_DEBUG
+/* note: prints function name for you */
+# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+# define DPRINTK(fmt, args...)
+#endif
+
+#ifdef RTL8139_NDEBUG
+# define assert(expr) do {} while (0)
+#else
+# define assert(expr) \
+ if(unlikely(!(expr))) { \
+ printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr,__FILE__,__FUNCTION__,__LINE__); \
+ }
+#endif
+
+
+/* A few user-configurable values. */
+/* media options */
+#define MAX_UNITS 8
+static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ The RTL chips use a 64 element hash table based on the Ethernet CRC. */
+static int multicast_filter_limit = 32;
+
+/* bitmapped message enable number */
+static int debug = -1;
+
+/*
+ * Receive ring size
+ * Warning: 64K ring has hardware issues and may lock up.
+ */
+#if defined(CONFIG_SH_DREAMCAST)
+#define RX_BUF_IDX 1 /* 16K ring */
+#else
+#define RX_BUF_IDX 2 /* 32K ring */
+#endif
+#define RX_BUF_LEN (8192 << RX_BUF_IDX)
+#define RX_BUF_PAD 16
+#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
+
+#if RX_BUF_LEN == 65536
+#define RX_BUF_TOT_LEN RX_BUF_LEN
+#else
+#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
+#endif
+
+/* Number of Tx descriptor registers. */
+#define NUM_TX_DESC 4
+
+/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/
+#define MAX_ETH_FRAME_SIZE 1536
+
+/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
+#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE
+#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC)
+
+/* PCI Tuning Parameters
+ Threshold is bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
+
+/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */
+#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */
+#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
+#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (6*HZ)
+
+
+enum {
+ HAS_MII_XCVR = 0x010000,
+ HAS_CHIP_XCVR = 0x020000,
+ HAS_LNK_CHNG = 0x040000,
+};
+
+#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */
+#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */
+#define RTL_MIN_IO_SIZE 0x80
+#define RTL8139B_IO_SIZE 256
+
+#define RTL8129_CAPS HAS_MII_XCVR
+#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG
+
+typedef enum {
+ RTL8139 = 0,
+ RTL8129,
+} board_t;
+
+
+/* indexed by board_t, above */
+static struct {
+ const char *name;
+ u32 hw_flags;
+} board_info[] __devinitdata = {
+ { "RealTek RTL8139", RTL8139_CAPS },
+ { "RealTek RTL8129", RTL8129_CAPS },
+};
+
+
+static struct pci_device_id rtl8139_pci_tbl[] = {
+ {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+
+#ifdef CONFIG_SH_SECUREEDGE5410
+ /* Bogus 8139 silicon reports 8129 without external PROM :-( */
+ {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+#endif
+#ifdef CONFIG_8139TOO_8129
+ {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
+#endif
+
+ /* some crazy cards report invalid vendor ids like
+ * 0x0001 here. The other ids are valid and constant,
+ * so we simply don't match on the main vendor id.
+ */
+ {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
+ {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },
+ {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },
+
+ {0,}
+};
+MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
+
+static struct {
+ const char str[ETH_GSTRING_LEN];
+} ethtool_stats_keys[] = {
+ { "early_rx" },
+ { "tx_buf_mapped" },
+ { "tx_timeouts" },
+ { "rx_lost_in_ring" },
+};
+
+/* The rest of these values should never change. */
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+ MAC0 = 0, /* Ethernet hardware address. */
+ MAR0 = 8, /* Multicast filter. */
+ TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */
+ TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
+ RxBuf = 0x30,
+ ChipCmd = 0x37,
+ RxBufPtr = 0x38,
+ RxBufAddr = 0x3A,
+ IntrMask = 0x3C,
+ IntrStatus = 0x3E,
+ TxConfig = 0x40,
+ RxConfig = 0x44,
+ Timer = 0x48, /* A general-purpose counter. */
+ RxMissed = 0x4C, /* 24 bits valid, write clears. */
+ Cfg9346 = 0x50,
+ Config0 = 0x51,
+ Config1 = 0x52,
+ FlashReg = 0x54,
+ MediaStatus = 0x58,
+ Config3 = 0x59,
+ Config4 = 0x5A, /* absent on RTL-8139A */
+ HltClk = 0x5B,
+ MultiIntr = 0x5C,
+ TxSummary = 0x60,
+ BasicModeCtrl = 0x62,
+ BasicModeStatus = 0x64,
+ NWayAdvert = 0x66,
+ NWayLPAR = 0x68,
+ NWayExpansion = 0x6A,
+ /* Undocumented registers, but required for proper operation. */
+ FIFOTMS = 0x70, /* FIFO Control and test. */
+ CSCR = 0x74, /* Chip Status and Configuration Register. */
+ PARA78 = 0x78,
+ PARA7c = 0x7c, /* Magic transceiver parameter register. */
+ Config5 = 0xD8, /* absent on RTL-8139A */
+};
+
+enum ClearBitMasks {
+ MultiIntrClear = 0xF000,
+ ChipCmdClear = 0xE2,
+ Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
+};
+
+enum ChipCmdBits {
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08,
+ CmdTxEnb = 0x04,
+ RxBufEmpty = 0x01,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+ PCIErr = 0x8000,
+ PCSTimeout = 0x4000,
+ RxFIFOOver = 0x40,
+ RxUnderrun = 0x20,
+ RxOverflow = 0x10,
+ TxErr = 0x08,
+ TxOK = 0x04,
+ RxErr = 0x02,
+ RxOK = 0x01,
+
+ RxAckBits = RxFIFOOver | RxOverflow | RxOK,
+};
+
+enum TxStatusBits {
+ TxHostOwns = 0x2000,
+ TxUnderrun = 0x4000,
+ TxStatOK = 0x8000,
+ TxOutOfWindow = 0x20000000,
+ TxAborted = 0x40000000,
+ TxCarrierLost = 0x80000000,
+};
+enum RxStatusBits {
+ RxMulticast = 0x8000,
+ RxPhysical = 0x4000,
+ RxBroadcast = 0x2000,
+ RxBadSymbol = 0x0020,
+ RxRunt = 0x0010,
+ RxTooLong = 0x0008,
+ RxCRCErr = 0x0004,
+ RxBadAlign = 0x0002,
+ RxStatusOK = 0x0001,
+};
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+};
+
+/* Bits in TxConfig. */
+enum tx_config_bits {
+
+ /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
+ TxIFGShift = 24,
+ TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
+ TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
+ TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
+ TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
+
+ TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
+ TxClearAbt = (1 << 0), /* Clear abort (WO) */
+ TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */
+ TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */
+
+ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+/* Bits in Config1 */
+enum Config1Bits {
+ Cfg1_PM_Enable = 0x01,
+ Cfg1_VPD_Enable = 0x02,
+ Cfg1_PIO = 0x04,
+ Cfg1_MMIO = 0x08,
+ LWAKE = 0x10, /* not on 8139, 8139A */
+ Cfg1_Driver_Load = 0x20,
+ Cfg1_LED0 = 0x40,
+ Cfg1_LED1 = 0x80,
+ SLEEP = (1 << 1), /* only on 8139, 8139A */
+ PWRDN = (1 << 0), /* only on 8139, 8139A */
+};
+
+/* Bits in Config3 */
+enum Config3Bits {
+ Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */
+ Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
+ Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
+ Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */
+ Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */
+ Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
+ Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */
+ Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
+};
+
+/* Bits in Config4 */
+enum Config4Bits {
+ LWPTN = (1 << 2), /* not on 8139, 8139A */
+};
+
+/* Bits in Config5 */
+enum Config5Bits {
+ Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */
+ Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */
+ Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */
+ Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
+ Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */
+ Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */
+ Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */
+};
+
+enum RxConfigBits {
+ /* rx fifo threshold */
+ RxCfgFIFOShift = 13,
+ RxCfgFIFONone = (7 << RxCfgFIFOShift),
+
+ /* Max DMA burst */
+ RxCfgDMAShift = 8,
+ RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
+
+ /* rx ring buffer length */
+ RxCfgRcv8K = 0,
+ RxCfgRcv16K = (1 << 11),
+ RxCfgRcv32K = (1 << 12),
+ RxCfgRcv64K = (1 << 11) | (1 << 12),
+
+ /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
+ RxNoWrap = (1 << 7),
+};
+
+/* Twister tuning parameters from RealTek.
+ Completely undocumented, but required to tune bad links on some boards. */
+enum CSCRBits {
+ CSCR_LinkOKBit = 0x0400,
+ CSCR_LinkChangeBit = 0x0800,
+ CSCR_LinkStatusBits = 0x0f000,
+ CSCR_LinkDownOffCmd = 0x003c0,
+ CSCR_LinkDownCmd = 0x0f3c0,
+};
+
+enum Cfg9346Bits {
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xC0,
+};
+
+typedef enum {
+ CH_8139 = 0,
+ CH_8139_K,
+ CH_8139A,
+ CH_8139A_G,
+ CH_8139B,
+ CH_8130,
+ CH_8139C,
+ CH_8100,
+ CH_8100B_8139D,
+ CH_8101,
+} chip_t;
+
+enum chip_flags {
+ HasHltClk = (1 << 0),
+ HasLWake = (1 << 1),
+};
+
+#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
+ (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
+#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1)
+
+/* directly indexed by chip_t, above */
+const static struct {
+ const char *name;
+ u32 version; /* from RTL8139C/RTL8139D docs */
+ u32 flags;
+} rtl_chip_info[] = {
+ { "RTL-8139",
+ HW_REVID(1, 0, 0, 0, 0, 0, 0),
+ HasHltClk,
+ },
+
+ { "RTL-8139 rev K",
+ HW_REVID(1, 1, 0, 0, 0, 0, 0),
+ HasHltClk,
+ },
+
+ { "RTL-8139A",
+ HW_REVID(1, 1, 1, 0, 0, 0, 0),
+ HasHltClk, /* XXX undocumented? */
+ },
+
+ { "RTL-8139A rev G",
+ HW_REVID(1, 1, 1, 0, 0, 1, 0),
+ HasHltClk, /* XXX undocumented? */
+ },
+
+ { "RTL-8139B",
+ HW_REVID(1, 1, 1, 1, 0, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8130",
+ HW_REVID(1, 1, 1, 1, 1, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8139C",
+ HW_REVID(1, 1, 1, 0, 1, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8100",
+ HW_REVID(1, 1, 1, 1, 0, 1, 0),
+ HasLWake,
+ },
+
+ { "RTL-8100B/8139D",
+ HW_REVID(1, 1, 1, 0, 1, 0, 1),
+ HasLWake,
+ },
+
+ { "RTL-8101",
+ HW_REVID(1, 1, 1, 0, 1, 1, 1),
+ HasLWake,
+ },
+};
+
+struct rtl_extra_stats {
+ unsigned long early_rx;
+ unsigned long tx_buf_mapped;
+ unsigned long tx_timeouts;
+ unsigned long rx_lost_in_ring;
+};
+
+struct rtl8139_private {
+ void __iomem *mmio_addr;
+ int drv_flags;
+ struct pci_dev *pci_dev;
+ u32 msg_enable;
+ struct net_device_stats stats;
+ unsigned char *rx_ring;
+ unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */
+ unsigned int tx_flag;
+ unsigned long cur_tx;
+ unsigned long dirty_tx;
+ unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */
+ unsigned char *tx_bufs; /* Tx bounce buffer region. */
+ dma_addr_t rx_ring_dma;
+ dma_addr_t tx_bufs_dma;
+ signed char phys[4]; /* MII device addresses. */
+ char twistie, twist_row, twist_col; /* Twister tune state. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ spinlock_t lock;
+ spinlock_t rx_lock;
+ chip_t chipset;
+ pid_t thr_pid;
+ wait_queue_head_t thr_wait;
+ struct completion thr_exited;
+ u32 rx_config;
+ struct rtl_extra_stats xstats;
+ int time_to_die;
+ struct mii_if_info mii;
+ unsigned int regs_len;
+ unsigned long fifo_copy_timeout;
+};
+
+MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
+MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_param(multicast_filter_limit, int, 0);
+module_param_array(media, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+module_param(debug, int, 0);
+MODULE_PARM_DESC (debug, "8139too bitmapped message enable number");
+MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses");
+MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps");
+MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)");
+
+static int read_eeprom (void __iomem *ioaddr, int location, int addr_len);
+static int rtl8139_open (struct net_device *dev);
+static int mdio_read (struct net_device *dev, int phy_id, int location);
+static void mdio_write (struct net_device *dev, int phy_id, int location,
+ int val);
+static void rtl8139_start_thread(struct net_device *dev);
+static void rtl8139_tx_timeout (struct net_device *dev);
+static void rtl8139_init_ring (struct net_device *dev);
+static int rtl8139_start_xmit (struct sk_buff *skb,
+ struct net_device *dev);
+static int rtl8139_poll(struct net_device *dev, int *budget);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void rtl8139_poll_controller(struct net_device *dev);
+#endif
+static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs);
+static int rtl8139_close (struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
+static void rtl8139_set_rx_mode (struct net_device *dev);
+static void __set_rx_mode (struct net_device *dev);
+static void rtl8139_hw_start (struct net_device *dev);
+static struct ethtool_ops rtl8139_ethtool_ops;
+
+/* write MMIO register, with flush */
+/* Flush avoids rtl8139 bug w/ posted MMIO writes */
+#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0)
+#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0)
+#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0)
+
+
+#define MMIO_FLUSH_AUDIT_COMPLETE 1
+#if MMIO_FLUSH_AUDIT_COMPLETE
+
+/* write MMIO register */
+#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg))
+
+#else
+
+/* write MMIO register, then flush */
+#define RTL_W8 RTL_W8_F
+#define RTL_W16 RTL_W16_F
+#define RTL_W32 RTL_W32_F
+
+#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
+
+/* read MMIO register */
+#define RTL_R8(reg) ioread8 (ioaddr + (reg))
+#define RTL_R16(reg) ioread16 (ioaddr + (reg))
+#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg)))
+
+
+static const u16 rtl8139_intr_mask =
+ PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
+ TxErr | TxOK | RxErr | RxOK;
+
+static const u16 rtl8139_norx_intr_mask =
+ PCIErr | PCSTimeout | RxUnderrun |
+ TxErr | TxOK | RxErr ;
+
+#if RX_BUF_IDX == 0
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv8K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 1
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv16K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 2
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv32K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 3
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv64K |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#else
+#error "Invalid configuration for 8139_RXBUF_IDX"
+#endif
+
+static const unsigned int rtl8139_tx_config =
+ TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift);
+
+static void __rtl8139_cleanup_dev (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ struct pci_dev *pdev;
+
+ assert (dev != NULL);
+ assert (tp->pci_dev != NULL);
+ pdev = tp->pci_dev;
+
+#ifdef USE_IO_OPS
+ if (tp->mmio_addr)
+ ioport_unmap (tp->mmio_addr);
+#else
+ if (tp->mmio_addr)
+ pci_iounmap (pdev, tp->mmio_addr);
+#endif /* USE_IO_OPS */
+
+ /* it's ok to call this even if we have no regions to free */
+ pci_release_regions (pdev);
+
+ free_netdev(dev);
+ pci_set_drvdata (pdev, NULL);
+}
+
+
+static void rtl8139_chip_reset (void __iomem *ioaddr)
+{
+ int i;
+
+ /* Soft reset the chip. */
+ RTL_W8 (ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--) {
+ barrier();
+ if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
+ break;
+ udelay (10);
+ }
+}
+
+
+static int __devinit rtl8139_init_board (struct pci_dev *pdev,
+ struct net_device **dev_out)
+{
+ void __iomem *ioaddr;
+ struct net_device *dev;
+ struct rtl8139_private *tp;
+ u8 tmp8;
+ int rc, disable_dev_on_err = 0;
+ unsigned int i;
+ unsigned long pio_start, pio_end, pio_flags, pio_len;
+ unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
+ u32 version;
+
+ assert (pdev != NULL);
+
+ *dev_out = NULL;
+
+ /* dev and priv zeroed in alloc_etherdev */
+ dev = alloc_etherdev (sizeof (*tp));
+ if (dev == NULL) {
+ printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev));
+ return -ENOMEM;
+ }
+ SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ tp = netdev_priv(dev);
+ tp->pci_dev = pdev;
+
+ /* enable device (incl. PCI PM wakeup and hotplug setup) */
+ rc = pci_enable_device (pdev);
+ if (rc)
+ goto err_out;
+
+ pio_start = pci_resource_start (pdev, 0);
+ pio_end = pci_resource_end (pdev, 0);
+ pio_flags = pci_resource_flags (pdev, 0);
+ pio_len = pci_resource_len (pdev, 0);
+
+ mmio_start = pci_resource_start (pdev, 1);
+ mmio_end = pci_resource_end (pdev, 1);
+ mmio_flags = pci_resource_flags (pdev, 1);
+ mmio_len = pci_resource_len (pdev, 1);
+
+ /* set this immediately, we need to know before
+ * we talk to the chip directly */
+ DPRINTK("PIO region size == 0x%02X\n", pio_len);
+ DPRINTK("MMIO region size == 0x%02lX\n", mmio_len);
+
+#ifdef USE_IO_OPS
+ /* make sure PCI base addr 0 is PIO */
+ if (!(pio_flags & IORESOURCE_IO)) {
+ printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+ /* check for weird/broken PCI region reporting */
+ if (pio_len < RTL_MIN_IO_SIZE) {
+ printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+#else
+ /* make sure PCI base addr 1 is MMIO */
+ if (!(mmio_flags & IORESOURCE_MEM)) {
+ printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+ if (mmio_len < RTL_MIN_IO_SIZE) {
+ printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+#endif
+
+ rc = pci_request_regions (pdev, "8139too");
+ if (rc)
+ goto err_out;
+ disable_dev_on_err = 1;
+
+ /* enable PCI bus-mastering */
+ pci_set_master (pdev);
+
+#ifdef USE_IO_OPS
+ ioaddr = ioport_map(pio_start, pio_len);
+ if (!ioaddr) {
+ printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+ dev->base_addr = pio_start;
+ tp->mmio_addr = ioaddr;
+ tp->regs_len = pio_len;
+#else
+ /* ioremap MMIO region */
+ ioaddr = pci_iomap(pdev, 1, 0);
+ if (ioaddr == NULL) {
+ printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+ dev->base_addr = (long) ioaddr;
+ tp->mmio_addr = ioaddr;
+ tp->regs_len = mmio_len;
+#endif /* USE_IO_OPS */
+
+ /* Bring old chips out of low-power mode. */
+ RTL_W8 (HltClk, 'R');
+
+ /* check for missing/broken hardware */
+ if (RTL_R32 (TxConfig) == 0xFFFFFFFF) {
+ printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n",
+ pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+
+ /* identify chip attached to board */
+ version = RTL_R32 (TxConfig) & HW_REVID_MASK;
+ for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++)
+ if (version == rtl_chip_info[i].version) {
+ tp->chipset = i;
+ goto match;
+ }
+
+ /* if unknown chip, assume array element #0, original RTL-8139 in this case */
+ printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n",
+ pci_name(pdev));
+ printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig));
+ tp->chipset = 0;
+
+match:
+ DPRINTK ("chipset id (%d) == index %d, '%s'\n",
+ version, i, rtl_chip_info[i].name);
+
+ if (tp->chipset >= CH_8139B) {
+ u8 new_tmp8 = tmp8 = RTL_R8 (Config1);
+ DPRINTK("PCI PM wakeup\n");
+ if ((rtl_chip_info[tp->chipset].flags & HasLWake) &&
+ (tmp8 & LWAKE))
+ new_tmp8 &= ~LWAKE;
+ new_tmp8 |= Cfg1_PM_Enable;
+ if (new_tmp8 != tmp8) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config1, tmp8);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ }
+ if (rtl_chip_info[tp->chipset].flags & HasLWake) {
+ tmp8 = RTL_R8 (Config4);
+ if (tmp8 & LWPTN) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config4, tmp8 & ~LWPTN);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ }
+ }
+ } else {
+ DPRINTK("Old chip wakeup\n");
+ tmp8 = RTL_R8 (Config1);
+ tmp8 &= ~(SLEEP | PWRDN);
+ RTL_W8 (Config1, tmp8);
+ }
+
+ rtl8139_chip_reset (ioaddr);
+
+ *dev_out = dev;
+ return 0;
+
+err_out:
+ __rtl8139_cleanup_dev (dev);
+ if (disable_dev_on_err)
+ pci_disable_device (pdev);
+ return rc;
+}
+
+
+static int __devinit rtl8139_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev = NULL;
+ struct rtl8139_private *tp;
+ int i, addr_len, option;
+ void __iomem *ioaddr;
+ static int board_idx = -1;
+ u8 pci_rev;
+
+ assert (pdev != NULL);
+ assert (ent != NULL);
+
+ board_idx++;
+
+ /* when we're built into the kernel, the driver version message
+ * is only printed if at least one 8139 board has been found
+ */
+#ifndef MODULE
+ {
+ static int printed_version;
+ if (!printed_version++)
+ printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
+ }
+#endif
+
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev);
+
+ if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
+ pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) {
+ printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n",
+ pci_name(pdev), pdev->vendor, pdev->device, pci_rev);
+ printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n");
+ }
+
+ i = rtl8139_init_board (pdev, &dev);
+ if (i < 0)
+ return i;
+
+ assert (dev != NULL);
+ tp = netdev_priv(dev);
+
+ ioaddr = tp->mmio_addr;
+ assert (ioaddr != NULL);
+
+ addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6;
+ for (i = 0; i < 3; i++)
+ ((u16 *) (dev->dev_addr))[i] =
+ le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len));
+
+ /* The Rtl8139-specific entries in the device structure. */
+ dev->open = rtl8139_open;
+ dev->hard_start_xmit = rtl8139_start_xmit;
+ dev->poll = rtl8139_poll;
+ dev->weight = 64;
+ dev->stop = rtl8139_close;
+ dev->get_stats = rtl8139_get_stats;
+ dev->set_multicast_list = rtl8139_set_rx_mode;
+ dev->do_ioctl = netdev_ioctl;
+ dev->ethtool_ops = &rtl8139_ethtool_ops;
+ dev->tx_timeout = rtl8139_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = rtl8139_poll_controller;
+#endif
+
+ /* note: the hardware is not capable of sg/csum/highdma, however
+ * through the use of skb_copy_and_csum_dev we enable these
+ * features
+ */
+ dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;
+
+ dev->irq = pdev->irq;
+
+ /* tp zeroed and aligned in alloc_etherdev */
+ tp = netdev_priv(dev);
+
+ /* note: tp->chipset set in rtl8139_init_board */
+ tp->drv_flags = board_info[ent->driver_data].hw_flags;
+ tp->mmio_addr = ioaddr;
+ tp->msg_enable =
+ (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1));
+ spin_lock_init (&tp->lock);
+ spin_lock_init (&tp->rx_lock);
+ init_waitqueue_head (&tp->thr_wait);
+ init_completion (&tp->thr_exited);
+ tp->mii.dev = dev;
+ tp->mii.mdio_read = mdio_read;
+ tp->mii.mdio_write = mdio_write;
+ tp->mii.phy_id_mask = 0x3f;
+ tp->mii.reg_num_mask = 0x1f;
+
+ /* dev is fully set up and ready to use now */
+ DPRINTK("about to register device named %s (%p)...\n", dev->name, dev);
+ i = register_netdev (dev);
+ if (i) goto err_out;
+
+ pci_set_drvdata (pdev, dev);
+
+ printk (KERN_INFO "%s: %s at 0x%lx, "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+ "IRQ %d\n",
+ dev->name,
+ board_info[ent->driver_data].name,
+ dev->base_addr,
+ dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3],
+ dev->dev_addr[4], dev->dev_addr[5],
+ dev->irq);
+
+ printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n",
+ dev->name, rtl_chip_info[tp->chipset].name);
+
+ /* Find the connected MII xcvrs.
+ Doing this in open() would allow detecting external xcvrs later, but
+ takes too much time. */
+#ifdef CONFIG_8139TOO_8129
+ if (tp->drv_flags & HAS_MII_XCVR) {
+ int phy, phy_idx = 0;
+ for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) {
+ int mii_status = mdio_read(dev, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ u16 advertising = mdio_read(dev, phy, 4);
+ tp->phys[phy_idx++] = phy;
+ printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x "
+ "advertising %4.4x.\n",
+ dev->name, phy, mii_status, advertising);
+ }
+ }
+ if (phy_idx == 0) {
+ printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM "
+ "transceiver.\n",
+ dev->name);
+ tp->phys[0] = 32;
+ }
+ } else
+#endif
+ tp->phys[0] = 32;
+ tp->mii.phy_id = tp->phys[0];
+
+ /* The lower four bits are the media type. */
+ option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx];
+ if (option > 0) {
+ tp->mii.full_duplex = (option & 0x210) ? 1 : 0;
+ tp->default_port = option & 0xFF;
+ if (tp->default_port)
+ tp->mii.force_media = 1;
+ }
+ if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0)
+ tp->mii.full_duplex = full_duplex[board_idx];
+ if (tp->mii.full_duplex) {
+ printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
+ /* Changing the MII-advertised media because might prevent
+ re-connection. */
+ tp->mii.force_media = 1;
+ }
+ if (tp->default_port) {
+ printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n",
+ (option & 0x20 ? 100 : 10),
+ (option & 0x10 ? "full" : "half"));
+ mdio_write(dev, tp->phys[0], 0,
+ ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */
+ ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+ }
+
+ /* Put the chip into low-power mode. */
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
+
+ return 0;
+
+err_out:
+ __rtl8139_cleanup_dev (dev);
+ pci_disable_device (pdev);
+ return i;
+}
+
+
+static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ assert (dev != NULL);
+
+ unregister_netdev (dev);
+
+ __rtl8139_cleanup_dev (dev);
+ pci_disable_device (pdev);
+}
+
+
+/* Serial EEPROM section. */
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x08 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x00
+#define EE_WRITE_1 0x02
+#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
+#define EE_ENB (0x80 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
+ */
+
+#define eeprom_delay() RTL_R32(Cfg9346)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5)
+#define EE_READ_CMD (6)
+#define EE_ERASE_CMD (7)
+
+static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len)
+{
+ int i;
+ unsigned retval = 0;
+ int read_cmd = location | (EE_READ_CMD << addr_len);
+
+ RTL_W8 (Cfg9346, EE_ENB & ~EE_CS);
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+
+ /* Shift the read command bits out. */
+ for (i = 4 + addr_len; i >= 0; i--) {
+ int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ RTL_W8 (Cfg9346, EE_ENB | dataval);
+ eeprom_delay ();
+ RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK);
+ eeprom_delay ();
+ }
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+
+ for (i = 16; i > 0; i--) {
+ RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK);
+ eeprom_delay ();
+ retval =
+ (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 :
+ 0);
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+ }
+
+ /* Terminate the EEPROM access. */
+ RTL_W8 (Cfg9346, ~EE_CS);
+ eeprom_delay ();
+
+ return retval;
+}
+
+/* MII serial management: mostly bogus for now. */
+/* Read and write the MII management registers using software-generated
+ serial MDIO protocol.
+ The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+ "overclocking" issues. */
+#define MDIO_DIR 0x80
+#define MDIO_DATA_OUT 0x04
+#define MDIO_DATA_IN 0x02
+#define MDIO_CLK 0x01
+#define MDIO_WRITE0 (MDIO_DIR)
+#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
+
+#define mdio_delay() RTL_R8(Config4)
+
+
+static char mii_2_8139_map[8] = {
+ BasicModeCtrl,
+ BasicModeStatus,
+ 0,
+ 0,
+ NWayAdvert,
+ NWayLPAR,
+ NWayExpansion,
+ 0
+};
+
+
+#ifdef CONFIG_8139TOO_8129
+/* Syncronize the MII management interface by shifting 32 one bits out. */
+static void mdio_sync (void __iomem *ioaddr)
+{
+ int i;
+
+ for (i = 32; i >= 0; i--) {
+ RTL_W8 (Config4, MDIO_WRITE1);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK);
+ mdio_delay ();
+ }
+}
+#endif
+
+static int mdio_read (struct net_device *dev, int phy_id, int location)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int retval = 0;
+#ifdef CONFIG_8139TOO_8129
+ void __iomem *ioaddr = tp->mmio_addr;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i;
+#endif
+
+ if (phy_id > 31) { /* Really a 8139. Use internal registers. */
+ void __iomem *ioaddr = tp->mmio_addr;
+ return location < 8 && mii_2_8139_map[location] ?
+ RTL_R16 (mii_2_8139_map[location]) : 0;
+ }
+
+#ifdef CONFIG_8139TOO_8129
+ mdio_sync (ioaddr);
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
+
+ RTL_W8 (Config4, MDIO_DIR | dataval);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK);
+ mdio_delay ();
+ }
+
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ RTL_W8 (Config4, 0);
+ mdio_delay ();
+ retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0);
+ RTL_W8 (Config4, MDIO_CLK);
+ mdio_delay ();
+ }
+#endif
+
+ return (retval >> 1) & 0xffff;
+}
+
+
+static void mdio_write (struct net_device *dev, int phy_id, int location,
+ int value)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+#ifdef CONFIG_8139TOO_8129
+ void __iomem *ioaddr = tp->mmio_addr;
+ int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
+ int i;
+#endif
+
+ if (phy_id > 31) { /* Really a 8139. Use internal registers. */
+ void __iomem *ioaddr = tp->mmio_addr;
+ if (location == 0) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W16 (BasicModeCtrl, value);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ } else if (location < 8 && mii_2_8139_map[location])
+ RTL_W16 (mii_2_8139_map[location], value);
+ return;
+ }
+
+#ifdef CONFIG_8139TOO_8129
+ mdio_sync (ioaddr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval =
+ (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+ RTL_W8 (Config4, dataval);
+ mdio_delay ();
+ RTL_W8 (Config4, dataval | MDIO_CLK);
+ mdio_delay ();
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ RTL_W8 (Config4, 0);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_CLK);
+ mdio_delay ();
+ }
+#endif
+}
+
+
+static int rtl8139_open (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int retval;
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev);
+ if (retval)
+ return retval;
+
+ tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ &tp->tx_bufs_dma);
+ tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ &tp->rx_ring_dma);
+ if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
+ free_irq(dev->irq, dev);
+
+ if (tp->tx_bufs)
+ pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ tp->tx_bufs, tp->tx_bufs_dma);
+ if (tp->rx_ring)
+ pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ tp->rx_ring, tp->rx_ring_dma);
+
+ return -ENOMEM;
+
+ }
+
+ tp->mii.full_duplex = tp->mii.force_media;
+ tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
+
+ rtl8139_init_ring (dev);
+ rtl8139_hw_start (dev);
+ netif_start_queue (dev);
+
+ if (netif_msg_ifup(tp))
+ printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d"
+ " GP Pins %2.2x %s-duplex.\n",
+ dev->name, pci_resource_start (tp->pci_dev, 1),
+ dev->irq, RTL_R8 (MediaStatus),
+ tp->mii.full_duplex ? "full" : "half");
+
+ rtl8139_start_thread(dev);
+
+ return 0;
+}
+
+
+static void rtl_check_media (struct net_device *dev, unsigned int init_media)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ if (tp->phys[0] >= 0) {
+ mii_check_media(&tp->mii, netif_msg_link(tp), init_media);
+ }
+}
+
+/* Start the hardware at open or resume. */
+static void rtl8139_hw_start (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u32 i;
+ u8 tmp;
+
+ /* Bring old chips out of low-power mode. */
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'R');
+
+ rtl8139_chip_reset (ioaddr);
+
+ /* unlock Config[01234] and BMCR register writes */
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+ /* Restore our idea of the MAC address. */
+ RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
+ RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
+
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
+ RTL_W32 (RxConfig, tp->rx_config);
+ RTL_W32 (TxConfig, rtl8139_tx_config);
+
+ tp->cur_rx = 0;
+
+ rtl_check_media (dev, 1);
+
+ if (tp->chipset >= CH_8139B) {
+ /* Disable magic packet scanning, which is enabled
+ * when PM is enabled in Config1. It can be reenabled
+ * via ETHTOOL_SWOL if desired. */
+ RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic);
+ }
+
+ DPRINTK("init buffer addresses\n");
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+ /* init Tx buffer DMA addresses */
+ for (i = 0; i < NUM_TX_DESC; i++)
+ RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
+
+ RTL_W32 (RxMissed, 0);
+
+ rtl8139_set_rx_mode (dev);
+
+ /* no early-rx interrupts */
+ RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear);
+
+ /* make sure RxTx has started */
+ tmp = RTL_R8 (ChipCmd);
+ if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb)))
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ /* Enable all known interrupts by setting the interrupt mask. */
+ RTL_W16 (IntrMask, rtl8139_intr_mask);
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void rtl8139_init_ring (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int i;
+
+ tp->cur_rx = 0;
+ tp->cur_tx = 0;
+ tp->dirty_tx = 0;
+
+ for (i = 0; i < NUM_TX_DESC; i++)
+ tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE];
+}
+
+
+/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */
+static int next_tick = 3 * HZ;
+
+#ifndef CONFIG_8139TOO_TUNE_TWISTER
+static inline void rtl8139_tune_twister (struct net_device *dev,
+ struct rtl8139_private *tp) {}
+#else
+enum TwisterParamVals {
+ PARA78_default = 0x78fa8388,
+ PARA7c_default = 0xcb38de43, /* param[0][3] */
+ PARA7c_xxx = 0xcb38de43,
+};
+
+static const unsigned long param[4][4] = {
+ {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
+};
+
+static void rtl8139_tune_twister (struct net_device *dev,
+ struct rtl8139_private *tp)
+{
+ int linkcase;
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ /* This is a complicated state machine to configure the "twister" for
+ impedance/echos based on the cable length.
+ All of this is magic and undocumented.
+ */
+ switch (tp->twistie) {
+ case 1:
+ if (RTL_R16 (CSCR) & CSCR_LinkOKBit) {
+ /* We have link beat, let us tune the twister. */
+ RTL_W16 (CSCR, CSCR_LinkDownOffCmd);
+ tp->twistie = 2; /* Change to state 2. */
+ next_tick = HZ / 10;
+ } else {
+ /* Just put in some reasonable defaults for when beat returns. */
+ RTL_W16 (CSCR, CSCR_LinkDownCmd);
+ RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */
+ RTL_W32 (PARA78, PARA78_default);
+ RTL_W32 (PARA7c, PARA7c_default);
+ tp->twistie = 0; /* Bail from future actions. */
+ }
+ break;
+ case 2:
+ /* Read how long it took to hear the echo. */
+ linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits;
+ if (linkcase == 0x7000)
+ tp->twist_row = 3;
+ else if (linkcase == 0x3000)
+ tp->twist_row = 2;
+ else if (linkcase == 0x1000)
+ tp->twist_row = 1;
+ else
+ tp->twist_row = 0;
+ tp->twist_col = 0;
+ tp->twistie = 3; /* Change to state 2. */
+ next_tick = HZ / 10;
+ break;
+ case 3:
+ /* Put out four tuning parameters, one per 100msec. */
+ if (tp->twist_col == 0)
+ RTL_W16 (FIFOTMS, 0);
+ RTL_W32 (PARA7c, param[(int) tp->twist_row]
+ [(int) tp->twist_col]);
+ next_tick = HZ / 10;
+ if (++tp->twist_col >= 4) {
+ /* For short cables we are done.
+ For long cables (row == 3) check for mistune. */
+ tp->twistie =
+ (tp->twist_row == 3) ? 4 : 0;
+ }
+ break;
+ case 4:
+ /* Special case for long cables: check for mistune. */
+ if ((RTL_R16 (CSCR) &
+ CSCR_LinkStatusBits) == 0x7000) {
+ tp->twistie = 0;
+ break;
+ } else {
+ RTL_W32 (PARA7c, 0xfb38de03);
+ tp->twistie = 5;
+ next_tick = HZ / 10;
+ }
+ break;
+ case 5:
+ /* Retune for shorter cable (column 2). */
+ RTL_W32 (FIFOTMS, 0x20);
+ RTL_W32 (PARA78, PARA78_default);
+ RTL_W32 (PARA7c, PARA7c_default);
+ RTL_W32 (FIFOTMS, 0x00);
+ tp->twist_row = 2;
+ tp->twist_col = 0;
+ tp->twistie = 3;
+ next_tick = HZ / 10;
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+}
+#endif /* CONFIG_8139TOO_TUNE_TWISTER */
+
+static inline void rtl8139_thread_iter (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr)
+{
+ int mii_lpa;
+
+ mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA);
+
+ if (!tp->mii.force_media && mii_lpa != 0xffff) {
+ int duplex = (mii_lpa & LPA_100FULL)
+ || (mii_lpa & 0x01C0) == 0x0040;
+ if (tp->mii.full_duplex != duplex) {
+ tp->mii.full_duplex = duplex;
+
+ if (mii_lpa) {
+ printk (KERN_INFO
+ "%s: Setting %s-duplex based on MII #%d link"
+ " partner ability of %4.4x.\n",
+ dev->name,
+ tp->mii.full_duplex ? "full" : "half",
+ tp->phys[0], mii_lpa);
+ } else {
+ printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n",
+ dev->name);
+ }
+#if 0
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+#endif
+ }
+ }
+
+ next_tick = HZ * 60;
+
+ rtl8139_tune_twister (dev, tp);
+
+ DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n",
+ dev->name, RTL_R16 (NWayLPAR));
+ DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n",
+ dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus));
+ DPRINTK ("%s: Chip config %2.2x %2.2x.\n",
+ dev->name, RTL_R8 (Config0),
+ RTL_R8 (Config1));
+}
+
+static int rtl8139_thread (void *data)
+{
+ struct net_device *dev = data;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ unsigned long timeout;
+
+ daemonize("%s", dev->name);
+ allow_signal(SIGTERM);
+
+ while (1) {
+ timeout = next_tick;
+ do {
+ timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout);
+ /* make swsusp happy with our thread */
+ try_to_freeze();
+ } while (!signal_pending (current) && (timeout > 0));
+
+ if (signal_pending (current)) {
+ flush_signals(current);
+ }
+
+ if (tp->time_to_die)
+ break;
+
+ if (rtnl_lock_interruptible ())
+ break;
+ rtl8139_thread_iter (dev, tp, tp->mmio_addr);
+ rtnl_unlock ();
+ }
+
+ complete_and_exit (&tp->thr_exited, 0);
+}
+
+static void rtl8139_start_thread(struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ tp->thr_pid = -1;
+ tp->twistie = 0;
+ tp->time_to_die = 0;
+ if (tp->chipset == CH_8139_K)
+ tp->twistie = 1;
+ else if (tp->drv_flags & HAS_LNK_CHNG)
+ return;
+
+ tp->thr_pid = kernel_thread(rtl8139_thread, dev, CLONE_FS|CLONE_FILES);
+ if (tp->thr_pid < 0) {
+ printk (KERN_WARNING "%s: unable to start kernel thread\n",
+ dev->name);
+ }
+}
+
+static inline void rtl8139_tx_clear (struct rtl8139_private *tp)
+{
+ tp->cur_tx = 0;
+ tp->dirty_tx = 0;
+
+ /* XXX account for unsent Tx packets in tp->stats.tx_dropped */
+}
+
+
+static void rtl8139_tx_timeout (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int i;
+ u8 tmp8;
+ unsigned long flags;
+
+ printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x "
+ "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd),
+ RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus));
+ /* Emit info to figure out what went wrong. */
+ printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n",
+ dev->name, tp->cur_tx, tp->dirty_tx);
+ for (i = 0; i < NUM_TX_DESC; i++)
+ printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n",
+ dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
+ i == tp->dirty_tx % NUM_TX_DESC ?
+ " (queue head)" : "");
+
+ tp->xstats.tx_timeouts++;
+
+ /* disable Tx ASAP, if not already */
+ tmp8 = RTL_R8 (ChipCmd);
+ if (tmp8 & CmdTxEnb)
+ RTL_W8 (ChipCmd, CmdRxEnb);
+
+ spin_lock(&tp->rx_lock);
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0x0000);
+
+ /* Stop a shared interrupt from scavenging while we are. */
+ spin_lock_irqsave (&tp->lock, flags);
+ rtl8139_tx_clear (tp);
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ /* ...and finally, reset everything */
+ if (netif_running(dev)) {
+ rtl8139_hw_start (dev);
+ netif_wake_queue (dev);
+ }
+ spin_unlock(&tp->rx_lock);
+}
+
+
+static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned int entry;
+ unsigned int len = skb->len;
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = tp->cur_tx % NUM_TX_DESC;
+
+ /* Note: the chip doesn't have auto-pad! */
+ if (likely(len < TX_BUF_SIZE)) {
+ if (len < ETH_ZLEN)
+ memset(tp->tx_buf[entry], 0, ETH_ZLEN);
+ skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
+ dev_kfree_skb(skb);
+ } else {
+ dev_kfree_skb(skb);
+ tp->stats.tx_dropped++;
+ return 0;
+ }
+
+ spin_lock_irq(&tp->lock);
+ RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
+ tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
+
+ dev->trans_start = jiffies;
+
+ tp->cur_tx++;
+ wmb();
+
+ if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
+ netif_stop_queue (dev);
+ spin_unlock_irq(&tp->lock);
+
+ if (netif_msg_tx_queued(tp))
+ printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n",
+ dev->name, len, entry);
+
+ return 0;
+}
+
+
+static void rtl8139_tx_interrupt (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr)
+{
+ unsigned long dirty_tx, tx_left;
+
+ assert (dev != NULL);
+ assert (ioaddr != NULL);
+
+ dirty_tx = tp->dirty_tx;
+ tx_left = tp->cur_tx - dirty_tx;
+ while (tx_left > 0) {
+ int entry = dirty_tx % NUM_TX_DESC;
+ int txstatus;
+
+ txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32)));
+
+ if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
+ break; /* It still hasn't been Txed */
+
+ /* Note: TxCarrierLost is always asserted at 100mbps. */
+ if (txstatus & (TxOutOfWindow | TxAborted)) {
+ /* There was an major error, log it. */
+ if (netif_msg_tx_err(tp))
+ printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+ dev->name, txstatus);
+ tp->stats.tx_errors++;
+ if (txstatus & TxAborted) {
+ tp->stats.tx_aborted_errors++;
+ RTL_W32 (TxConfig, TxClearAbt);
+ RTL_W16 (IntrStatus, TxErr);
+ wmb();
+ }
+ if (txstatus & TxCarrierLost)
+ tp->stats.tx_carrier_errors++;
+ if (txstatus & TxOutOfWindow)
+ tp->stats.tx_window_errors++;
+ } else {
+ if (txstatus & TxUnderrun) {
+ /* Add 64 to the Tx FIFO threshold. */
+ if (tp->tx_flag < 0x00300000)
+ tp->tx_flag += 0x00020000;
+ tp->stats.tx_fifo_errors++;
+ }
+ tp->stats.collisions += (txstatus >> 24) & 15;
+ tp->stats.tx_bytes += txstatus & 0x7ff;
+ tp->stats.tx_packets++;
+ }
+
+ dirty_tx++;
+ tx_left--;
+ }
+
+#ifndef RTL8139_NDEBUG
+ if (tp->cur_tx - dirty_tx > NUM_TX_DESC) {
+ printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n",
+ dev->name, dirty_tx, tp->cur_tx);
+ dirty_tx += NUM_TX_DESC;
+ }
+#endif /* RTL8139_NDEBUG */
+
+ /* only wake the queue if we did work, and the queue is stopped */
+ if (tp->dirty_tx != dirty_tx) {
+ tp->dirty_tx = dirty_tx;
+ mb();
+ netif_wake_queue (dev);
+ }
+}
+
+
+/* TODO: clean this up! Rx reset need not be this intensive */
+static void rtl8139_rx_err (u32 rx_status, struct net_device *dev,
+ struct rtl8139_private *tp, void __iomem *ioaddr)
+{
+ u8 tmp8;
+#ifdef CONFIG_8139_OLD_RX_RESET
+ int tmp_work;
+#endif
+
+ if (netif_msg_rx_err (tp))
+ printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n",
+ dev->name, rx_status);
+ tp->stats.rx_errors++;
+ if (!(rx_status & RxStatusOK)) {
+ if (rx_status & RxTooLong) {
+ DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
+ dev->name, rx_status);
+ /* A.C.: The chip hangs here. */
+ }
+ if (rx_status & (RxBadSymbol | RxBadAlign))
+ tp->stats.rx_frame_errors++;
+ if (rx_status & (RxRunt | RxTooLong))
+ tp->stats.rx_length_errors++;
+ if (rx_status & RxCRCErr)
+ tp->stats.rx_crc_errors++;
+ } else {
+ tp->xstats.rx_lost_in_ring++;
+ }
+
+#ifndef CONFIG_8139_OLD_RX_RESET
+ tmp8 = RTL_R8 (ChipCmd);
+ RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb);
+ RTL_W8 (ChipCmd, tmp8);
+ RTL_W32 (RxConfig, tp->rx_config);
+ tp->cur_rx = 0;
+#else
+ /* Reset the receiver, based on RealTek recommendation. (Bug?) */
+
+ /* disable receive */
+ RTL_W8_F (ChipCmd, CmdTxEnb);
+ tmp_work = 200;
+ while (--tmp_work > 0) {
+ udelay(1);
+ tmp8 = RTL_R8 (ChipCmd);
+ if (!(tmp8 & CmdRxEnb))
+ break;
+ }
+ if (tmp_work <= 0)
+ printk (KERN_WARNING PFX "rx stop wait too long\n");
+ /* restart receive */
+ tmp_work = 200;
+ while (--tmp_work > 0) {
+ RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb);
+ udelay(1);
+ tmp8 = RTL_R8 (ChipCmd);
+ if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
+ break;
+ }
+ if (tmp_work <= 0)
+ printk (KERN_WARNING PFX "tx/rx enable wait too long\n");
+
+ /* and reinitialize all rx related registers */
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
+ RTL_W32 (RxConfig, tp->rx_config);
+ tp->cur_rx = 0;
+
+ DPRINTK("init buffer addresses\n");
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+ /* A.C.: Reset the multicast list. */
+ __set_rx_mode (dev);
+#endif
+}
+
+#if RX_BUF_IDX == 3
+static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring,
+ u32 offset, unsigned int size)
+{
+ u32 left = RX_BUF_LEN - offset;
+
+ if (size > left) {
+ memcpy(skb->data, ring + offset, left);
+ memcpy(skb->data+left, ring, size - left);
+ } else
+ memcpy(skb->data, ring + offset, size);
+}
+#endif
+
+static void rtl8139_isr_ack(struct rtl8139_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 status;
+
+ status = RTL_R16 (IntrStatus) & RxAckBits;
+
+ /* Clear out errors and receive interrupts */
+ if (likely(status != 0)) {
+ if (unlikely(status & (RxFIFOOver | RxOverflow))) {
+ tp->stats.rx_errors++;
+ if (status & RxFIFOOver)
+ tp->stats.rx_fifo_errors++;
+ }
+ RTL_W16_F (IntrStatus, RxAckBits);
+ }
+}
+
+static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
+ int budget)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ int received = 0;
+ unsigned char *rx_ring = tp->rx_ring;
+ unsigned int cur_rx = tp->cur_rx;
+ unsigned int rx_size = 0;
+
+ DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+ " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx,
+ RTL_R16 (RxBufAddr),
+ RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+
+ while (netif_running(dev) && received < budget
+ && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
+ u32 ring_offset = cur_rx % RX_BUF_LEN;
+ u32 rx_status;
+ unsigned int pkt_size;
+ struct sk_buff *skb;
+
+ rmb();
+
+ /* read size+status of next frame from DMA ring buffer */
+ rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
+ rx_size = rx_status >> 16;
+ pkt_size = rx_size - 4;
+
+ if (netif_msg_rx_status(tp))
+ printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x,"
+ " cur %4.4x.\n", dev->name, rx_status,
+ rx_size, cur_rx);
+#if RTL8139_DEBUG > 2
+ {
+ int i;
+ DPRINTK ("%s: Frame contents ", dev->name);
+ for (i = 0; i < 70; i++)
+ printk (" %2.2x",
+ rx_ring[ring_offset + i]);
+ printk (".\n");
+ }
+#endif
+
+ /* Packet copy from FIFO still in progress.
+ * Theoretically, this should never happen
+ * since EarlyRx is disabled.
+ */
+ if (unlikely(rx_size == 0xfff0)) {
+ if (!tp->fifo_copy_timeout)
+ tp->fifo_copy_timeout = jiffies + 2;
+ else if (time_after(jiffies, tp->fifo_copy_timeout)) {
+ DPRINTK ("%s: hung FIFO. Reset.", dev->name);
+ rx_size = 0;
+ goto no_early_rx;
+ }
+ if (netif_msg_intr(tp)) {
+ printk(KERN_DEBUG "%s: fifo copy in progress.",
+ dev->name);
+ }
+ tp->xstats.early_rx++;
+ break;
+ }
+
+no_early_rx:
+ tp->fifo_copy_timeout = 0;
+
+ /* If Rx err or invalid rx_size/rx_status received
+ * (which happens if we get lost in the ring),
+ * Rx process gets reset, so we abort any further
+ * Rx processing.
+ */
+ if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
+ (rx_size < 8) ||
+ (!(rx_status & RxStatusOK)))) {
+ rtl8139_rx_err (rx_status, dev, tp, ioaddr);
+ received = -1;
+ goto out;
+ }
+
+ /* Malloc up new buffer, compatible with net-2e. */
+ /* Omit the four octet CRC from the length. */
+
+ skb = dev_alloc_skb (pkt_size + 2);
+ if (likely(skb)) {
+ skb->dev = dev;
+ skb_reserve (skb, 2); /* 16 byte align the IP fields. */
+#if RX_BUF_IDX == 3
+ wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
+#else
+ eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
+#endif
+ skb_put (skb, pkt_size);
+
+ skb->protocol = eth_type_trans (skb, dev);
+
+ dev->last_rx = jiffies;
+ tp->stats.rx_bytes += pkt_size;
+ tp->stats.rx_packets++;
+
+ netif_receive_skb (skb);
+ } else {
+ if (net_ratelimit())
+ printk (KERN_WARNING
+ "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ tp->stats.rx_dropped++;
+ }
+ received++;
+
+ cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
+ RTL_W16 (RxBufPtr, (u16) (cur_rx - 16));
+
+ rtl8139_isr_ack(tp);
+ }
+
+ if (unlikely(!received || rx_size == 0xfff0))
+ rtl8139_isr_ack(tp);
+
+#if RTL8139_DEBUG > 1
+ DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+ " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
+ RTL_R16 (RxBufAddr),
+ RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+#endif
+
+ tp->cur_rx = cur_rx;
+
+ /*
+ * The receive buffer should be mostly empty.
+ * Tell NAPI to reenable the Rx irq.
+ */
+ if (tp->fifo_copy_timeout)
+ received = budget;
+
+out:
+ return received;
+}
+
+
+static void rtl8139_weird_interrupt (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr,
+ int status, int link_changed)
+{
+ DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",
+ dev->name, status);
+
+ assert (dev != NULL);
+ assert (tp != NULL);
+ assert (ioaddr != NULL);
+
+ /* Update the error count. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ if ((status & RxUnderrun) && link_changed &&
+ (tp->drv_flags & HAS_LNK_CHNG)) {
+ rtl_check_media(dev, 0);
+ status &= ~RxUnderrun;
+ }
+
+ if (status & (RxUnderrun | RxErr))
+ tp->stats.rx_errors++;
+
+ if (status & PCSTimeout)
+ tp->stats.rx_length_errors++;
+ if (status & RxUnderrun)
+ tp->stats.rx_fifo_errors++;
+ if (status & PCIErr) {
+ u16 pci_cmd_status;
+ pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status);
+ pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status);
+
+ printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",
+ dev->name, pci_cmd_status);
+ }
+}
+
+static int rtl8139_poll(struct net_device *dev, int *budget)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int orig_budget = min(*budget, dev->quota);
+ int done = 1;
+
+ spin_lock(&tp->rx_lock);
+ if (likely(RTL_R16(IntrStatus) & RxAckBits)) {
+ int work_done;
+
+ work_done = rtl8139_rx(dev, tp, orig_budget);
+ if (likely(work_done > 0)) {
+ *budget -= work_done;
+ dev->quota -= work_done;
+ done = (work_done < orig_budget);
+ }
+ }
+
+ if (done) {
+ /*
+ * Order is important since data can get interrupted
+ * again when we think we are done.
+ */
+ local_irq_disable();
+ RTL_W16_F(IntrMask, rtl8139_intr_mask);
+ __netif_rx_complete(dev);
+ local_irq_enable();
+ }
+ spin_unlock(&tp->rx_lock);
+
+ return !done;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_instance;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 status, ackstat;
+ int link_changed = 0; /* avoid bogus "uninit" warning */
+ int handled = 0;
+
+ spin_lock (&tp->lock);
+ status = RTL_R16 (IntrStatus);
+
+ /* shared irq? */
+ if (unlikely((status & rtl8139_intr_mask) == 0))
+ goto out;
+
+ handled = 1;
+
+ /* h/w no longer present (hotplug?) or major error, bail */
+ if (unlikely(status == 0xFFFF))
+ goto out;
+
+ /* close possible race's with dev_close */
+ if (unlikely(!netif_running(dev))) {
+ RTL_W16 (IntrMask, 0);
+ goto out;
+ }
+
+ /* Acknowledge all of the current interrupt sources ASAP, but
+ an first get an additional status bit from CSCR. */
+ if (unlikely(status & RxUnderrun))
+ link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
+
+ ackstat = status & ~(RxAckBits | TxErr);
+ if (ackstat)
+ RTL_W16 (IntrStatus, ackstat);
+
+ /* Receive packets are processed by poll routine.
+ If not running start it now. */
+ if (status & RxAckBits){
+ if (netif_rx_schedule_prep(dev)) {
+ RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
+ __netif_rx_schedule (dev);
+ }
+ }
+
+ /* Check uncommon events with one test. */
+ if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr)))
+ rtl8139_weird_interrupt (dev, tp, ioaddr,
+ status, link_changed);
+
+ if (status & (TxOK | TxErr)) {
+ rtl8139_tx_interrupt (dev, tp, ioaddr);
+ if (status & TxErr)
+ RTL_W16 (IntrStatus, TxErr);
+ }
+ out:
+ spin_unlock (&tp->lock);
+
+ DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
+ dev->name, RTL_R16 (IntrStatus));
+ return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void rtl8139_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ rtl8139_interrupt(dev->irq, dev, NULL);
+ enable_irq(dev->irq);
+}
+#endif
+
+static int rtl8139_close (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int ret = 0;
+ unsigned long flags;
+
+ netif_stop_queue (dev);
+
+ if (tp->thr_pid >= 0) {
+ tp->time_to_die = 1;
+ wmb();
+ ret = kill_proc (tp->thr_pid, SIGTERM, 1);
+ if (ret) {
+ printk (KERN_ERR "%s: unable to signal thread\n", dev->name);
+ return ret;
+ }
+ wait_for_completion (&tp->thr_exited);
+ }
+
+ if (netif_msg_ifdown(tp))
+ printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n",
+ dev->name, RTL_R16 (IntrStatus));
+
+ spin_lock_irqsave (&tp->lock, flags);
+
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8 (ChipCmd, 0);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ synchronize_irq (dev->irq); /* racy, but that's ok here */
+ free_irq (dev->irq, dev);
+
+ rtl8139_tx_clear (tp);
+
+ pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ tp->rx_ring, tp->rx_ring_dma);
+ pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ tp->tx_bufs, tp->tx_bufs_dma);
+ tp->rx_ring = NULL;
+ tp->tx_bufs = NULL;
+
+ /* Green! Put the chip in low-power mode. */
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
+
+ return 0;
+}
+
+
+/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to
+ kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and
+ other threads or interrupts aren't messing with the 8139. */
+static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->mmio_addr;
+
+ spin_lock_irq(&np->lock);
+ if (rtl_chip_info[np->chipset].flags & HasLWake) {
+ u8 cfg3 = RTL_R8 (Config3);
+ u8 cfg5 = RTL_R8 (Config5);
+
+ wol->supported = WAKE_PHY | WAKE_MAGIC
+ | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST;
+
+ wol->wolopts = 0;
+ if (cfg3 & Cfg3_LinkUp)
+ wol->wolopts |= WAKE_PHY;
+ if (cfg3 & Cfg3_Magic)
+ wol->wolopts |= WAKE_MAGIC;
+ /* (KON)FIXME: See how netdev_set_wol() handles the
+ following constants. */
+ if (cfg5 & Cfg5_UWF)
+ wol->wolopts |= WAKE_UCAST;
+ if (cfg5 & Cfg5_MWF)
+ wol->wolopts |= WAKE_MCAST;
+ if (cfg5 & Cfg5_BWF)
+ wol->wolopts |= WAKE_BCAST;
+ }
+ spin_unlock_irq(&np->lock);
+}
+
+
+/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes
+ that wol points to kernel memory and other threads or interrupts
+ aren't messing with the 8139. */
+static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->mmio_addr;
+ u32 support;
+ u8 cfg3, cfg5;
+
+ support = ((rtl_chip_info[np->chipset].flags & HasLWake)
+ ? (WAKE_PHY | WAKE_MAGIC
+ | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)
+ : 0);
+ if (wol->wolopts & ~support)
+ return -EINVAL;
+
+ spin_lock_irq(&np->lock);
+ cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic);
+ if (wol->wolopts & WAKE_PHY)
+ cfg3 |= Cfg3_LinkUp;
+ if (wol->wolopts & WAKE_MAGIC)
+ cfg3 |= Cfg3_Magic;
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config3, cfg3);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF);
+ /* (KON)FIXME: These are untested. We may have to set the
+ CRC0, Wakeup0 and LSBCRC0 registers too, but I have no
+ documentation. */
+ if (wol->wolopts & WAKE_UCAST)
+ cfg5 |= Cfg5_UWF;
+ if (wol->wolopts & WAKE_MCAST)
+ cfg5 |= Cfg5_MWF;
+ if (wol->wolopts & WAKE_BCAST)
+ cfg5 |= Cfg5_BWF;
+ RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */
+ spin_unlock_irq(&np->lock);
+
+ return 0;
+}
+
+static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, pci_name(np->pci_dev));
+ info->regdump_len = np->regs_len;
+}
+
+static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ spin_lock_irq(&np->lock);
+ mii_ethtool_gset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+ return 0;
+}
+
+static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ int rc;
+ spin_lock_irq(&np->lock);
+ rc = mii_ethtool_sset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+ return rc;
+}
+
+static int rtl8139_nway_reset(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return mii_nway_restart(&np->mii);
+}
+
+static u32 rtl8139_get_link(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return mii_link_ok(&np->mii);
+}
+
+static u32 rtl8139_get_msglevel(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return np->msg_enable;
+}
+
+static void rtl8139_set_msglevel(struct net_device *dev, u32 datum)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ np->msg_enable = datum;
+}
+
+/* TODO: we are too slack to do reg dumping for pio, for now */
+#ifdef CONFIG_8139TOO_PIO
+#define rtl8139_get_regs_len NULL
+#define rtl8139_get_regs NULL
+#else
+static int rtl8139_get_regs_len(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return np->regs_len;
+}
+
+static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+
+ regs->version = RTL_REGS_VER;
+
+ spin_lock_irq(&np->lock);
+ memcpy_fromio(regbuf, np->mmio_addr, regs->len);
+ spin_unlock_irq(&np->lock);
+}
+#endif /* CONFIG_8139TOO_MMIO */
+
+static int rtl8139_get_stats_count(struct net_device *dev)
+{
+ return RTL_NUM_STATS;
+}
+
+static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+
+ data[0] = np->xstats.early_rx;
+ data[1] = np->xstats.tx_buf_mapped;
+ data[2] = np->xstats.tx_timeouts;
+ data[3] = np->xstats.rx_lost_in_ring;
+}
+
+static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys));
+}
+
+static struct ethtool_ops rtl8139_ethtool_ops = {
+ .get_drvinfo = rtl8139_get_drvinfo,
+ .get_settings = rtl8139_get_settings,
+ .set_settings = rtl8139_set_settings,
+ .get_regs_len = rtl8139_get_regs_len,
+ .get_regs = rtl8139_get_regs,
+ .nway_reset = rtl8139_nway_reset,
+ .get_link = rtl8139_get_link,
+ .get_msglevel = rtl8139_get_msglevel,
+ .set_msglevel = rtl8139_set_msglevel,
+ .get_wol = rtl8139_get_wol,
+ .set_wol = rtl8139_set_wol,
+ .get_strings = rtl8139_get_strings,
+ .get_stats_count = rtl8139_get_stats_count,
+ .get_ethtool_stats = rtl8139_get_ethtool_stats,
+};
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ int rc;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ spin_lock_irq(&np->lock);
+ rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
+ spin_unlock_irq(&np->lock);
+
+ return rc;
+}
+
+
+static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ if (netif_running(dev)) {
+ spin_lock_irqsave (&tp->lock, flags);
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+ spin_unlock_irqrestore (&tp->lock, flags);
+ }
+
+ return &tp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ This routine is not state sensitive and need not be SMP locked. */
+
+static void __set_rx_mode (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u32 mc_filter[2]; /* Multicast hash filter */
+ int i, rx_mode;
+ u32 tmp;
+
+ DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n",
+ dev->name, dev->flags, RTL_R32 (RxConfig));
+
+ /* Note: do not reorder, GCC is clever about common statements. */
+ if (dev->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n",
+ dev->name);
+ rx_mode =
+ AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+ AcceptAllPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else if ((dev->mc_count > multicast_filter_limit)
+ || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else {
+ struct dev_mc_list *mclist;
+ rx_mode = AcceptBroadcast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0;
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+
+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+ rx_mode |= AcceptMulticast;
+ }
+ }
+
+ /* We can safely update without stopping the chip. */
+ tmp = rtl8139_rx_config | rx_mode;
+ if (tp->rx_config != tmp) {
+ RTL_W32_F (RxConfig, tmp);
+ tp->rx_config = tmp;
+ }
+ RTL_W32_F (MAR0 + 0, mc_filter[0]);
+ RTL_W32_F (MAR0 + 4, mc_filter[1]);
+}
+
+static void rtl8139_set_rx_mode (struct net_device *dev)
+{
+ unsigned long flags;
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ spin_lock_irqsave (&tp->lock, flags);
+ __set_rx_mode(dev);
+ spin_unlock_irqrestore (&tp->lock, flags);
+}
+
+#ifdef CONFIG_PM
+
+static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ pci_save_state (pdev);
+
+ if (!netif_running (dev))
+ return 0;
+
+ netif_device_detach (dev);
+
+ spin_lock_irqsave (&tp->lock, flags);
+
+ /* Disable interrupts, stop Tx and Rx. */
+ RTL_W16 (IntrMask, 0);
+ RTL_W8 (ChipCmd, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ pci_set_power_state (pdev, PCI_D3hot);
+
+ return 0;
+}
+
+
+static int rtl8139_resume (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ pci_restore_state (pdev);
+ if (!netif_running (dev))
+ return 0;
+ pci_set_power_state (pdev, PCI_D0);
+ rtl8139_init_ring (dev);
+ rtl8139_hw_start (dev);
+ netif_device_attach (dev);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver rtl8139_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = rtl8139_pci_tbl,
+ .probe = rtl8139_init_one,
+ .remove = __devexit_p(rtl8139_remove_one),
+#ifdef CONFIG_PM
+ .suspend = rtl8139_suspend,
+ .resume = rtl8139_resume,
+#endif /* CONFIG_PM */
+};
+
+
+static int __init rtl8139_init_module (void)
+{
+ /* when we're a module, we always print a version message,
+ * even if no 8139 board is found.
+ */
+#ifdef MODULE
+ printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
+#endif
+
+ return pci_module_init (&rtl8139_pci_driver);
+}
+
+
+static void __exit rtl8139_cleanup_module (void)
+{
+ pci_unregister_driver (&rtl8139_pci_driver);
+}
+
+
+module_init(rtl8139_init_module);
+module_exit(rtl8139_cleanup_module);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devices/8139too-2.6.17-ethercat.c Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,2954 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT driver for RTL8139-compatible NICs.
+*/
+
+/*****************************************************************************/
+
+/*
+ Former documentation:
+
+ 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux.
+
+ Maintained by Jeff Garzik <jgarzik@pobox.com>
+ Copyright 2000-2002 Jeff Garzik
+
+ Much code comes from Donald Becker's rtl8139.c driver,
+ versions 1.13 and older. This driver was originally based
+ on rtl8139.c version 1.07. Header of rtl8139.c version 1.13:
+
+ -----<snip>-----
+
+ Written 1997-2001 by Donald Becker.
+ This software may be used and distributed according to the
+ terms of the GNU General Public License (GPL), incorporated
+ herein by reference. Drivers based on or derived from this
+ code fall under the GPL and must retain the authorship,
+ copyright and license notice. This file is not a complete
+ program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ This driver is for boards based on the RTL8129 and RTL8139
+ PCI ethernet chips.
+
+ The author may be reached as becker@scyld.com, or C/O Scyld
+ Computing Corporation 410 Severn Ave., Suite 210 Annapolis
+ MD 21403
+
+ Support and updates available at
+ http://www.scyld.com/network/rtl8139.html
+
+ Twister-tuning table provided by Kinston
+ <shangh@realtek.com.tw>.
+
+ -----<snip>-----
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ Contributors:
+
+ Donald Becker - he wrote the original driver, kudos to him!
+ (but please don't e-mail him for support, this isn't his driver)
+
+ Tigran Aivazian - bug fixes, skbuff free cleanup
+
+ Martin Mares - suggestions for PCI cleanup
+
+ David S. Miller - PCI DMA and softnet updates
+
+ Ernst Gill - fixes ported from BSD driver
+
+ Daniel Kobras - identified specific locations of
+ posted MMIO write bugginess
+
+ Gerard Sharp - bug fix, testing and feedback
+
+ David Ford - Rx ring wrap fix
+
+ Dan DeMaggio - swapped RTL8139 cards with me, and allowed me
+ to find and fix a crucial bug on older chipsets.
+
+ Donald Becker/Chris Butterworth/Marcus Westergren -
+ Noticed various Rx packet size-related buglets.
+
+ Santiago Garcia Mantinan - testing and feedback
+
+ Jens David - 2.2.x kernel backports
+
+ Martin Dennett - incredibly helpful insight on undocumented
+ features of the 8139 chips
+
+ Jean-Jacques Michel - bug fix
+
+ Tobias Ringström - Rx interrupt status checking suggestion
+
+ Andrew Morton - Clear blocked signals, avoid
+ buffer overrun setting current->comm.
+
+ Kalle Olavi Niemitalo - Wake-on-LAN ioctls
+
+ Robert Kuebel - Save kernel thread from dying on any signal.
+
+ Submitting bug reports:
+
+ "rtl8139-diag -mmmaaavvveefN" output
+ enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log
+
+*/
+
+#define DRV_NAME "ec_8139too"
+#define DRV_VERSION "0.9.27"
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/completion.h>
+#include <linux/crc32.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+#include "../globals.h"
+#include "ecdev.h"
+
+#define RTL8139_DRIVER_NAME DRV_NAME \
+ " EtherCAT-capable Fast Ethernet driver " \
+ DRV_VERSION ", master " EC_MASTER_VERSION
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+#define PFX DRV_NAME ": "
+
+/* Default Message level */
+#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK)
+
+
+/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */
+#ifdef CONFIG_8139TOO_PIO
+#define USE_IO_OPS 1
+#endif
+
+/* define to 1, 2 or 3 to enable copious debugging info */
+#define RTL8139_DEBUG 0
+
+/* define to 1 to disable lightweight runtime debugging checks */
+#undef RTL8139_NDEBUG
+
+
+#if RTL8139_DEBUG
+/* note: prints function name for you */
+# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+# define DPRINTK(fmt, args...)
+#endif
+
+#ifdef RTL8139_NDEBUG
+# define assert(expr) do {} while (0)
+#else
+# define assert(expr) \
+ if(unlikely(!(expr))) { \
+ printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr,__FILE__,__FUNCTION__,__LINE__); \
+ }
+#endif
+
+
+/* A few user-configurable values. */
+/* media options */
+#define MAX_UNITS 8
+static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ The RTL chips use a 64 element hash table based on the Ethernet CRC. */
+static int multicast_filter_limit = 32;
+
+/* bitmapped message enable number */
+static int debug = -1;
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+static int ec_device_index = -1;
+static int ec_device_master_index = 0;
+static ec_device_t *rtl_ec_dev;
+struct net_device *rtl_ec_net_dev = NULL;
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+/*
+ * Receive ring size
+ * Warning: 64K ring has hardware issues and may lock up.
+ */
+#if defined(CONFIG_SH_DREAMCAST)
+#define RX_BUF_IDX 1 /* 16K ring */
+#else
+#define RX_BUF_IDX 2 /* 32K ring */
+#endif
+#define RX_BUF_LEN (8192 << RX_BUF_IDX)
+#define RX_BUF_PAD 16
+#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
+
+#if RX_BUF_LEN == 65536
+#define RX_BUF_TOT_LEN RX_BUF_LEN
+#else
+#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
+#endif
+
+/* Number of Tx descriptor registers. */
+#define NUM_TX_DESC 4
+
+/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/
+#define MAX_ETH_FRAME_SIZE 1536
+
+/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
+#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE
+#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC)
+
+/* PCI Tuning Parameters
+ Threshold is bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
+
+/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */
+#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */
+#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
+#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (6*HZ)
+
+
+enum {
+ HAS_MII_XCVR = 0x010000,
+ HAS_CHIP_XCVR = 0x020000,
+ HAS_LNK_CHNG = 0x040000,
+};
+
+#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */
+#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */
+#define RTL_MIN_IO_SIZE 0x80
+#define RTL8139B_IO_SIZE 256
+
+#define RTL8129_CAPS HAS_MII_XCVR
+#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG
+
+typedef enum {
+ RTL8139 = 0,
+ RTL8129,
+} board_t;
+
+
+/* indexed by board_t, above */
+static const struct {
+ const char *name;
+ u32 hw_flags;
+} board_info[] __devinitdata = {
+ { "RealTek RTL8139", RTL8139_CAPS },
+ { "RealTek RTL8129", RTL8129_CAPS },
+};
+
+
+static struct pci_device_id rtl8139_pci_tbl[] = {
+ {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+
+#ifdef CONFIG_SH_SECUREEDGE5410
+ /* Bogus 8139 silicon reports 8129 without external PROM :-( */
+ {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+#endif
+#ifdef CONFIG_8139TOO_8129
+ {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
+#endif
+
+ /* some crazy cards report invalid vendor ids like
+ * 0x0001 here. The other ids are valid and constant,
+ * so we simply don't match on the main vendor id.
+ */
+ {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
+ {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },
+ {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },
+
+ {0,}
+};
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+/* prevent driver from being loaded automatically */
+//MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+static struct {
+ const char str[ETH_GSTRING_LEN];
+} ethtool_stats_keys[] = {
+ { "early_rx" },
+ { "tx_buf_mapped" },
+ { "tx_timeouts" },
+ { "rx_lost_in_ring" },
+};
+
+/* The rest of these values should never change. */
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+ MAC0 = 0, /* Ethernet hardware address. */
+ MAR0 = 8, /* Multicast filter. */
+ TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */
+ TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
+ RxBuf = 0x30,
+ ChipCmd = 0x37,
+ RxBufPtr = 0x38,
+ RxBufAddr = 0x3A,
+ IntrMask = 0x3C,
+ IntrStatus = 0x3E,
+ TxConfig = 0x40,
+ RxConfig = 0x44,
+ Timer = 0x48, /* A general-purpose counter. */
+ RxMissed = 0x4C, /* 24 bits valid, write clears. */
+ Cfg9346 = 0x50,
+ Config0 = 0x51,
+ Config1 = 0x52,
+ FlashReg = 0x54,
+ MediaStatus = 0x58,
+ Config3 = 0x59,
+ Config4 = 0x5A, /* absent on RTL-8139A */
+ HltClk = 0x5B,
+ MultiIntr = 0x5C,
+ TxSummary = 0x60,
+ BasicModeCtrl = 0x62,
+ BasicModeStatus = 0x64,
+ NWayAdvert = 0x66,
+ NWayLPAR = 0x68,
+ NWayExpansion = 0x6A,
+ /* Undocumented registers, but required for proper operation. */
+ FIFOTMS = 0x70, /* FIFO Control and test. */
+ CSCR = 0x74, /* Chip Status and Configuration Register. */
+ PARA78 = 0x78,
+ PARA7c = 0x7c, /* Magic transceiver parameter register. */
+ Config5 = 0xD8, /* absent on RTL-8139A */
+};
+
+enum ClearBitMasks {
+ MultiIntrClear = 0xF000,
+ ChipCmdClear = 0xE2,
+ Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
+};
+
+enum ChipCmdBits {
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08,
+ CmdTxEnb = 0x04,
+ RxBufEmpty = 0x01,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+ PCIErr = 0x8000,
+ PCSTimeout = 0x4000,
+ RxFIFOOver = 0x40,
+ RxUnderrun = 0x20,
+ RxOverflow = 0x10,
+ TxErr = 0x08,
+ TxOK = 0x04,
+ RxErr = 0x02,
+ RxOK = 0x01,
+
+ RxAckBits = RxFIFOOver | RxOverflow | RxOK,
+};
+
+enum TxStatusBits {
+ TxHostOwns = 0x2000,
+ TxUnderrun = 0x4000,
+ TxStatOK = 0x8000,
+ TxOutOfWindow = 0x20000000,
+ TxAborted = 0x40000000,
+ TxCarrierLost = 0x80000000,
+};
+enum RxStatusBits {
+ RxMulticast = 0x8000,
+ RxPhysical = 0x4000,
+ RxBroadcast = 0x2000,
+ RxBadSymbol = 0x0020,
+ RxRunt = 0x0010,
+ RxTooLong = 0x0008,
+ RxCRCErr = 0x0004,
+ RxBadAlign = 0x0002,
+ RxStatusOK = 0x0001,
+};
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+};
+
+/* Bits in TxConfig. */
+enum tx_config_bits {
+
+ /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
+ TxIFGShift = 24,
+ TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
+ TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
+ TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
+ TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
+
+ TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
+ TxClearAbt = (1 << 0), /* Clear abort (WO) */
+ TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */
+ TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */
+
+ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+/* Bits in Config1 */
+enum Config1Bits {
+ Cfg1_PM_Enable = 0x01,
+ Cfg1_VPD_Enable = 0x02,
+ Cfg1_PIO = 0x04,
+ Cfg1_MMIO = 0x08,
+ LWAKE = 0x10, /* not on 8139, 8139A */
+ Cfg1_Driver_Load = 0x20,
+ Cfg1_LED0 = 0x40,
+ Cfg1_LED1 = 0x80,
+ SLEEP = (1 << 1), /* only on 8139, 8139A */
+ PWRDN = (1 << 0), /* only on 8139, 8139A */
+};
+
+/* Bits in Config3 */
+enum Config3Bits {
+ Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */
+ Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
+ Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
+ Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */
+ Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */
+ Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
+ Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */
+ Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
+};
+
+/* Bits in Config4 */
+enum Config4Bits {
+ LWPTN = (1 << 2), /* not on 8139, 8139A */
+};
+
+/* Bits in Config5 */
+enum Config5Bits {
+ Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */
+ Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */
+ Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */
+ Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
+ Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */
+ Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */
+ Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */
+};
+
+enum RxConfigBits {
+ /* rx fifo threshold */
+ RxCfgFIFOShift = 13,
+ RxCfgFIFONone = (7 << RxCfgFIFOShift),
+
+ /* Max DMA burst */
+ RxCfgDMAShift = 8,
+ RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
+
+ /* rx ring buffer length */
+ RxCfgRcv8K = 0,
+ RxCfgRcv16K = (1 << 11),
+ RxCfgRcv32K = (1 << 12),
+ RxCfgRcv64K = (1 << 11) | (1 << 12),
+
+ /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
+ RxNoWrap = (1 << 7),
+};
+
+/* Twister tuning parameters from RealTek.
+ Completely undocumented, but required to tune bad links on some boards. */
+enum CSCRBits {
+ CSCR_LinkOKBit = 0x0400,
+ CSCR_LinkChangeBit = 0x0800,
+ CSCR_LinkStatusBits = 0x0f000,
+ CSCR_LinkDownOffCmd = 0x003c0,
+ CSCR_LinkDownCmd = 0x0f3c0,
+};
+
+enum Cfg9346Bits {
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xC0,
+};
+
+typedef enum {
+ CH_8139 = 0,
+ CH_8139_K,
+ CH_8139A,
+ CH_8139A_G,
+ CH_8139B,
+ CH_8130,
+ CH_8139C,
+ CH_8100,
+ CH_8100B_8139D,
+ CH_8101,
+} chip_t;
+
+enum chip_flags {
+ HasHltClk = (1 << 0),
+ HasLWake = (1 << 1),
+};
+
+#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
+ (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
+#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1)
+
+/* directly indexed by chip_t, above */
+static const struct {
+ const char *name;
+ u32 version; /* from RTL8139C/RTL8139D docs */
+ u32 flags;
+} rtl_chip_info[] = {
+ { "RTL-8139",
+ HW_REVID(1, 0, 0, 0, 0, 0, 0),
+ HasHltClk,
+ },
+
+ { "RTL-8139 rev K",
+ HW_REVID(1, 1, 0, 0, 0, 0, 0),
+ HasHltClk,
+ },
+
+ { "RTL-8139A",
+ HW_REVID(1, 1, 1, 0, 0, 0, 0),
+ HasHltClk, /* XXX undocumented? */
+ },
+
+ { "RTL-8139A rev G",
+ HW_REVID(1, 1, 1, 0, 0, 1, 0),
+ HasHltClk, /* XXX undocumented? */
+ },
+
+ { "RTL-8139B",
+ HW_REVID(1, 1, 1, 1, 0, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8130",
+ HW_REVID(1, 1, 1, 1, 1, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8139C",
+ HW_REVID(1, 1, 1, 0, 1, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8100",
+ HW_REVID(1, 1, 1, 1, 0, 1, 0),
+ HasLWake,
+ },
+
+ { "RTL-8100B/8139D",
+ HW_REVID(1, 1, 1, 0, 1, 0, 1),
+ HasHltClk /* XXX undocumented? */
+ | HasLWake,
+ },
+
+ { "RTL-8101",
+ HW_REVID(1, 1, 1, 0, 1, 1, 1),
+ HasLWake,
+ },
+};
+
+struct rtl_extra_stats {
+ unsigned long early_rx;
+ unsigned long tx_buf_mapped;
+ unsigned long tx_timeouts;
+ unsigned long rx_lost_in_ring;
+};
+
+struct rtl8139_private {
+ void __iomem *mmio_addr;
+ int drv_flags;
+ struct pci_dev *pci_dev;
+ u32 msg_enable;
+ struct net_device_stats stats;
+ unsigned char *rx_ring;
+ unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */
+ unsigned int tx_flag;
+ unsigned long cur_tx;
+ unsigned long dirty_tx;
+ unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */
+ unsigned char *tx_bufs; /* Tx bounce buffer region. */
+ dma_addr_t rx_ring_dma;
+ dma_addr_t tx_bufs_dma;
+ signed char phys[4]; /* MII device addresses. */
+ char twistie, twist_row, twist_col; /* Twister tune state. */
+ unsigned int watchdog_fired : 1;
+ unsigned int default_port : 4; /* Last dev->if_port value. */
+ unsigned int have_thread : 1;
+ spinlock_t lock;
+ spinlock_t rx_lock;
+ chip_t chipset;
+ u32 rx_config;
+ struct rtl_extra_stats xstats;
+
+ struct work_struct thread;
+
+ struct mii_if_info mii;
+ unsigned int regs_len;
+ unsigned long fifo_copy_timeout;
+};
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+MODULE_AUTHOR("Florian Pose <fp@igh-essen.com>");
+MODULE_DESCRIPTION("RealTek RTL-8139 EtherCAT driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(EC_MASTER_VERSION);
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+module_param(multicast_filter_limit, int, 0);
+module_param_array(media, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+module_param(debug, int, 0);
+MODULE_PARM_DESC (debug, "8139too bitmapped message enable number");
+MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses");
+MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps");
+MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)");
+
+/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+module_param(ec_device_index, int, -1);
+module_param(ec_device_master_index, int, 0);
+MODULE_PARM_DESC(ec_device_index,
+ "Index of the device reserved for EtherCAT.");
+MODULE_PARM_DESC(ec_device_master_index,
+ "Index of the EtherCAT master to register the device.");
+
+/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+static int read_eeprom (void __iomem *ioaddr, int location, int addr_len);
+static int rtl8139_open (struct net_device *dev);
+static int mdio_read (struct net_device *dev, int phy_id, int location);
+static void mdio_write (struct net_device *dev, int phy_id, int location,
+ int val);
+static void rtl8139_start_thread(struct rtl8139_private *tp);
+static void rtl8139_tx_timeout (struct net_device *dev);
+static void rtl8139_init_ring (struct net_device *dev);
+static int rtl8139_start_xmit (struct sk_buff *skb,
+ struct net_device *dev);
+static int rtl8139_poll(struct net_device *dev, int *budget);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void rtl8139_poll_controller(struct net_device *dev);
+#endif
+irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs);
+static int rtl8139_close (struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
+static void rtl8139_set_rx_mode (struct net_device *dev);
+static void __set_rx_mode (struct net_device *dev);
+static void rtl8139_hw_start (struct net_device *dev);
+static void rtl8139_thread (void *_data);
+static void rtl8139_tx_timeout_task(void *_data);
+static struct ethtool_ops rtl8139_ethtool_ops;
+
+/* write MMIO register, with flush */
+/* Flush avoids rtl8139 bug w/ posted MMIO writes */
+#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0)
+#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0)
+#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0)
+
+
+#define MMIO_FLUSH_AUDIT_COMPLETE 1
+#if MMIO_FLUSH_AUDIT_COMPLETE
+
+/* write MMIO register */
+#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg))
+
+#else
+
+/* write MMIO register, then flush */
+#define RTL_W8 RTL_W8_F
+#define RTL_W16 RTL_W16_F
+#define RTL_W32 RTL_W32_F
+
+#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
+
+/* read MMIO register */
+#define RTL_R8(reg) ioread8 (ioaddr + (reg))
+#define RTL_R16(reg) ioread16 (ioaddr + (reg))
+#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg)))
+
+
+static const u16 rtl8139_intr_mask =
+ PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
+ TxErr | TxOK | RxErr | RxOK;
+
+static const u16 rtl8139_norx_intr_mask =
+ PCIErr | PCSTimeout | RxUnderrun |
+ TxErr | TxOK | RxErr ;
+
+#if RX_BUF_IDX == 0
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv8K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 1
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv16K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 2
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv32K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 3
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv64K |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#else
+#error "Invalid configuration for 8139_RXBUF_IDX"
+#endif
+
+static const unsigned int rtl8139_tx_config =
+ TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift);
+
+static void __rtl8139_cleanup_dev (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ struct pci_dev *pdev;
+
+ assert (dev != NULL);
+ assert (tp->pci_dev != NULL);
+ pdev = tp->pci_dev;
+
+#ifdef USE_IO_OPS
+ if (tp->mmio_addr)
+ ioport_unmap (tp->mmio_addr);
+#else
+ if (tp->mmio_addr)
+ pci_iounmap (pdev, tp->mmio_addr);
+#endif /* USE_IO_OPS */
+
+ /* it's ok to call this even if we have no regions to free */
+ pci_release_regions (pdev);
+
+ free_netdev(dev);
+ pci_set_drvdata (pdev, NULL);
+}
+
+
+static void rtl8139_chip_reset (void __iomem *ioaddr)
+{
+ int i;
+
+ /* Soft reset the chip. */
+ RTL_W8 (ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--) {
+ barrier();
+ if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
+ break;
+ udelay (10);
+ }
+}
+
+
+static int __devinit rtl8139_init_board (struct pci_dev *pdev,
+ struct net_device **dev_out)
+{
+ void __iomem *ioaddr;
+ struct net_device *dev;
+ struct rtl8139_private *tp;
+ u8 tmp8;
+ int rc, disable_dev_on_err = 0;
+ unsigned int i;
+ unsigned long pio_start, pio_end, pio_flags, pio_len;
+ unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
+ u32 version;
+
+ assert (pdev != NULL);
+
+ *dev_out = NULL;
+
+ /* dev and priv zeroed in alloc_etherdev */
+ dev = alloc_etherdev (sizeof (*tp));
+ if (dev == NULL) {
+ printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev));
+ return -ENOMEM;
+ }
+ SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ tp = netdev_priv(dev);
+ tp->pci_dev = pdev;
+
+ /* enable device (incl. PCI PM wakeup and hotplug setup) */
+ rc = pci_enable_device (pdev);
+ if (rc)
+ goto err_out;
+
+ pio_start = pci_resource_start (pdev, 0);
+ pio_end = pci_resource_end (pdev, 0);
+ pio_flags = pci_resource_flags (pdev, 0);
+ pio_len = pci_resource_len (pdev, 0);
+
+ mmio_start = pci_resource_start (pdev, 1);
+ mmio_end = pci_resource_end (pdev, 1);
+ mmio_flags = pci_resource_flags (pdev, 1);
+ mmio_len = pci_resource_len (pdev, 1);
+
+ /* set this immediately, we need to know before
+ * we talk to the chip directly */
+ DPRINTK("PIO region size == 0x%02X\n", pio_len);
+ DPRINTK("MMIO region size == 0x%02lX\n", mmio_len);
+
+#ifdef USE_IO_OPS
+ /* make sure PCI base addr 0 is PIO */
+ if (!(pio_flags & IORESOURCE_IO)) {
+ printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+ /* check for weird/broken PCI region reporting */
+ if (pio_len < RTL_MIN_IO_SIZE) {
+ printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+#else
+ /* make sure PCI base addr 1 is MMIO */
+ if (!(mmio_flags & IORESOURCE_MEM)) {
+ printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+ if (mmio_len < RTL_MIN_IO_SIZE) {
+ printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+#endif
+
+ rc = pci_request_regions (pdev, "8139too");
+ if (rc)
+ goto err_out;
+ disable_dev_on_err = 1;
+
+ /* enable PCI bus-mastering */
+ pci_set_master (pdev);
+
+#ifdef USE_IO_OPS
+ ioaddr = ioport_map(pio_start, pio_len);
+ if (!ioaddr) {
+ printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+ dev->base_addr = pio_start;
+ tp->mmio_addr = ioaddr;
+ tp->regs_len = pio_len;
+#else
+ /* ioremap MMIO region */
+ ioaddr = pci_iomap(pdev, 1, 0);
+ if (ioaddr == NULL) {
+ printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+ dev->base_addr = (long) ioaddr;
+ tp->mmio_addr = ioaddr;
+ tp->regs_len = mmio_len;
+#endif /* USE_IO_OPS */
+
+ /* Bring old chips out of low-power mode. */
+ RTL_W8 (HltClk, 'R');
+
+ /* check for missing/broken hardware */
+ if (RTL_R32 (TxConfig) == 0xFFFFFFFF) {
+ printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n",
+ pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+
+ /* identify chip attached to board */
+ version = RTL_R32 (TxConfig) & HW_REVID_MASK;
+ for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++)
+ if (version == rtl_chip_info[i].version) {
+ tp->chipset = i;
+ goto match;
+ }
+
+ /* if unknown chip, assume array element #0, original RTL-8139 in this case */
+ printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n",
+ pci_name(pdev));
+ printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig));
+ tp->chipset = 0;
+
+match:
+ DPRINTK ("chipset id (%d) == index %d, '%s'\n",
+ version, i, rtl_chip_info[i].name);
+
+ if (tp->chipset >= CH_8139B) {
+ u8 new_tmp8 = tmp8 = RTL_R8 (Config1);
+ DPRINTK("PCI PM wakeup\n");
+ if ((rtl_chip_info[tp->chipset].flags & HasLWake) &&
+ (tmp8 & LWAKE))
+ new_tmp8 &= ~LWAKE;
+ new_tmp8 |= Cfg1_PM_Enable;
+ if (new_tmp8 != tmp8) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config1, tmp8);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ }
+ if (rtl_chip_info[tp->chipset].flags & HasLWake) {
+ tmp8 = RTL_R8 (Config4);
+ if (tmp8 & LWPTN) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config4, tmp8 & ~LWPTN);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ }
+ }
+ } else {
+ DPRINTK("Old chip wakeup\n");
+ tmp8 = RTL_R8 (Config1);
+ tmp8 &= ~(SLEEP | PWRDN);
+ RTL_W8 (Config1, tmp8);
+ }
+
+ rtl8139_chip_reset (ioaddr);
+
+ *dev_out = dev;
+ return 0;
+
+err_out:
+ __rtl8139_cleanup_dev (dev);
+ if (disable_dev_on_err)
+ pci_disable_device (pdev);
+ return rc;
+}
+
+
+static int __devinit rtl8139_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev = NULL;
+ struct rtl8139_private *tp;
+ int i, addr_len, option;
+ void __iomem *ioaddr;
+ static int board_idx = -1;
+ u8 pci_rev;
+
+ assert (pdev != NULL);
+ assert (ent != NULL);
+
+ board_idx++;
+
+ /* when we're built into the kernel, the driver version message
+ * is only printed if at least one 8139 board has been found
+ */
+#ifndef MODULE
+ {
+ static int printed_version;
+ if (!printed_version++)
+ printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
+ }
+#endif
+
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev);
+
+ if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
+ pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) {
+ printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n",
+ pci_name(pdev), pdev->vendor, pdev->device, pci_rev);
+ printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n");
+ }
+
+ i = rtl8139_init_board (pdev, &dev);
+ if (i < 0)
+ return i;
+
+ assert (dev != NULL);
+ tp = netdev_priv(dev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (board_idx == ec_device_index) {
+ rtl_ec_net_dev = dev;
+ strcpy(dev->name, "ec0");
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ ioaddr = tp->mmio_addr;
+ assert (ioaddr != NULL);
+
+ addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6;
+ for (i = 0; i < 3; i++)
+ ((u16 *) (dev->dev_addr))[i] =
+ le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len));
+ memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+
+ /* The Rtl8139-specific entries in the device structure. */
+ dev->open = rtl8139_open;
+ dev->hard_start_xmit = rtl8139_start_xmit;
+ dev->poll = rtl8139_poll;
+ dev->weight = 64;
+ dev->stop = rtl8139_close;
+ dev->get_stats = rtl8139_get_stats;
+ dev->set_multicast_list = rtl8139_set_rx_mode;
+ dev->do_ioctl = netdev_ioctl;
+ dev->ethtool_ops = &rtl8139_ethtool_ops;
+ dev->tx_timeout = rtl8139_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = rtl8139_poll_controller;
+#endif
+
+ /* note: the hardware is not capable of sg/csum/highdma, however
+ * through the use of skb_copy_and_csum_dev we enable these
+ * features
+ */
+ dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;
+
+ dev->irq = pdev->irq;
+
+ /* tp zeroed and aligned in alloc_etherdev */
+ tp = netdev_priv(dev);
+
+ /* note: tp->chipset set in rtl8139_init_board */
+ tp->drv_flags = board_info[ent->driver_data].hw_flags;
+ tp->mmio_addr = ioaddr;
+ tp->msg_enable =
+ (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1));
+ spin_lock_init (&tp->lock);
+ spin_lock_init (&tp->rx_lock);
+ INIT_WORK(&tp->thread, rtl8139_thread, dev);
+ tp->mii.dev = dev;
+ tp->mii.mdio_read = mdio_read;
+ tp->mii.mdio_write = mdio_write;
+ tp->mii.phy_id_mask = 0x3f;
+ tp->mii.reg_num_mask = 0x1f;
+
+ /* dev is fully set up and ready to use now */
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ DPRINTK("about to register device named %s (%p)...\n", dev->name, dev);
+ i = register_netdev (dev);
+ if (i) goto err_out;
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ pci_set_drvdata (pdev, dev);
+
+ printk (KERN_INFO "%s: %s at 0x%lx, "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+ "IRQ %d\n",
+ dev->name,
+ board_info[ent->driver_data].name,
+ dev->base_addr,
+ dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3],
+ dev->dev_addr[4], dev->dev_addr[5],
+ dev->irq);
+
+ printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n",
+ dev->name, rtl_chip_info[tp->chipset].name);
+
+ /* Find the connected MII xcvrs.
+ Doing this in open() would allow detecting external xcvrs later, but
+ takes too much time. */
+#ifdef CONFIG_8139TOO_8129
+ if (tp->drv_flags & HAS_MII_XCVR) {
+ int phy, phy_idx = 0;
+ for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) {
+ int mii_status = mdio_read(dev, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ u16 advertising = mdio_read(dev, phy, 4);
+ tp->phys[phy_idx++] = phy;
+ printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x "
+ "advertising %4.4x.\n",
+ dev->name, phy, mii_status, advertising);
+ }
+ }
+ if (phy_idx == 0) {
+ printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM "
+ "transceiver.\n",
+ dev->name);
+ tp->phys[0] = 32;
+ }
+ } else
+#endif
+ tp->phys[0] = 32;
+ tp->mii.phy_id = tp->phys[0];
+
+ /* The lower four bits are the media type. */
+ option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx];
+ if (option > 0) {
+ tp->mii.full_duplex = (option & 0x210) ? 1 : 0;
+ tp->default_port = option & 0xFF;
+ if (tp->default_port)
+ tp->mii.force_media = 1;
+ }
+ if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0)
+ tp->mii.full_duplex = full_duplex[board_idx];
+ if (tp->mii.full_duplex) {
+ printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
+ /* Changing the MII-advertised media because might prevent
+ re-connection. */
+ tp->mii.force_media = 1;
+ }
+ if (tp->default_port) {
+ printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n",
+ (option & 0x20 ? 100 : 10),
+ (option & 0x10 ? "full" : "half"));
+ mdio_write(dev, tp->phys[0], 0,
+ ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */
+ ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+ }
+
+ /* Put the chip into low-power mode. */
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
+
+ return 0;
+
+err_out:
+ __rtl8139_cleanup_dev (dev);
+ pci_disable_device (pdev);
+ return i;
+}
+
+
+static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ assert (dev != NULL);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ unregister_netdev (dev);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ __rtl8139_cleanup_dev (dev);
+ pci_disable_device (pdev);
+}
+
+
+/* Serial EEPROM section. */
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x08 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x00
+#define EE_WRITE_1 0x02
+#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
+#define EE_ENB (0x80 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
+ */
+
+#define eeprom_delay() (void)RTL_R32(Cfg9346)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5)
+#define EE_READ_CMD (6)
+#define EE_ERASE_CMD (7)
+
+static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len)
+{
+ int i;
+ unsigned retval = 0;
+ int read_cmd = location | (EE_READ_CMD << addr_len);
+
+ RTL_W8 (Cfg9346, EE_ENB & ~EE_CS);
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+
+ /* Shift the read command bits out. */
+ for (i = 4 + addr_len; i >= 0; i--) {
+ int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ RTL_W8 (Cfg9346, EE_ENB | dataval);
+ eeprom_delay ();
+ RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK);
+ eeprom_delay ();
+ }
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+
+ for (i = 16; i > 0; i--) {
+ RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK);
+ eeprom_delay ();
+ retval =
+ (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 :
+ 0);
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+ }
+
+ /* Terminate the EEPROM access. */
+ RTL_W8 (Cfg9346, ~EE_CS);
+ eeprom_delay ();
+
+ return retval;
+}
+
+/* MII serial management: mostly bogus for now. */
+/* Read and write the MII management registers using software-generated
+ serial MDIO protocol.
+ The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+ "overclocking" issues. */
+#define MDIO_DIR 0x80
+#define MDIO_DATA_OUT 0x04
+#define MDIO_DATA_IN 0x02
+#define MDIO_CLK 0x01
+#define MDIO_WRITE0 (MDIO_DIR)
+#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
+
+#define mdio_delay() RTL_R8(Config4)
+
+
+static const char mii_2_8139_map[8] = {
+ BasicModeCtrl,
+ BasicModeStatus,
+ 0,
+ 0,
+ NWayAdvert,
+ NWayLPAR,
+ NWayExpansion,
+ 0
+};
+
+
+#ifdef CONFIG_8139TOO_8129
+/* Syncronize the MII management interface by shifting 32 one bits out. */
+static void mdio_sync (void __iomem *ioaddr)
+{
+ int i;
+
+ for (i = 32; i >= 0; i--) {
+ RTL_W8 (Config4, MDIO_WRITE1);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK);
+ mdio_delay ();
+ }
+}
+#endif
+
+static int mdio_read (struct net_device *dev, int phy_id, int location)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int retval = 0;
+#ifdef CONFIG_8139TOO_8129
+ void __iomem *ioaddr = tp->mmio_addr;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i;
+#endif
+
+ if (phy_id > 31) { /* Really a 8139. Use internal registers. */
+ void __iomem *ioaddr = tp->mmio_addr;
+ return location < 8 && mii_2_8139_map[location] ?
+ RTL_R16 (mii_2_8139_map[location]) : 0;
+ }
+
+#ifdef CONFIG_8139TOO_8129
+ mdio_sync (ioaddr);
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
+
+ RTL_W8 (Config4, MDIO_DIR | dataval);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK);
+ mdio_delay ();
+ }
+
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ RTL_W8 (Config4, 0);
+ mdio_delay ();
+ retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0);
+ RTL_W8 (Config4, MDIO_CLK);
+ mdio_delay ();
+ }
+#endif
+
+ return (retval >> 1) & 0xffff;
+}
+
+
+static void mdio_write (struct net_device *dev, int phy_id, int location,
+ int value)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+#ifdef CONFIG_8139TOO_8129
+ void __iomem *ioaddr = tp->mmio_addr;
+ int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
+ int i;
+#endif
+
+ if (phy_id > 31) { /* Really a 8139. Use internal registers. */
+ void __iomem *ioaddr = tp->mmio_addr;
+ if (location == 0) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W16 (BasicModeCtrl, value);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ } else if (location < 8 && mii_2_8139_map[location])
+ RTL_W16 (mii_2_8139_map[location], value);
+ return;
+ }
+
+#ifdef CONFIG_8139TOO_8129
+ mdio_sync (ioaddr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval =
+ (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+ RTL_W8 (Config4, dataval);
+ mdio_delay ();
+ RTL_W8 (Config4, dataval | MDIO_CLK);
+ mdio_delay ();
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ RTL_W8 (Config4, 0);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_CLK);
+ mdio_delay ();
+ }
+#endif
+}
+
+
+static int rtl8139_open (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int retval;
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ retval = request_irq(dev->irq, rtl8139_interrupt,
+ SA_SHIRQ, dev->name, dev);
+ if (retval)
+ return retval;
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ &tp->tx_bufs_dma);
+ tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ &tp->rx_ring_dma);
+ if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ free_irq(dev->irq, dev);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ if (tp->tx_bufs)
+ pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ tp->tx_bufs, tp->tx_bufs_dma);
+ if (tp->rx_ring)
+ pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ tp->rx_ring, tp->rx_ring_dma);
+
+ return -ENOMEM;
+
+ }
+
+ tp->mii.full_duplex = tp->mii.force_media;
+ tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
+
+ rtl8139_init_ring (dev);
+ rtl8139_hw_start (dev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ netif_start_queue (dev);
+
+ if (netif_msg_ifup(tp))
+ printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d"
+ " GP Pins %2.2x %s-duplex.\n",
+ dev->name, pci_resource_start (tp->pci_dev, 1),
+ dev->irq, RTL_R8 (MediaStatus),
+ tp->mii.full_duplex ? "full" : "half");
+
+ rtl8139_start_thread(tp);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ return 0;
+}
+
+
+static void rtl_check_media (struct net_device *dev, unsigned int init_media)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ if (tp->phys[0] >= 0) {
+ mii_check_media(&tp->mii, netif_msg_link(tp), init_media);
+ }
+ } else {
+ void __iomem *ioaddr = tp->mmio_addr;
+ uint16_t state = RTL_R16(BasicModeStatus) & BMSR_LSTATUS;
+ ecdev_link_state(rtl_ec_dev, state ? 1 : 0);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+/* Start the hardware at open or resume. */
+static void rtl8139_hw_start (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u32 i;
+ u8 tmp;
+
+ /* Bring old chips out of low-power mode. */
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'R');
+
+ rtl8139_chip_reset (ioaddr);
+
+ /* unlock Config[01234] and BMCR register writes */
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+ /* Restore our idea of the MAC address. */
+ RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
+ RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
+
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
+ RTL_W32 (RxConfig, tp->rx_config);
+ RTL_W32 (TxConfig, rtl8139_tx_config);
+
+ tp->cur_rx = 0;
+
+ rtl_check_media (dev, 1);
+
+ if (tp->chipset >= CH_8139B) {
+ /* Disable magic packet scanning, which is enabled
+ * when PM is enabled in Config1. It can be reenabled
+ * via ETHTOOL_SWOL if desired. */
+ RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic);
+ }
+
+ DPRINTK("init buffer addresses\n");
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+ /* init Tx buffer DMA addresses */
+ for (i = 0; i < NUM_TX_DESC; i++)
+ RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
+
+ RTL_W32 (RxMissed, 0);
+
+ rtl8139_set_rx_mode (dev);
+
+ /* no early-rx interrupts */
+ RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear);
+
+ /* make sure RxTx has started */
+ tmp = RTL_R8 (ChipCmd);
+ if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb)))
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ /* Enable all known interrupts by setting the interrupt mask. */
+ RTL_W16 (IntrMask, rtl8139_intr_mask);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void rtl8139_init_ring (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int i;
+
+ tp->cur_rx = 0;
+ tp->cur_tx = 0;
+ tp->dirty_tx = 0;
+
+ for (i = 0; i < NUM_TX_DESC; i++)
+ tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE];
+}
+
+
+/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */
+static int next_tick = 3 * HZ;
+
+#ifndef CONFIG_8139TOO_TUNE_TWISTER
+static inline void rtl8139_tune_twister (struct net_device *dev,
+ struct rtl8139_private *tp) {}
+#else
+enum TwisterParamVals {
+ PARA78_default = 0x78fa8388,
+ PARA7c_default = 0xcb38de43, /* param[0][3] */
+ PARA7c_xxx = 0xcb38de43,
+};
+
+static const unsigned long param[4][4] = {
+ {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
+};
+
+static void rtl8139_tune_twister (struct net_device *dev,
+ struct rtl8139_private *tp)
+{
+ int linkcase;
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ /* This is a complicated state machine to configure the "twister" for
+ impedance/echos based on the cable length.
+ All of this is magic and undocumented.
+ */
+ switch (tp->twistie) {
+ case 1:
+ if (RTL_R16 (CSCR) & CSCR_LinkOKBit) {
+ /* We have link beat, let us tune the twister. */
+ RTL_W16 (CSCR, CSCR_LinkDownOffCmd);
+ tp->twistie = 2; /* Change to state 2. */
+ next_tick = HZ / 10;
+ } else {
+ /* Just put in some reasonable defaults for when beat returns. */
+ RTL_W16 (CSCR, CSCR_LinkDownCmd);
+ RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */
+ RTL_W32 (PARA78, PARA78_default);
+ RTL_W32 (PARA7c, PARA7c_default);
+ tp->twistie = 0; /* Bail from future actions. */
+ }
+ break;
+ case 2:
+ /* Read how long it took to hear the echo. */
+ linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits;
+ if (linkcase == 0x7000)
+ tp->twist_row = 3;
+ else if (linkcase == 0x3000)
+ tp->twist_row = 2;
+ else if (linkcase == 0x1000)
+ tp->twist_row = 1;
+ else
+ tp->twist_row = 0;
+ tp->twist_col = 0;
+ tp->twistie = 3; /* Change to state 2. */
+ next_tick = HZ / 10;
+ break;
+ case 3:
+ /* Put out four tuning parameters, one per 100msec. */
+ if (tp->twist_col == 0)
+ RTL_W16 (FIFOTMS, 0);
+ RTL_W32 (PARA7c, param[(int) tp->twist_row]
+ [(int) tp->twist_col]);
+ next_tick = HZ / 10;
+ if (++tp->twist_col >= 4) {
+ /* For short cables we are done.
+ For long cables (row == 3) check for mistune. */
+ tp->twistie =
+ (tp->twist_row == 3) ? 4 : 0;
+ }
+ break;
+ case 4:
+ /* Special case for long cables: check for mistune. */
+ if ((RTL_R16 (CSCR) &
+ CSCR_LinkStatusBits) == 0x7000) {
+ tp->twistie = 0;
+ break;
+ } else {
+ RTL_W32 (PARA7c, 0xfb38de03);
+ tp->twistie = 5;
+ next_tick = HZ / 10;
+ }
+ break;
+ case 5:
+ /* Retune for shorter cable (column 2). */
+ RTL_W32 (FIFOTMS, 0x20);
+ RTL_W32 (PARA78, PARA78_default);
+ RTL_W32 (PARA7c, PARA7c_default);
+ RTL_W32 (FIFOTMS, 0x00);
+ tp->twist_row = 2;
+ tp->twist_col = 0;
+ tp->twistie = 3;
+ next_tick = HZ / 10;
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+}
+#endif /* CONFIG_8139TOO_TUNE_TWISTER */
+
+static inline void rtl8139_thread_iter (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr)
+{
+ int mii_lpa;
+
+ mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA);
+
+ if (!tp->mii.force_media && mii_lpa != 0xffff) {
+ int duplex = (mii_lpa & LPA_100FULL)
+ || (mii_lpa & 0x01C0) == 0x0040;
+ if (tp->mii.full_duplex != duplex) {
+ tp->mii.full_duplex = duplex;
+
+ if (mii_lpa) {
+ printk (KERN_INFO
+ "%s: Setting %s-duplex based on MII #%d link"
+ " partner ability of %4.4x.\n",
+ dev->name,
+ tp->mii.full_duplex ? "full" : "half",
+ tp->phys[0], mii_lpa);
+ } else {
+ printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n",
+ dev->name);
+ }
+#if 0
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+#endif
+ }
+ }
+
+ next_tick = HZ * 60;
+
+ rtl8139_tune_twister (dev, tp);
+
+ DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n",
+ dev->name, RTL_R16 (NWayLPAR));
+ DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n",
+ dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus));
+ DPRINTK ("%s: Chip config %2.2x %2.2x.\n",
+ dev->name, RTL_R8 (Config0),
+ RTL_R8 (Config1));
+}
+
+static void rtl8139_thread (void *_data)
+{
+ struct net_device *dev = _data;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ unsigned long thr_delay = next_tick;
+
+ if (tp->watchdog_fired) {
+ tp->watchdog_fired = 0;
+ rtl8139_tx_timeout_task(_data);
+ } else if (rtnl_trylock()) {
+ rtl8139_thread_iter (dev, tp, tp->mmio_addr);
+ rtnl_unlock ();
+ } else {
+ /* unlikely race. mitigate with fast poll. */
+ thr_delay = HZ / 2;
+ }
+
+ schedule_delayed_work(&tp->thread, thr_delay);
+}
+
+static void rtl8139_start_thread(struct rtl8139_private *tp)
+{
+ tp->twistie = 0;
+ if (tp->chipset == CH_8139_K)
+ tp->twistie = 1;
+ else if (tp->drv_flags & HAS_LNK_CHNG)
+ return;
+
+ tp->have_thread = 1;
+
+ schedule_delayed_work(&tp->thread, next_tick);
+}
+
+static void rtl8139_stop_thread(struct rtl8139_private *tp)
+{
+ if (tp->have_thread) {
+ cancel_rearming_delayed_work(&tp->thread);
+ tp->have_thread = 0;
+ } else
+ flush_scheduled_work();
+}
+
+static inline void rtl8139_tx_clear (struct rtl8139_private *tp)
+{
+ tp->cur_tx = 0;
+ tp->dirty_tx = 0;
+
+ /* XXX account for unsent Tx packets in tp->stats.tx_dropped */
+}
+
+static void rtl8139_tx_timeout_task (void *_data)
+{
+ struct net_device *dev = _data;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int i;
+ u8 tmp8;
+
+ printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x "
+ "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd),
+ RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus));
+ /* Emit info to figure out what went wrong. */
+ printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n",
+ dev->name, tp->cur_tx, tp->dirty_tx);
+ for (i = 0; i < NUM_TX_DESC; i++)
+ printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n",
+ dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
+ i == tp->dirty_tx % NUM_TX_DESC ?
+ " (queue head)" : "");
+
+ tp->xstats.tx_timeouts++;
+
+ /* disable Tx ASAP, if not already */
+ tmp8 = RTL_R8 (ChipCmd);
+ if (tmp8 & CmdTxEnb)
+ RTL_W8 (ChipCmd, CmdRxEnb);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ spin_lock_bh(&tp->rx_lock);
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0x0000);
+
+ /* Stop a shared interrupt from scavenging while we are. */
+ spin_lock_irq(&tp->lock);
+ rtl8139_tx_clear (tp);
+ spin_unlock_irq(&tp->lock);
+
+ /* ...and finally, reset everything */
+ if (netif_running(dev)) {
+ rtl8139_hw_start (dev);
+ netif_wake_queue (dev);
+ }
+ spin_unlock_bh(&tp->rx_lock);
+ } else {
+ rtl8139_tx_clear (tp);
+ rtl8139_hw_start (dev);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+static void rtl8139_tx_timeout (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev && !tp->have_thread) {
+ INIT_WORK(&tp->thread, rtl8139_tx_timeout_task, dev);
+ schedule_delayed_work(&tp->thread, next_tick);
+ } else
+ tp->watchdog_fired = 1;
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned int entry;
+ unsigned int len = skb->len;
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = tp->cur_tx % NUM_TX_DESC;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ /* Note: the chip doesn't have auto-pad! */
+ if (likely(len < TX_BUF_SIZE)) {
+ if (len < ETH_ZLEN)
+ memset(tp->tx_buf[entry], 0, ETH_ZLEN);
+ skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
+ if (dev != rtl_ec_net_dev) {
+ dev_kfree_skb(skb);
+ }
+ } else {
+ if (dev != rtl_ec_net_dev) {
+ dev_kfree_skb(skb);
+ }
+ tp->stats.tx_dropped++;
+ return 0;
+ }
+
+ if (dev != rtl_ec_net_dev) {
+ spin_lock_irq(&tp->lock);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
+ tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
+
+ dev->trans_start = jiffies;
+
+ tp->cur_tx++;
+ wmb();
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
+ netif_stop_queue (dev);
+ spin_unlock_irq(&tp->lock);
+
+ if (netif_msg_tx_queued(tp))
+ printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n",
+ dev->name, len, entry);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ return 0;
+}
+
+
+static void rtl8139_tx_interrupt (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr)
+{
+ unsigned long dirty_tx, tx_left;
+
+ assert (dev != NULL);
+ assert (ioaddr != NULL);
+
+ dirty_tx = tp->dirty_tx;
+ tx_left = tp->cur_tx - dirty_tx;
+ while (tx_left > 0) {
+ int entry = dirty_tx % NUM_TX_DESC;
+ int txstatus;
+
+ txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32)));
+
+ if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
+ break; /* It still hasn't been Txed */
+
+ /* Note: TxCarrierLost is always asserted at 100mbps. */
+ if (txstatus & (TxOutOfWindow | TxAborted)) {
+ /* There was an major error, log it. */
+ if (netif_msg_tx_err(tp))
+ printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+ dev->name, txstatus);
+ tp->stats.tx_errors++;
+ if (txstatus & TxAborted) {
+ tp->stats.tx_aborted_errors++;
+ RTL_W32 (TxConfig, TxClearAbt);
+ RTL_W16 (IntrStatus, TxErr);
+ wmb();
+ }
+ if (txstatus & TxCarrierLost)
+ tp->stats.tx_carrier_errors++;
+ if (txstatus & TxOutOfWindow)
+ tp->stats.tx_window_errors++;
+ } else {
+ if (txstatus & TxUnderrun) {
+ /* Add 64 to the Tx FIFO threshold. */
+ if (tp->tx_flag < 0x00300000)
+ tp->tx_flag += 0x00020000;
+ tp->stats.tx_fifo_errors++;
+ }
+ tp->stats.collisions += (txstatus >> 24) & 15;
+ tp->stats.tx_bytes += txstatus & 0x7ff;
+ tp->stats.tx_packets++;
+ }
+
+ dirty_tx++;
+ tx_left--;
+ }
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+#ifndef RTL8139_NDEBUG
+ if (dev != rtl_ec_net_dev && tp->cur_tx - dirty_tx > NUM_TX_DESC) {
+ printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n",
+ dev->name, dirty_tx, tp->cur_tx);
+ dirty_tx += NUM_TX_DESC;
+ }
+#endif /* RTL8139_NDEBUG */
+
+ /* only wake the queue if we did work, and the queue is stopped */
+ if (tp->dirty_tx != dirty_tx) {
+ tp->dirty_tx = dirty_tx;
+ mb();
+
+ if (dev != rtl_ec_net_dev) {
+ netif_wake_queue (dev);
+ }
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+
+/* TODO: clean this up! Rx reset need not be this intensive */
+static void rtl8139_rx_err (u32 rx_status, struct net_device *dev,
+ struct rtl8139_private *tp, void __iomem *ioaddr)
+{
+ u8 tmp8;
+#ifdef CONFIG_8139_OLD_RX_RESET
+ int tmp_work;
+#endif
+
+ if (netif_msg_rx_err (tp))
+ printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n",
+ dev->name, rx_status);
+ tp->stats.rx_errors++;
+ if (!(rx_status & RxStatusOK)) {
+ if (rx_status & RxTooLong) {
+ DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
+ dev->name, rx_status);
+ /* A.C.: The chip hangs here. */
+ }
+ if (rx_status & (RxBadSymbol | RxBadAlign))
+ tp->stats.rx_frame_errors++;
+ if (rx_status & (RxRunt | RxTooLong))
+ tp->stats.rx_length_errors++;
+ if (rx_status & RxCRCErr)
+ tp->stats.rx_crc_errors++;
+ } else {
+ tp->xstats.rx_lost_in_ring++;
+ }
+
+#ifndef CONFIG_8139_OLD_RX_RESET
+ tmp8 = RTL_R8 (ChipCmd);
+ RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb);
+ RTL_W8 (ChipCmd, tmp8);
+ RTL_W32 (RxConfig, tp->rx_config);
+ tp->cur_rx = 0;
+#else
+ /* Reset the receiver, based on RealTek recommendation. (Bug?) */
+
+ /* disable receive */
+ RTL_W8_F (ChipCmd, CmdTxEnb);
+ tmp_work = 200;
+ while (--tmp_work > 0) {
+ udelay(1);
+ tmp8 = RTL_R8 (ChipCmd);
+ if (!(tmp8 & CmdRxEnb))
+ break;
+ }
+ if (tmp_work <= 0)
+ printk (KERN_WARNING PFX "rx stop wait too long\n");
+ /* restart receive */
+ tmp_work = 200;
+ while (--tmp_work > 0) {
+ RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb);
+ udelay(1);
+ tmp8 = RTL_R8 (ChipCmd);
+ if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
+ break;
+ }
+ if (tmp_work <= 0)
+ printk (KERN_WARNING PFX "tx/rx enable wait too long\n");
+
+ /* and reinitialize all rx related registers */
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
+ RTL_W32 (RxConfig, tp->rx_config);
+ tp->cur_rx = 0;
+
+ DPRINTK("init buffer addresses\n");
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+ /* A.C.: Reset the multicast list. */
+ __set_rx_mode (dev);
+#endif
+}
+
+#if RX_BUF_IDX == 3
+static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring,
+ u32 offset, unsigned int size)
+{
+ u32 left = RX_BUF_LEN - offset;
+
+ if (size > left) {
+ memcpy(skb->data, ring + offset, left);
+ memcpy(skb->data+left, ring, size - left);
+ } else
+ memcpy(skb->data, ring + offset, size);
+}
+#endif
+
+static void rtl8139_isr_ack(struct rtl8139_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 status;
+
+ status = RTL_R16 (IntrStatus) & RxAckBits;
+
+ /* Clear out errors and receive interrupts */
+ if (likely(status != 0)) {
+ if (unlikely(status & (RxFIFOOver | RxOverflow))) {
+ tp->stats.rx_errors++;
+ if (status & RxFIFOOver)
+ tp->stats.rx_fifo_errors++;
+ }
+ RTL_W16_F (IntrStatus, RxAckBits);
+ }
+}
+
+static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
+ int budget)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ int received = 0;
+ unsigned char *rx_ring = tp->rx_ring;
+ unsigned int cur_rx = tp->cur_rx;
+ unsigned int rx_size = 0;
+
+ DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+ " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx,
+ RTL_R16 (RxBufAddr),
+ RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ while ((dev == rtl_ec_net_dev || netif_running(dev))
+ && received < budget
+ && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ u32 ring_offset = cur_rx % RX_BUF_LEN;
+ u32 rx_status;
+ unsigned int pkt_size;
+ struct sk_buff *skb;
+
+ rmb();
+
+ /* read size+status of next frame from DMA ring buffer */
+ rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
+ rx_size = rx_status >> 16;
+ pkt_size = rx_size - 4;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ if (netif_msg_rx_status(tp))
+ printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x,"
+ " cur %4.4x.\n", dev->name, rx_status,
+ rx_size, cur_rx);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+#if RTL8139_DEBUG > 2
+ {
+ int i;
+ DPRINTK ("%s: Frame contents ", dev->name);
+ for (i = 0; i < 70; i++)
+ printk (" %2.2x",
+ rx_ring[ring_offset + i]);
+ printk (".\n");
+ }
+#endif
+
+ /* Packet copy from FIFO still in progress.
+ * Theoretically, this should never happen
+ * since EarlyRx is disabled.
+ */
+ if (unlikely(rx_size == 0xfff0)) {
+ if (!tp->fifo_copy_timeout)
+ tp->fifo_copy_timeout = jiffies + 2;
+ else if (time_after(jiffies, tp->fifo_copy_timeout)) {
+ DPRINTK ("%s: hung FIFO. Reset.", dev->name);
+ rx_size = 0;
+ goto no_early_rx;
+ }
+ if (netif_msg_intr(tp)) {
+ printk(KERN_DEBUG "%s: fifo copy in progress.",
+ dev->name);
+ }
+ tp->xstats.early_rx++;
+ break;
+ }
+
+no_early_rx:
+ tp->fifo_copy_timeout = 0;
+
+ /* If Rx err or invalid rx_size/rx_status received
+ * (which happens if we get lost in the ring),
+ * Rx process gets reset, so we abort any further
+ * Rx processing.
+ */
+ if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
+ (rx_size < 8) ||
+ (!(rx_status & RxStatusOK)))) {
+ rtl8139_rx_err (rx_status, dev, tp, ioaddr);
+ received = -1;
+ goto out;
+ }
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ /* Malloc up new buffer, compatible with net-2e. */
+ /* Omit the four octet CRC from the length. */
+
+ skb = dev_alloc_skb (pkt_size + 2);
+ if (likely(skb)) {
+ skb->dev = dev;
+ skb_reserve (skb, 2); /* 16 byte align the IP fields. */
+#if RX_BUF_IDX == 3
+ wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
+#else
+ eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
+#endif
+ skb_put (skb, pkt_size);
+
+ skb->protocol = eth_type_trans (skb, dev);
+
+ dev->last_rx = jiffies;
+ tp->stats.rx_bytes += pkt_size;
+ tp->stats.rx_packets++;
+
+ netif_receive_skb (skb);
+ } else {
+ if (net_ratelimit())
+ printk(KERN_WARNING
+ "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ tp->stats.rx_dropped++;
+ }
+ } else {
+ ecdev_receive(rtl_ec_dev,
+ &rx_ring[ring_offset + 4], pkt_size);
+ dev->last_rx = jiffies;
+ tp->stats.rx_bytes += pkt_size;
+ tp->stats.rx_packets++;
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ received++;
+
+ cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
+ RTL_W16 (RxBufPtr, (u16) (cur_rx - 16));
+
+ rtl8139_isr_ack(tp);
+ }
+
+ if (unlikely(!received || rx_size == 0xfff0))
+ rtl8139_isr_ack(tp);
+
+#if RTL8139_DEBUG > 1
+ DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+ " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
+ RTL_R16 (RxBufAddr),
+ RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+#endif
+
+ tp->cur_rx = cur_rx;
+
+ /*
+ * The receive buffer should be mostly empty.
+ * Tell NAPI to reenable the Rx irq.
+ */
+ if (tp->fifo_copy_timeout)
+ received = budget;
+
+out:
+ return received;
+}
+
+
+static void rtl8139_weird_interrupt (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr,
+ int status, int link_changed)
+{
+ DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",
+ dev->name, status);
+
+ assert (dev != NULL);
+ assert (tp != NULL);
+ assert (ioaddr != NULL);
+
+ /* Update the error count. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ if ((status & RxUnderrun) && link_changed &&
+ (tp->drv_flags & HAS_LNK_CHNG)) {
+ rtl_check_media(dev, 0);
+ status &= ~RxUnderrun;
+ }
+
+ if (status & (RxUnderrun | RxErr))
+ tp->stats.rx_errors++;
+
+ if (status & PCSTimeout)
+ tp->stats.rx_length_errors++;
+ if (status & RxUnderrun)
+ tp->stats.rx_fifo_errors++;
+ if (status & PCIErr) {
+ u16 pci_cmd_status;
+ pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status);
+ pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status);
+
+ printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",
+ dev->name, pci_cmd_status);
+ }
+}
+
+static int rtl8139_poll(struct net_device *dev, int *budget)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int orig_budget = min(*budget, dev->quota);
+ int done = 1;
+
+ spin_lock(&tp->rx_lock);
+ if (likely(RTL_R16(IntrStatus) & RxAckBits)) {
+ int work_done;
+
+ work_done = rtl8139_rx(dev, tp, orig_budget);
+ if (likely(work_done > 0)) {
+ *budget -= work_done;
+ dev->quota -= work_done;
+ done = (work_done < orig_budget);
+ }
+ }
+
+ if (done) {
+ /*
+ * Order is important since data can get interrupted
+ * again when we think we are done.
+ */
+ local_irq_disable();
+ RTL_W16_F(IntrMask, rtl8139_intr_mask);
+ __netif_rx_complete(dev);
+ local_irq_enable();
+ }
+ spin_unlock(&tp->rx_lock);
+
+ return !done;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_instance;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 status, ackstat;
+ int link_changed = 0; /* avoid bogus "uninit" warning */
+ int handled = 0;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ spin_lock (&tp->lock);
+ status = RTL_R16 (IntrStatus);
+
+ /* shared irq? */
+ if (unlikely((status & rtl8139_intr_mask) == 0))
+ goto out;
+ } else {
+ status = RTL_R16 (IntrStatus);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ handled = 1;
+
+ /* h/w no longer present (hotplug?) or major error, bail */
+ if (unlikely(status == 0xFFFF))
+ goto out;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ /* close possible race's with dev_close */
+ if (unlikely(!netif_running(dev))) {
+ RTL_W16 (IntrMask, 0);
+ goto out;
+ }
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ /* Acknowledge all of the current interrupt sources ASAP, but
+ an first get an additional status bit from CSCR. */
+ if (unlikely(status & RxUnderrun))
+ link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
+
+ ackstat = status & ~(RxAckBits | TxErr);
+ if (ackstat)
+ RTL_W16 (IntrStatus, ackstat);
+
+ /* Receive packets are processed by poll routine.
+ If not running start it now. */
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (status & RxAckBits){
+ if (dev != rtl_ec_net_dev) {
+ /* Mark for polling */
+ if (netif_rx_schedule_prep(dev)) {
+ RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
+ __netif_rx_schedule (dev);
+ }
+ } else {
+ /* EtherCAT device: Just receive all frames */
+ rtl8139_rx(dev, tp, 100); // FIXME
+ }
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ /* Check uncommon events with one test. */
+ if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr)))
+ rtl8139_weird_interrupt (dev, tp, ioaddr,
+ status, link_changed);
+
+ if (status & (TxOK | TxErr)) {
+ rtl8139_tx_interrupt (dev, tp, ioaddr);
+ if (status & TxErr)
+ RTL_W16 (IntrStatus, TxErr);
+ }
+ out:
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ spin_unlock (&tp->lock);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
+ dev->name, RTL_R16 (IntrStatus));
+ return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void rtl8139_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ rtl8139_interrupt(dev->irq, dev, NULL);
+ enable_irq(dev->irq);
+}
+#endif
+
+static int rtl8139_close (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev != rtl_ec_net_dev) {
+ netif_stop_queue (dev);
+
+ rtl8139_stop_thread(tp);
+
+ if (netif_msg_ifdown(tp))
+ printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n",
+ dev->name, RTL_R16 (IntrStatus));
+
+ spin_lock_irqsave (&tp->lock, flags);
+
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8 (ChipCmd, 0);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ synchronize_irq (dev->irq); /* racy, but that's ok here */
+ free_irq (dev->irq, dev);
+ } else {
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8 (ChipCmd, 0);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ rtl8139_tx_clear (tp);
+
+ pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ tp->rx_ring, tp->rx_ring_dma);
+ pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ tp->tx_bufs, tp->tx_bufs_dma);
+ tp->rx_ring = NULL;
+ tp->tx_bufs = NULL;
+
+ /* Green! Put the chip in low-power mode. */
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
+
+ return 0;
+}
+
+
+/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to
+ kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and
+ other threads or interrupts aren't messing with the 8139. */
+static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->mmio_addr;
+
+ spin_lock_irq(&np->lock);
+ if (rtl_chip_info[np->chipset].flags & HasLWake) {
+ u8 cfg3 = RTL_R8 (Config3);
+ u8 cfg5 = RTL_R8 (Config5);
+
+ wol->supported = WAKE_PHY | WAKE_MAGIC
+ | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST;
+
+ wol->wolopts = 0;
+ if (cfg3 & Cfg3_LinkUp)
+ wol->wolopts |= WAKE_PHY;
+ if (cfg3 & Cfg3_Magic)
+ wol->wolopts |= WAKE_MAGIC;
+ /* (KON)FIXME: See how netdev_set_wol() handles the
+ following constants. */
+ if (cfg5 & Cfg5_UWF)
+ wol->wolopts |= WAKE_UCAST;
+ if (cfg5 & Cfg5_MWF)
+ wol->wolopts |= WAKE_MCAST;
+ if (cfg5 & Cfg5_BWF)
+ wol->wolopts |= WAKE_BCAST;
+ }
+ spin_unlock_irq(&np->lock);
+}
+
+
+/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes
+ that wol points to kernel memory and other threads or interrupts
+ aren't messing with the 8139. */
+static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->mmio_addr;
+ u32 support;
+ u8 cfg3, cfg5;
+
+ support = ((rtl_chip_info[np->chipset].flags & HasLWake)
+ ? (WAKE_PHY | WAKE_MAGIC
+ | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)
+ : 0);
+ if (wol->wolopts & ~support)
+ return -EINVAL;
+
+ spin_lock_irq(&np->lock);
+ cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic);
+ if (wol->wolopts & WAKE_PHY)
+ cfg3 |= Cfg3_LinkUp;
+ if (wol->wolopts & WAKE_MAGIC)
+ cfg3 |= Cfg3_Magic;
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config3, cfg3);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF);
+ /* (KON)FIXME: These are untested. We may have to set the
+ CRC0, Wakeup0 and LSBCRC0 registers too, but I have no
+ documentation. */
+ if (wol->wolopts & WAKE_UCAST)
+ cfg5 |= Cfg5_UWF;
+ if (wol->wolopts & WAKE_MCAST)
+ cfg5 |= Cfg5_MWF;
+ if (wol->wolopts & WAKE_BCAST)
+ cfg5 |= Cfg5_BWF;
+ RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */
+ spin_unlock_irq(&np->lock);
+
+ return 0;
+}
+
+static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, pci_name(np->pci_dev));
+ info->regdump_len = np->regs_len;
+}
+
+static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ spin_lock_irq(&np->lock);
+ mii_ethtool_gset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+ return 0;
+}
+
+static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ int rc;
+ spin_lock_irq(&np->lock);
+ rc = mii_ethtool_sset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+ return rc;
+}
+
+static int rtl8139_nway_reset(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return mii_nway_restart(&np->mii);
+}
+
+static u32 rtl8139_get_link(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return mii_link_ok(&np->mii);
+}
+
+static u32 rtl8139_get_msglevel(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return np->msg_enable;
+}
+
+static void rtl8139_set_msglevel(struct net_device *dev, u32 datum)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ np->msg_enable = datum;
+}
+
+/* TODO: we are too slack to do reg dumping for pio, for now */
+#ifdef CONFIG_8139TOO_PIO
+#define rtl8139_get_regs_len NULL
+#define rtl8139_get_regs NULL
+#else
+static int rtl8139_get_regs_len(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return np->regs_len;
+}
+
+static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+
+ regs->version = RTL_REGS_VER;
+
+ spin_lock_irq(&np->lock);
+ memcpy_fromio(regbuf, np->mmio_addr, regs->len);
+ spin_unlock_irq(&np->lock);
+}
+#endif /* CONFIG_8139TOO_MMIO */
+
+static int rtl8139_get_stats_count(struct net_device *dev)
+{
+ return RTL_NUM_STATS;
+}
+
+static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+
+ data[0] = np->xstats.early_rx;
+ data[1] = np->xstats.tx_buf_mapped;
+ data[2] = np->xstats.tx_timeouts;
+ data[3] = np->xstats.rx_lost_in_ring;
+}
+
+static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys));
+}
+
+static struct ethtool_ops rtl8139_ethtool_ops = {
+ .get_drvinfo = rtl8139_get_drvinfo,
+ .get_settings = rtl8139_get_settings,
+ .set_settings = rtl8139_set_settings,
+ .get_regs_len = rtl8139_get_regs_len,
+ .get_regs = rtl8139_get_regs,
+ .nway_reset = rtl8139_nway_reset,
+ .get_link = rtl8139_get_link,
+ .get_msglevel = rtl8139_get_msglevel,
+ .set_msglevel = rtl8139_set_msglevel,
+ .get_wol = rtl8139_get_wol,
+ .set_wol = rtl8139_set_wol,
+ .get_strings = rtl8139_get_strings,
+ .get_stats_count = rtl8139_get_stats_count,
+ .get_ethtool_stats = rtl8139_get_ethtool_stats,
+ .get_perm_addr = ethtool_op_get_perm_addr,
+};
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ int rc;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev == rtl_ec_net_dev || !netif_running(dev))
+ return -EINVAL;
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ spin_lock_irq(&np->lock);
+ rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
+ spin_unlock_irq(&np->lock);
+
+ return rc;
+}
+
+
+static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev == rtl_ec_net_dev || netif_running(dev)) {
+ spin_lock_irqsave (&tp->lock, flags);
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+ spin_unlock_irqrestore (&tp->lock, flags);
+ }
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ return &tp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ This routine is not state sensitive and need not be SMP locked. */
+
+static void __set_rx_mode (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u32 mc_filter[2]; /* Multicast hash filter */
+ int i, rx_mode;
+ u32 tmp;
+
+ DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n",
+ dev->name, dev->flags, RTL_R32 (RxConfig));
+
+ /* Note: do not reorder, GCC is clever about common statements. */
+ if (dev->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n",
+ dev->name);
+ rx_mode =
+ AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+ AcceptAllPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else if ((dev->mc_count > multicast_filter_limit)
+ || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else {
+ struct dev_mc_list *mclist;
+ rx_mode = AcceptBroadcast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0;
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+
+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+ rx_mode |= AcceptMulticast;
+ }
+ }
+
+ /* We can safely update without stopping the chip. */
+ tmp = rtl8139_rx_config | rx_mode;
+ if (tp->rx_config != tmp) {
+ RTL_W32_F (RxConfig, tmp);
+ tp->rx_config = tmp;
+ }
+ RTL_W32_F (MAR0 + 0, mc_filter[0]);
+ RTL_W32_F (MAR0 + 4, mc_filter[1]);
+}
+
+static void rtl8139_set_rx_mode (struct net_device *dev)
+{
+ unsigned long flags;
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ spin_lock_irqsave (&tp->lock, flags);
+ __set_rx_mode(dev);
+ spin_unlock_irqrestore (&tp->lock, flags);
+}
+
+#ifdef CONFIG_PM
+
+static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ pci_save_state (pdev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev == rtl_ec_net_dev || !netif_running (dev))
+ return 0;
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ netif_device_detach (dev);
+
+ spin_lock_irqsave (&tp->lock, flags);
+
+ /* Disable interrupts, stop Tx and Rx. */
+ RTL_W16 (IntrMask, 0);
+ RTL_W8 (ChipCmd, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ pci_set_power_state (pdev, PCI_D3hot);
+
+ return 0;
+}
+
+
+static int rtl8139_resume (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ pci_restore_state (pdev);
+
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ if (dev == rtl_ec_net_dev || !netif_running (dev))
+ return 0;
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+
+ pci_set_power_state (pdev, PCI_D0);
+ rtl8139_init_ring (dev);
+ rtl8139_hw_start (dev);
+ netif_device_attach (dev);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver rtl8139_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = rtl8139_pci_tbl,
+ .probe = rtl8139_init_one,
+ .remove = __devexit_p(rtl8139_remove_one),
+#ifdef CONFIG_PM
+ .suspend = rtl8139_suspend,
+ .resume = rtl8139_resume,
+#endif /* CONFIG_PM */
+};
+
+
+static int __init rtl8139_init_module (void)
+{
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ printk(KERN_INFO RTL8139_DRIVER_NAME "\n");
+ printk(KERN_INFO "ec_device_index is %i\n", ec_device_index);
+
+ if (pci_module_init(&rtl8139_pci_driver) < 0) {
+ printk(KERN_ERR "Failed to init PCI module.\n");
+ goto out_return;
+ }
+
+ if (rtl_ec_net_dev) {
+ printk(KERN_INFO "Registering EtherCAT device...\n");
+ if (!(rtl_ec_dev = ecdev_register(ec_device_master_index,
+ rtl_ec_net_dev, rtl8139_interrupt, THIS_MODULE))) {
+ printk(KERN_ERR "Failed to register EtherCAT device!\n");
+ goto out_pci;
+ }
+
+ printk(KERN_INFO "Starting EtherCAT device...\n");
+ if (ecdev_start(ec_device_master_index)) {
+ printk(KERN_ERR "Failed to start EtherCAT device!\n");
+ goto out_unregister;
+ }
+ } else {
+ printk(KERN_WARNING "No EtherCAT device registered!\n");
+ }
+
+ return 0;
+
+ out_unregister:
+ ecdev_unregister(ec_device_master_index, rtl_ec_dev);
+ out_pci:
+ pci_unregister_driver(&rtl8139_pci_driver);
+ out_return:
+ return -1;
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+
+static void __exit rtl8139_cleanup_module (void)
+{
+ /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ printk(KERN_INFO "Cleaning up RTL8139-EtherCAT module...\n");
+
+ if (rtl_ec_net_dev) {
+ printk(KERN_INFO "Stopping device...\n");
+ ecdev_stop(ec_device_master_index);
+ printk(KERN_INFO "Unregistering device...\n");
+ ecdev_unregister(ec_device_master_index, rtl_ec_dev);
+ rtl_ec_dev = NULL;
+ }
+
+ pci_unregister_driver(&rtl8139_pci_driver);
+
+ printk(KERN_INFO "RTL8139-EtherCAT module cleaned up.\n");
+
+ /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+}
+
+
+module_init(rtl8139_init_module);
+module_exit(rtl8139_cleanup_module);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devices/8139too-2.6.17-orig.c Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,2641 @@
+/*
+
+ 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux.
+
+ Maintained by Jeff Garzik <jgarzik@pobox.com>
+ Copyright 2000-2002 Jeff Garzik
+
+ Much code comes from Donald Becker's rtl8139.c driver,
+ versions 1.13 and older. This driver was originally based
+ on rtl8139.c version 1.07. Header of rtl8139.c version 1.13:
+
+ -----<snip>-----
+
+ Written 1997-2001 by Donald Becker.
+ This software may be used and distributed according to the
+ terms of the GNU General Public License (GPL), incorporated
+ herein by reference. Drivers based on or derived from this
+ code fall under the GPL and must retain the authorship,
+ copyright and license notice. This file is not a complete
+ program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ This driver is for boards based on the RTL8129 and RTL8139
+ PCI ethernet chips.
+
+ The author may be reached as becker@scyld.com, or C/O Scyld
+ Computing Corporation 410 Severn Ave., Suite 210 Annapolis
+ MD 21403
+
+ Support and updates available at
+ http://www.scyld.com/network/rtl8139.html
+
+ Twister-tuning table provided by Kinston
+ <shangh@realtek.com.tw>.
+
+ -----<snip>-----
+
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ Contributors:
+
+ Donald Becker - he wrote the original driver, kudos to him!
+ (but please don't e-mail him for support, this isn't his driver)
+
+ Tigran Aivazian - bug fixes, skbuff free cleanup
+
+ Martin Mares - suggestions for PCI cleanup
+
+ David S. Miller - PCI DMA and softnet updates
+
+ Ernst Gill - fixes ported from BSD driver
+
+ Daniel Kobras - identified specific locations of
+ posted MMIO write bugginess
+
+ Gerard Sharp - bug fix, testing and feedback
+
+ David Ford - Rx ring wrap fix
+
+ Dan DeMaggio - swapped RTL8139 cards with me, and allowed me
+ to find and fix a crucial bug on older chipsets.
+
+ Donald Becker/Chris Butterworth/Marcus Westergren -
+ Noticed various Rx packet size-related buglets.
+
+ Santiago Garcia Mantinan - testing and feedback
+
+ Jens David - 2.2.x kernel backports
+
+ Martin Dennett - incredibly helpful insight on undocumented
+ features of the 8139 chips
+
+ Jean-Jacques Michel - bug fix
+
+ Tobias Ringström - Rx interrupt status checking suggestion
+
+ Andrew Morton - Clear blocked signals, avoid
+ buffer overrun setting current->comm.
+
+ Kalle Olavi Niemitalo - Wake-on-LAN ioctls
+
+ Robert Kuebel - Save kernel thread from dying on any signal.
+
+ Submitting bug reports:
+
+ "rtl8139-diag -mmmaaavvveefN" output
+ enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log
+
+*/
+
+#define DRV_NAME "8139too"
+#define DRV_VERSION "0.9.27"
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/completion.h>
+#include <linux/crc32.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+
+#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION
+#define PFX DRV_NAME ": "
+
+/* Default Message level */
+#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK)
+
+
+/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */
+#ifdef CONFIG_8139TOO_PIO
+#define USE_IO_OPS 1
+#endif
+
+/* define to 1, 2 or 3 to enable copious debugging info */
+#define RTL8139_DEBUG 0
+
+/* define to 1 to disable lightweight runtime debugging checks */
+#undef RTL8139_NDEBUG
+
+
+#if RTL8139_DEBUG
+/* note: prints function name for you */
+# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+# define DPRINTK(fmt, args...)
+#endif
+
+#ifdef RTL8139_NDEBUG
+# define assert(expr) do {} while (0)
+#else
+# define assert(expr) \
+ if(unlikely(!(expr))) { \
+ printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr,__FILE__,__FUNCTION__,__LINE__); \
+ }
+#endif
+
+
+/* A few user-configurable values. */
+/* media options */
+#define MAX_UNITS 8
+static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ The RTL chips use a 64 element hash table based on the Ethernet CRC. */
+static int multicast_filter_limit = 32;
+
+/* bitmapped message enable number */
+static int debug = -1;
+
+/*
+ * Receive ring size
+ * Warning: 64K ring has hardware issues and may lock up.
+ */
+#if defined(CONFIG_SH_DREAMCAST)
+#define RX_BUF_IDX 1 /* 16K ring */
+#else
+#define RX_BUF_IDX 2 /* 32K ring */
+#endif
+#define RX_BUF_LEN (8192 << RX_BUF_IDX)
+#define RX_BUF_PAD 16
+#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
+
+#if RX_BUF_LEN == 65536
+#define RX_BUF_TOT_LEN RX_BUF_LEN
+#else
+#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
+#endif
+
+/* Number of Tx descriptor registers. */
+#define NUM_TX_DESC 4
+
+/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/
+#define MAX_ETH_FRAME_SIZE 1536
+
+/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
+#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE
+#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC)
+
+/* PCI Tuning Parameters
+ Threshold is bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
+
+/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */
+#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */
+#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
+#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (6*HZ)
+
+
+enum {
+ HAS_MII_XCVR = 0x010000,
+ HAS_CHIP_XCVR = 0x020000,
+ HAS_LNK_CHNG = 0x040000,
+};
+
+#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */
+#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */
+#define RTL_MIN_IO_SIZE 0x80
+#define RTL8139B_IO_SIZE 256
+
+#define RTL8129_CAPS HAS_MII_XCVR
+#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG
+
+typedef enum {
+ RTL8139 = 0,
+ RTL8129,
+} board_t;
+
+
+/* indexed by board_t, above */
+static const struct {
+ const char *name;
+ u32 hw_flags;
+} board_info[] __devinitdata = {
+ { "RealTek RTL8139", RTL8139_CAPS },
+ { "RealTek RTL8129", RTL8129_CAPS },
+};
+
+
+static struct pci_device_id rtl8139_pci_tbl[] = {
+ {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+ {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+
+#ifdef CONFIG_SH_SECUREEDGE5410
+ /* Bogus 8139 silicon reports 8129 without external PROM :-( */
+ {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
+#endif
+#ifdef CONFIG_8139TOO_8129
+ {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
+#endif
+
+ /* some crazy cards report invalid vendor ids like
+ * 0x0001 here. The other ids are valid and constant,
+ * so we simply don't match on the main vendor id.
+ */
+ {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
+ {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },
+ {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },
+
+ {0,}
+};
+MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
+
+static struct {
+ const char str[ETH_GSTRING_LEN];
+} ethtool_stats_keys[] = {
+ { "early_rx" },
+ { "tx_buf_mapped" },
+ { "tx_timeouts" },
+ { "rx_lost_in_ring" },
+};
+
+/* The rest of these values should never change. */
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+ MAC0 = 0, /* Ethernet hardware address. */
+ MAR0 = 8, /* Multicast filter. */
+ TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */
+ TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
+ RxBuf = 0x30,
+ ChipCmd = 0x37,
+ RxBufPtr = 0x38,
+ RxBufAddr = 0x3A,
+ IntrMask = 0x3C,
+ IntrStatus = 0x3E,
+ TxConfig = 0x40,
+ RxConfig = 0x44,
+ Timer = 0x48, /* A general-purpose counter. */
+ RxMissed = 0x4C, /* 24 bits valid, write clears. */
+ Cfg9346 = 0x50,
+ Config0 = 0x51,
+ Config1 = 0x52,
+ FlashReg = 0x54,
+ MediaStatus = 0x58,
+ Config3 = 0x59,
+ Config4 = 0x5A, /* absent on RTL-8139A */
+ HltClk = 0x5B,
+ MultiIntr = 0x5C,
+ TxSummary = 0x60,
+ BasicModeCtrl = 0x62,
+ BasicModeStatus = 0x64,
+ NWayAdvert = 0x66,
+ NWayLPAR = 0x68,
+ NWayExpansion = 0x6A,
+ /* Undocumented registers, but required for proper operation. */
+ FIFOTMS = 0x70, /* FIFO Control and test. */
+ CSCR = 0x74, /* Chip Status and Configuration Register. */
+ PARA78 = 0x78,
+ PARA7c = 0x7c, /* Magic transceiver parameter register. */
+ Config5 = 0xD8, /* absent on RTL-8139A */
+};
+
+enum ClearBitMasks {
+ MultiIntrClear = 0xF000,
+ ChipCmdClear = 0xE2,
+ Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
+};
+
+enum ChipCmdBits {
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08,
+ CmdTxEnb = 0x04,
+ RxBufEmpty = 0x01,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+ PCIErr = 0x8000,
+ PCSTimeout = 0x4000,
+ RxFIFOOver = 0x40,
+ RxUnderrun = 0x20,
+ RxOverflow = 0x10,
+ TxErr = 0x08,
+ TxOK = 0x04,
+ RxErr = 0x02,
+ RxOK = 0x01,
+
+ RxAckBits = RxFIFOOver | RxOverflow | RxOK,
+};
+
+enum TxStatusBits {
+ TxHostOwns = 0x2000,
+ TxUnderrun = 0x4000,
+ TxStatOK = 0x8000,
+ TxOutOfWindow = 0x20000000,
+ TxAborted = 0x40000000,
+ TxCarrierLost = 0x80000000,
+};
+enum RxStatusBits {
+ RxMulticast = 0x8000,
+ RxPhysical = 0x4000,
+ RxBroadcast = 0x2000,
+ RxBadSymbol = 0x0020,
+ RxRunt = 0x0010,
+ RxTooLong = 0x0008,
+ RxCRCErr = 0x0004,
+ RxBadAlign = 0x0002,
+ RxStatusOK = 0x0001,
+};
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+};
+
+/* Bits in TxConfig. */
+enum tx_config_bits {
+
+ /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
+ TxIFGShift = 24,
+ TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
+ TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
+ TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
+ TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
+
+ TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
+ TxClearAbt = (1 << 0), /* Clear abort (WO) */
+ TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */
+ TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */
+
+ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+/* Bits in Config1 */
+enum Config1Bits {
+ Cfg1_PM_Enable = 0x01,
+ Cfg1_VPD_Enable = 0x02,
+ Cfg1_PIO = 0x04,
+ Cfg1_MMIO = 0x08,
+ LWAKE = 0x10, /* not on 8139, 8139A */
+ Cfg1_Driver_Load = 0x20,
+ Cfg1_LED0 = 0x40,
+ Cfg1_LED1 = 0x80,
+ SLEEP = (1 << 1), /* only on 8139, 8139A */
+ PWRDN = (1 << 0), /* only on 8139, 8139A */
+};
+
+/* Bits in Config3 */
+enum Config3Bits {
+ Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */
+ Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
+ Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
+ Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */
+ Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */
+ Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
+ Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */
+ Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
+};
+
+/* Bits in Config4 */
+enum Config4Bits {
+ LWPTN = (1 << 2), /* not on 8139, 8139A */
+};
+
+/* Bits in Config5 */
+enum Config5Bits {
+ Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */
+ Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */
+ Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */
+ Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
+ Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */
+ Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */
+ Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */
+};
+
+enum RxConfigBits {
+ /* rx fifo threshold */
+ RxCfgFIFOShift = 13,
+ RxCfgFIFONone = (7 << RxCfgFIFOShift),
+
+ /* Max DMA burst */
+ RxCfgDMAShift = 8,
+ RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
+
+ /* rx ring buffer length */
+ RxCfgRcv8K = 0,
+ RxCfgRcv16K = (1 << 11),
+ RxCfgRcv32K = (1 << 12),
+ RxCfgRcv64K = (1 << 11) | (1 << 12),
+
+ /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
+ RxNoWrap = (1 << 7),
+};
+
+/* Twister tuning parameters from RealTek.
+ Completely undocumented, but required to tune bad links on some boards. */
+enum CSCRBits {
+ CSCR_LinkOKBit = 0x0400,
+ CSCR_LinkChangeBit = 0x0800,
+ CSCR_LinkStatusBits = 0x0f000,
+ CSCR_LinkDownOffCmd = 0x003c0,
+ CSCR_LinkDownCmd = 0x0f3c0,
+};
+
+enum Cfg9346Bits {
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xC0,
+};
+
+typedef enum {
+ CH_8139 = 0,
+ CH_8139_K,
+ CH_8139A,
+ CH_8139A_G,
+ CH_8139B,
+ CH_8130,
+ CH_8139C,
+ CH_8100,
+ CH_8100B_8139D,
+ CH_8101,
+} chip_t;
+
+enum chip_flags {
+ HasHltClk = (1 << 0),
+ HasLWake = (1 << 1),
+};
+
+#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
+ (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
+#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1)
+
+/* directly indexed by chip_t, above */
+static const struct {
+ const char *name;
+ u32 version; /* from RTL8139C/RTL8139D docs */
+ u32 flags;
+} rtl_chip_info[] = {
+ { "RTL-8139",
+ HW_REVID(1, 0, 0, 0, 0, 0, 0),
+ HasHltClk,
+ },
+
+ { "RTL-8139 rev K",
+ HW_REVID(1, 1, 0, 0, 0, 0, 0),
+ HasHltClk,
+ },
+
+ { "RTL-8139A",
+ HW_REVID(1, 1, 1, 0, 0, 0, 0),
+ HasHltClk, /* XXX undocumented? */
+ },
+
+ { "RTL-8139A rev G",
+ HW_REVID(1, 1, 1, 0, 0, 1, 0),
+ HasHltClk, /* XXX undocumented? */
+ },
+
+ { "RTL-8139B",
+ HW_REVID(1, 1, 1, 1, 0, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8130",
+ HW_REVID(1, 1, 1, 1, 1, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8139C",
+ HW_REVID(1, 1, 1, 0, 1, 0, 0),
+ HasLWake,
+ },
+
+ { "RTL-8100",
+ HW_REVID(1, 1, 1, 1, 0, 1, 0),
+ HasLWake,
+ },
+
+ { "RTL-8100B/8139D",
+ HW_REVID(1, 1, 1, 0, 1, 0, 1),
+ HasHltClk /* XXX undocumented? */
+ | HasLWake,
+ },
+
+ { "RTL-8101",
+ HW_REVID(1, 1, 1, 0, 1, 1, 1),
+ HasLWake,
+ },
+};
+
+struct rtl_extra_stats {
+ unsigned long early_rx;
+ unsigned long tx_buf_mapped;
+ unsigned long tx_timeouts;
+ unsigned long rx_lost_in_ring;
+};
+
+struct rtl8139_private {
+ void __iomem *mmio_addr;
+ int drv_flags;
+ struct pci_dev *pci_dev;
+ u32 msg_enable;
+ struct net_device_stats stats;
+ unsigned char *rx_ring;
+ unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */
+ unsigned int tx_flag;
+ unsigned long cur_tx;
+ unsigned long dirty_tx;
+ unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */
+ unsigned char *tx_bufs; /* Tx bounce buffer region. */
+ dma_addr_t rx_ring_dma;
+ dma_addr_t tx_bufs_dma;
+ signed char phys[4]; /* MII device addresses. */
+ char twistie, twist_row, twist_col; /* Twister tune state. */
+ unsigned int watchdog_fired : 1;
+ unsigned int default_port : 4; /* Last dev->if_port value. */
+ unsigned int have_thread : 1;
+ spinlock_t lock;
+ spinlock_t rx_lock;
+ chip_t chipset;
+ u32 rx_config;
+ struct rtl_extra_stats xstats;
+
+ struct work_struct thread;
+
+ struct mii_if_info mii;
+ unsigned int regs_len;
+ unsigned long fifo_copy_timeout;
+};
+
+MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
+MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_param(multicast_filter_limit, int, 0);
+module_param_array(media, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+module_param(debug, int, 0);
+MODULE_PARM_DESC (debug, "8139too bitmapped message enable number");
+MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses");
+MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps");
+MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)");
+
+static int read_eeprom (void __iomem *ioaddr, int location, int addr_len);
+static int rtl8139_open (struct net_device *dev);
+static int mdio_read (struct net_device *dev, int phy_id, int location);
+static void mdio_write (struct net_device *dev, int phy_id, int location,
+ int val);
+static void rtl8139_start_thread(struct rtl8139_private *tp);
+static void rtl8139_tx_timeout (struct net_device *dev);
+static void rtl8139_init_ring (struct net_device *dev);
+static int rtl8139_start_xmit (struct sk_buff *skb,
+ struct net_device *dev);
+static int rtl8139_poll(struct net_device *dev, int *budget);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void rtl8139_poll_controller(struct net_device *dev);
+#endif
+static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs);
+static int rtl8139_close (struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
+static void rtl8139_set_rx_mode (struct net_device *dev);
+static void __set_rx_mode (struct net_device *dev);
+static void rtl8139_hw_start (struct net_device *dev);
+static void rtl8139_thread (void *_data);
+static void rtl8139_tx_timeout_task(void *_data);
+static struct ethtool_ops rtl8139_ethtool_ops;
+
+/* write MMIO register, with flush */
+/* Flush avoids rtl8139 bug w/ posted MMIO writes */
+#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0)
+#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0)
+#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0)
+
+
+#define MMIO_FLUSH_AUDIT_COMPLETE 1
+#if MMIO_FLUSH_AUDIT_COMPLETE
+
+/* write MMIO register */
+#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg))
+
+#else
+
+/* write MMIO register, then flush */
+#define RTL_W8 RTL_W8_F
+#define RTL_W16 RTL_W16_F
+#define RTL_W32 RTL_W32_F
+
+#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
+
+/* read MMIO register */
+#define RTL_R8(reg) ioread8 (ioaddr + (reg))
+#define RTL_R16(reg) ioread16 (ioaddr + (reg))
+#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg)))
+
+
+static const u16 rtl8139_intr_mask =
+ PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
+ TxErr | TxOK | RxErr | RxOK;
+
+static const u16 rtl8139_norx_intr_mask =
+ PCIErr | PCSTimeout | RxUnderrun |
+ TxErr | TxOK | RxErr ;
+
+#if RX_BUF_IDX == 0
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv8K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 1
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv16K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 2
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv32K | RxNoWrap |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#elif RX_BUF_IDX == 3
+static const unsigned int rtl8139_rx_config =
+ RxCfgRcv64K |
+ (RX_FIFO_THRESH << RxCfgFIFOShift) |
+ (RX_DMA_BURST << RxCfgDMAShift);
+#else
+#error "Invalid configuration for 8139_RXBUF_IDX"
+#endif
+
+static const unsigned int rtl8139_tx_config =
+ TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift);
+
+static void __rtl8139_cleanup_dev (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ struct pci_dev *pdev;
+
+ assert (dev != NULL);
+ assert (tp->pci_dev != NULL);
+ pdev = tp->pci_dev;
+
+#ifdef USE_IO_OPS
+ if (tp->mmio_addr)
+ ioport_unmap (tp->mmio_addr);
+#else
+ if (tp->mmio_addr)
+ pci_iounmap (pdev, tp->mmio_addr);
+#endif /* USE_IO_OPS */
+
+ /* it's ok to call this even if we have no regions to free */
+ pci_release_regions (pdev);
+
+ free_netdev(dev);
+ pci_set_drvdata (pdev, NULL);
+}
+
+
+static void rtl8139_chip_reset (void __iomem *ioaddr)
+{
+ int i;
+
+ /* Soft reset the chip. */
+ RTL_W8 (ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--) {
+ barrier();
+ if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
+ break;
+ udelay (10);
+ }
+}
+
+
+static int __devinit rtl8139_init_board (struct pci_dev *pdev,
+ struct net_device **dev_out)
+{
+ void __iomem *ioaddr;
+ struct net_device *dev;
+ struct rtl8139_private *tp;
+ u8 tmp8;
+ int rc, disable_dev_on_err = 0;
+ unsigned int i;
+ unsigned long pio_start, pio_end, pio_flags, pio_len;
+ unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
+ u32 version;
+
+ assert (pdev != NULL);
+
+ *dev_out = NULL;
+
+ /* dev and priv zeroed in alloc_etherdev */
+ dev = alloc_etherdev (sizeof (*tp));
+ if (dev == NULL) {
+ printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev));
+ return -ENOMEM;
+ }
+ SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ tp = netdev_priv(dev);
+ tp->pci_dev = pdev;
+
+ /* enable device (incl. PCI PM wakeup and hotplug setup) */
+ rc = pci_enable_device (pdev);
+ if (rc)
+ goto err_out;
+
+ pio_start = pci_resource_start (pdev, 0);
+ pio_end = pci_resource_end (pdev, 0);
+ pio_flags = pci_resource_flags (pdev, 0);
+ pio_len = pci_resource_len (pdev, 0);
+
+ mmio_start = pci_resource_start (pdev, 1);
+ mmio_end = pci_resource_end (pdev, 1);
+ mmio_flags = pci_resource_flags (pdev, 1);
+ mmio_len = pci_resource_len (pdev, 1);
+
+ /* set this immediately, we need to know before
+ * we talk to the chip directly */
+ DPRINTK("PIO region size == 0x%02X\n", pio_len);
+ DPRINTK("MMIO region size == 0x%02lX\n", mmio_len);
+
+#ifdef USE_IO_OPS
+ /* make sure PCI base addr 0 is PIO */
+ if (!(pio_flags & IORESOURCE_IO)) {
+ printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+ /* check for weird/broken PCI region reporting */
+ if (pio_len < RTL_MIN_IO_SIZE) {
+ printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+#else
+ /* make sure PCI base addr 1 is MMIO */
+ if (!(mmio_flags & IORESOURCE_MEM)) {
+ printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+ if (mmio_len < RTL_MIN_IO_SIZE) {
+ printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev));
+ rc = -ENODEV;
+ goto err_out;
+ }
+#endif
+
+ rc = pci_request_regions (pdev, "8139too");
+ if (rc)
+ goto err_out;
+ disable_dev_on_err = 1;
+
+ /* enable PCI bus-mastering */
+ pci_set_master (pdev);
+
+#ifdef USE_IO_OPS
+ ioaddr = ioport_map(pio_start, pio_len);
+ if (!ioaddr) {
+ printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+ dev->base_addr = pio_start;
+ tp->mmio_addr = ioaddr;
+ tp->regs_len = pio_len;
+#else
+ /* ioremap MMIO region */
+ ioaddr = pci_iomap(pdev, 1, 0);
+ if (ioaddr == NULL) {
+ printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+ dev->base_addr = (long) ioaddr;
+ tp->mmio_addr = ioaddr;
+ tp->regs_len = mmio_len;
+#endif /* USE_IO_OPS */
+
+ /* Bring old chips out of low-power mode. */
+ RTL_W8 (HltClk, 'R');
+
+ /* check for missing/broken hardware */
+ if (RTL_R32 (TxConfig) == 0xFFFFFFFF) {
+ printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n",
+ pci_name(pdev));
+ rc = -EIO;
+ goto err_out;
+ }
+
+ /* identify chip attached to board */
+ version = RTL_R32 (TxConfig) & HW_REVID_MASK;
+ for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++)
+ if (version == rtl_chip_info[i].version) {
+ tp->chipset = i;
+ goto match;
+ }
+
+ /* if unknown chip, assume array element #0, original RTL-8139 in this case */
+ printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n",
+ pci_name(pdev));
+ printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig));
+ tp->chipset = 0;
+
+match:
+ DPRINTK ("chipset id (%d) == index %d, '%s'\n",
+ version, i, rtl_chip_info[i].name);
+
+ if (tp->chipset >= CH_8139B) {
+ u8 new_tmp8 = tmp8 = RTL_R8 (Config1);
+ DPRINTK("PCI PM wakeup\n");
+ if ((rtl_chip_info[tp->chipset].flags & HasLWake) &&
+ (tmp8 & LWAKE))
+ new_tmp8 &= ~LWAKE;
+ new_tmp8 |= Cfg1_PM_Enable;
+ if (new_tmp8 != tmp8) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config1, tmp8);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ }
+ if (rtl_chip_info[tp->chipset].flags & HasLWake) {
+ tmp8 = RTL_R8 (Config4);
+ if (tmp8 & LWPTN) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config4, tmp8 & ~LWPTN);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ }
+ }
+ } else {
+ DPRINTK("Old chip wakeup\n");
+ tmp8 = RTL_R8 (Config1);
+ tmp8 &= ~(SLEEP | PWRDN);
+ RTL_W8 (Config1, tmp8);
+ }
+
+ rtl8139_chip_reset (ioaddr);
+
+ *dev_out = dev;
+ return 0;
+
+err_out:
+ __rtl8139_cleanup_dev (dev);
+ if (disable_dev_on_err)
+ pci_disable_device (pdev);
+ return rc;
+}
+
+
+static int __devinit rtl8139_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev = NULL;
+ struct rtl8139_private *tp;
+ int i, addr_len, option;
+ void __iomem *ioaddr;
+ static int board_idx = -1;
+ u8 pci_rev;
+
+ assert (pdev != NULL);
+ assert (ent != NULL);
+
+ board_idx++;
+
+ /* when we're built into the kernel, the driver version message
+ * is only printed if at least one 8139 board has been found
+ */
+#ifndef MODULE
+ {
+ static int printed_version;
+ if (!printed_version++)
+ printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
+ }
+#endif
+
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev);
+
+ if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
+ pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) {
+ printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n",
+ pci_name(pdev), pdev->vendor, pdev->device, pci_rev);
+ printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n");
+ }
+
+ i = rtl8139_init_board (pdev, &dev);
+ if (i < 0)
+ return i;
+
+ assert (dev != NULL);
+ tp = netdev_priv(dev);
+
+ ioaddr = tp->mmio_addr;
+ assert (ioaddr != NULL);
+
+ addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6;
+ for (i = 0; i < 3; i++)
+ ((u16 *) (dev->dev_addr))[i] =
+ le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len));
+ memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+
+ /* The Rtl8139-specific entries in the device structure. */
+ dev->open = rtl8139_open;
+ dev->hard_start_xmit = rtl8139_start_xmit;
+ dev->poll = rtl8139_poll;
+ dev->weight = 64;
+ dev->stop = rtl8139_close;
+ dev->get_stats = rtl8139_get_stats;
+ dev->set_multicast_list = rtl8139_set_rx_mode;
+ dev->do_ioctl = netdev_ioctl;
+ dev->ethtool_ops = &rtl8139_ethtool_ops;
+ dev->tx_timeout = rtl8139_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = rtl8139_poll_controller;
+#endif
+
+ /* note: the hardware is not capable of sg/csum/highdma, however
+ * through the use of skb_copy_and_csum_dev we enable these
+ * features
+ */
+ dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;
+
+ dev->irq = pdev->irq;
+
+ /* tp zeroed and aligned in alloc_etherdev */
+ tp = netdev_priv(dev);
+
+ /* note: tp->chipset set in rtl8139_init_board */
+ tp->drv_flags = board_info[ent->driver_data].hw_flags;
+ tp->mmio_addr = ioaddr;
+ tp->msg_enable =
+ (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1));
+ spin_lock_init (&tp->lock);
+ spin_lock_init (&tp->rx_lock);
+ INIT_WORK(&tp->thread, rtl8139_thread, dev);
+ tp->mii.dev = dev;
+ tp->mii.mdio_read = mdio_read;
+ tp->mii.mdio_write = mdio_write;
+ tp->mii.phy_id_mask = 0x3f;
+ tp->mii.reg_num_mask = 0x1f;
+
+ /* dev is fully set up and ready to use now */
+ DPRINTK("about to register device named %s (%p)...\n", dev->name, dev);
+ i = register_netdev (dev);
+ if (i) goto err_out;
+
+ pci_set_drvdata (pdev, dev);
+
+ printk (KERN_INFO "%s: %s at 0x%lx, "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+ "IRQ %d\n",
+ dev->name,
+ board_info[ent->driver_data].name,
+ dev->base_addr,
+ dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3],
+ dev->dev_addr[4], dev->dev_addr[5],
+ dev->irq);
+
+ printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n",
+ dev->name, rtl_chip_info[tp->chipset].name);
+
+ /* Find the connected MII xcvrs.
+ Doing this in open() would allow detecting external xcvrs later, but
+ takes too much time. */
+#ifdef CONFIG_8139TOO_8129
+ if (tp->drv_flags & HAS_MII_XCVR) {
+ int phy, phy_idx = 0;
+ for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) {
+ int mii_status = mdio_read(dev, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ u16 advertising = mdio_read(dev, phy, 4);
+ tp->phys[phy_idx++] = phy;
+ printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x "
+ "advertising %4.4x.\n",
+ dev->name, phy, mii_status, advertising);
+ }
+ }
+ if (phy_idx == 0) {
+ printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM "
+ "transceiver.\n",
+ dev->name);
+ tp->phys[0] = 32;
+ }
+ } else
+#endif
+ tp->phys[0] = 32;
+ tp->mii.phy_id = tp->phys[0];
+
+ /* The lower four bits are the media type. */
+ option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx];
+ if (option > 0) {
+ tp->mii.full_duplex = (option & 0x210) ? 1 : 0;
+ tp->default_port = option & 0xFF;
+ if (tp->default_port)
+ tp->mii.force_media = 1;
+ }
+ if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0)
+ tp->mii.full_duplex = full_duplex[board_idx];
+ if (tp->mii.full_duplex) {
+ printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
+ /* Changing the MII-advertised media because might prevent
+ re-connection. */
+ tp->mii.force_media = 1;
+ }
+ if (tp->default_port) {
+ printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n",
+ (option & 0x20 ? 100 : 10),
+ (option & 0x10 ? "full" : "half"));
+ mdio_write(dev, tp->phys[0], 0,
+ ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */
+ ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+ }
+
+ /* Put the chip into low-power mode. */
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
+
+ return 0;
+
+err_out:
+ __rtl8139_cleanup_dev (dev);
+ pci_disable_device (pdev);
+ return i;
+}
+
+
+static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ assert (dev != NULL);
+
+ unregister_netdev (dev);
+
+ __rtl8139_cleanup_dev (dev);
+ pci_disable_device (pdev);
+}
+
+
+/* Serial EEPROM section. */
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x08 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x00
+#define EE_WRITE_1 0x02
+#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
+#define EE_ENB (0x80 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
+ */
+
+#define eeprom_delay() (void)RTL_R32(Cfg9346)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5)
+#define EE_READ_CMD (6)
+#define EE_ERASE_CMD (7)
+
+static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len)
+{
+ int i;
+ unsigned retval = 0;
+ int read_cmd = location | (EE_READ_CMD << addr_len);
+
+ RTL_W8 (Cfg9346, EE_ENB & ~EE_CS);
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+
+ /* Shift the read command bits out. */
+ for (i = 4 + addr_len; i >= 0; i--) {
+ int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ RTL_W8 (Cfg9346, EE_ENB | dataval);
+ eeprom_delay ();
+ RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK);
+ eeprom_delay ();
+ }
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+
+ for (i = 16; i > 0; i--) {
+ RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK);
+ eeprom_delay ();
+ retval =
+ (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 :
+ 0);
+ RTL_W8 (Cfg9346, EE_ENB);
+ eeprom_delay ();
+ }
+
+ /* Terminate the EEPROM access. */
+ RTL_W8 (Cfg9346, ~EE_CS);
+ eeprom_delay ();
+
+ return retval;
+}
+
+/* MII serial management: mostly bogus for now. */
+/* Read and write the MII management registers using software-generated
+ serial MDIO protocol.
+ The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+ "overclocking" issues. */
+#define MDIO_DIR 0x80
+#define MDIO_DATA_OUT 0x04
+#define MDIO_DATA_IN 0x02
+#define MDIO_CLK 0x01
+#define MDIO_WRITE0 (MDIO_DIR)
+#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
+
+#define mdio_delay() RTL_R8(Config4)
+
+
+static const char mii_2_8139_map[8] = {
+ BasicModeCtrl,
+ BasicModeStatus,
+ 0,
+ 0,
+ NWayAdvert,
+ NWayLPAR,
+ NWayExpansion,
+ 0
+};
+
+
+#ifdef CONFIG_8139TOO_8129
+/* Syncronize the MII management interface by shifting 32 one bits out. */
+static void mdio_sync (void __iomem *ioaddr)
+{
+ int i;
+
+ for (i = 32; i >= 0; i--) {
+ RTL_W8 (Config4, MDIO_WRITE1);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK);
+ mdio_delay ();
+ }
+}
+#endif
+
+static int mdio_read (struct net_device *dev, int phy_id, int location)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int retval = 0;
+#ifdef CONFIG_8139TOO_8129
+ void __iomem *ioaddr = tp->mmio_addr;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i;
+#endif
+
+ if (phy_id > 31) { /* Really a 8139. Use internal registers. */
+ void __iomem *ioaddr = tp->mmio_addr;
+ return location < 8 && mii_2_8139_map[location] ?
+ RTL_R16 (mii_2_8139_map[location]) : 0;
+ }
+
+#ifdef CONFIG_8139TOO_8129
+ mdio_sync (ioaddr);
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
+
+ RTL_W8 (Config4, MDIO_DIR | dataval);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK);
+ mdio_delay ();
+ }
+
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ RTL_W8 (Config4, 0);
+ mdio_delay ();
+ retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0);
+ RTL_W8 (Config4, MDIO_CLK);
+ mdio_delay ();
+ }
+#endif
+
+ return (retval >> 1) & 0xffff;
+}
+
+
+static void mdio_write (struct net_device *dev, int phy_id, int location,
+ int value)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+#ifdef CONFIG_8139TOO_8129
+ void __iomem *ioaddr = tp->mmio_addr;
+ int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
+ int i;
+#endif
+
+ if (phy_id > 31) { /* Really a 8139. Use internal registers. */
+ void __iomem *ioaddr = tp->mmio_addr;
+ if (location == 0) {
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W16 (BasicModeCtrl, value);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+ } else if (location < 8 && mii_2_8139_map[location])
+ RTL_W16 (mii_2_8139_map[location], value);
+ return;
+ }
+
+#ifdef CONFIG_8139TOO_8129
+ mdio_sync (ioaddr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval =
+ (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+ RTL_W8 (Config4, dataval);
+ mdio_delay ();
+ RTL_W8 (Config4, dataval | MDIO_CLK);
+ mdio_delay ();
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ RTL_W8 (Config4, 0);
+ mdio_delay ();
+ RTL_W8 (Config4, MDIO_CLK);
+ mdio_delay ();
+ }
+#endif
+}
+
+
+static int rtl8139_open (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int retval;
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev);
+ if (retval)
+ return retval;
+
+ tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ &tp->tx_bufs_dma);
+ tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ &tp->rx_ring_dma);
+ if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
+ free_irq(dev->irq, dev);
+
+ if (tp->tx_bufs)
+ pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ tp->tx_bufs, tp->tx_bufs_dma);
+ if (tp->rx_ring)
+ pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ tp->rx_ring, tp->rx_ring_dma);
+
+ return -ENOMEM;
+
+ }
+
+ tp->mii.full_duplex = tp->mii.force_media;
+ tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
+
+ rtl8139_init_ring (dev);
+ rtl8139_hw_start (dev);
+ netif_start_queue (dev);
+
+ if (netif_msg_ifup(tp))
+ printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d"
+ " GP Pins %2.2x %s-duplex.\n",
+ dev->name, pci_resource_start (tp->pci_dev, 1),
+ dev->irq, RTL_R8 (MediaStatus),
+ tp->mii.full_duplex ? "full" : "half");
+
+ rtl8139_start_thread(tp);
+
+ return 0;
+}
+
+
+static void rtl_check_media (struct net_device *dev, unsigned int init_media)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ if (tp->phys[0] >= 0) {
+ mii_check_media(&tp->mii, netif_msg_link(tp), init_media);
+ }
+}
+
+/* Start the hardware at open or resume. */
+static void rtl8139_hw_start (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u32 i;
+ u8 tmp;
+
+ /* Bring old chips out of low-power mode. */
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'R');
+
+ rtl8139_chip_reset (ioaddr);
+
+ /* unlock Config[01234] and BMCR register writes */
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+ /* Restore our idea of the MAC address. */
+ RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
+ RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
+
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
+ RTL_W32 (RxConfig, tp->rx_config);
+ RTL_W32 (TxConfig, rtl8139_tx_config);
+
+ tp->cur_rx = 0;
+
+ rtl_check_media (dev, 1);
+
+ if (tp->chipset >= CH_8139B) {
+ /* Disable magic packet scanning, which is enabled
+ * when PM is enabled in Config1. It can be reenabled
+ * via ETHTOOL_SWOL if desired. */
+ RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic);
+ }
+
+ DPRINTK("init buffer addresses\n");
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+ /* init Tx buffer DMA addresses */
+ for (i = 0; i < NUM_TX_DESC; i++)
+ RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
+
+ RTL_W32 (RxMissed, 0);
+
+ rtl8139_set_rx_mode (dev);
+
+ /* no early-rx interrupts */
+ RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear);
+
+ /* make sure RxTx has started */
+ tmp = RTL_R8 (ChipCmd);
+ if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb)))
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ /* Enable all known interrupts by setting the interrupt mask. */
+ RTL_W16 (IntrMask, rtl8139_intr_mask);
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void rtl8139_init_ring (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ int i;
+
+ tp->cur_rx = 0;
+ tp->cur_tx = 0;
+ tp->dirty_tx = 0;
+
+ for (i = 0; i < NUM_TX_DESC; i++)
+ tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE];
+}
+
+
+/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */
+static int next_tick = 3 * HZ;
+
+#ifndef CONFIG_8139TOO_TUNE_TWISTER
+static inline void rtl8139_tune_twister (struct net_device *dev,
+ struct rtl8139_private *tp) {}
+#else
+enum TwisterParamVals {
+ PARA78_default = 0x78fa8388,
+ PARA7c_default = 0xcb38de43, /* param[0][3] */
+ PARA7c_xxx = 0xcb38de43,
+};
+
+static const unsigned long param[4][4] = {
+ {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
+ {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
+};
+
+static void rtl8139_tune_twister (struct net_device *dev,
+ struct rtl8139_private *tp)
+{
+ int linkcase;
+ void __iomem *ioaddr = tp->mmio_addr;
+
+ /* This is a complicated state machine to configure the "twister" for
+ impedance/echos based on the cable length.
+ All of this is magic and undocumented.
+ */
+ switch (tp->twistie) {
+ case 1:
+ if (RTL_R16 (CSCR) & CSCR_LinkOKBit) {
+ /* We have link beat, let us tune the twister. */
+ RTL_W16 (CSCR, CSCR_LinkDownOffCmd);
+ tp->twistie = 2; /* Change to state 2. */
+ next_tick = HZ / 10;
+ } else {
+ /* Just put in some reasonable defaults for when beat returns. */
+ RTL_W16 (CSCR, CSCR_LinkDownCmd);
+ RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */
+ RTL_W32 (PARA78, PARA78_default);
+ RTL_W32 (PARA7c, PARA7c_default);
+ tp->twistie = 0; /* Bail from future actions. */
+ }
+ break;
+ case 2:
+ /* Read how long it took to hear the echo. */
+ linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits;
+ if (linkcase == 0x7000)
+ tp->twist_row = 3;
+ else if (linkcase == 0x3000)
+ tp->twist_row = 2;
+ else if (linkcase == 0x1000)
+ tp->twist_row = 1;
+ else
+ tp->twist_row = 0;
+ tp->twist_col = 0;
+ tp->twistie = 3; /* Change to state 2. */
+ next_tick = HZ / 10;
+ break;
+ case 3:
+ /* Put out four tuning parameters, one per 100msec. */
+ if (tp->twist_col == 0)
+ RTL_W16 (FIFOTMS, 0);
+ RTL_W32 (PARA7c, param[(int) tp->twist_row]
+ [(int) tp->twist_col]);
+ next_tick = HZ / 10;
+ if (++tp->twist_col >= 4) {
+ /* For short cables we are done.
+ For long cables (row == 3) check for mistune. */
+ tp->twistie =
+ (tp->twist_row == 3) ? 4 : 0;
+ }
+ break;
+ case 4:
+ /* Special case for long cables: check for mistune. */
+ if ((RTL_R16 (CSCR) &
+ CSCR_LinkStatusBits) == 0x7000) {
+ tp->twistie = 0;
+ break;
+ } else {
+ RTL_W32 (PARA7c, 0xfb38de03);
+ tp->twistie = 5;
+ next_tick = HZ / 10;
+ }
+ break;
+ case 5:
+ /* Retune for shorter cable (column 2). */
+ RTL_W32 (FIFOTMS, 0x20);
+ RTL_W32 (PARA78, PARA78_default);
+ RTL_W32 (PARA7c, PARA7c_default);
+ RTL_W32 (FIFOTMS, 0x00);
+ tp->twist_row = 2;
+ tp->twist_col = 0;
+ tp->twistie = 3;
+ next_tick = HZ / 10;
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+}
+#endif /* CONFIG_8139TOO_TUNE_TWISTER */
+
+static inline void rtl8139_thread_iter (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr)
+{
+ int mii_lpa;
+
+ mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA);
+
+ if (!tp->mii.force_media && mii_lpa != 0xffff) {
+ int duplex = (mii_lpa & LPA_100FULL)
+ || (mii_lpa & 0x01C0) == 0x0040;
+ if (tp->mii.full_duplex != duplex) {
+ tp->mii.full_duplex = duplex;
+
+ if (mii_lpa) {
+ printk (KERN_INFO
+ "%s: Setting %s-duplex based on MII #%d link"
+ " partner ability of %4.4x.\n",
+ dev->name,
+ tp->mii.full_duplex ? "full" : "half",
+ tp->phys[0], mii_lpa);
+ } else {
+ printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n",
+ dev->name);
+ }
+#if 0
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+#endif
+ }
+ }
+
+ next_tick = HZ * 60;
+
+ rtl8139_tune_twister (dev, tp);
+
+ DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n",
+ dev->name, RTL_R16 (NWayLPAR));
+ DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n",
+ dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus));
+ DPRINTK ("%s: Chip config %2.2x %2.2x.\n",
+ dev->name, RTL_R8 (Config0),
+ RTL_R8 (Config1));
+}
+
+static void rtl8139_thread (void *_data)
+{
+ struct net_device *dev = _data;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ unsigned long thr_delay = next_tick;
+
+ if (tp->watchdog_fired) {
+ tp->watchdog_fired = 0;
+ rtl8139_tx_timeout_task(_data);
+ } else if (rtnl_trylock()) {
+ rtl8139_thread_iter (dev, tp, tp->mmio_addr);
+ rtnl_unlock ();
+ } else {
+ /* unlikely race. mitigate with fast poll. */
+ thr_delay = HZ / 2;
+ }
+
+ schedule_delayed_work(&tp->thread, thr_delay);
+}
+
+static void rtl8139_start_thread(struct rtl8139_private *tp)
+{
+ tp->twistie = 0;
+ if (tp->chipset == CH_8139_K)
+ tp->twistie = 1;
+ else if (tp->drv_flags & HAS_LNK_CHNG)
+ return;
+
+ tp->have_thread = 1;
+
+ schedule_delayed_work(&tp->thread, next_tick);
+}
+
+static void rtl8139_stop_thread(struct rtl8139_private *tp)
+{
+ if (tp->have_thread) {
+ cancel_rearming_delayed_work(&tp->thread);
+ tp->have_thread = 0;
+ } else
+ flush_scheduled_work();
+}
+
+static inline void rtl8139_tx_clear (struct rtl8139_private *tp)
+{
+ tp->cur_tx = 0;
+ tp->dirty_tx = 0;
+
+ /* XXX account for unsent Tx packets in tp->stats.tx_dropped */
+}
+
+static void rtl8139_tx_timeout_task (void *_data)
+{
+ struct net_device *dev = _data;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int i;
+ u8 tmp8;
+
+ printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x "
+ "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd),
+ RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus));
+ /* Emit info to figure out what went wrong. */
+ printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n",
+ dev->name, tp->cur_tx, tp->dirty_tx);
+ for (i = 0; i < NUM_TX_DESC; i++)
+ printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n",
+ dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
+ i == tp->dirty_tx % NUM_TX_DESC ?
+ " (queue head)" : "");
+
+ tp->xstats.tx_timeouts++;
+
+ /* disable Tx ASAP, if not already */
+ tmp8 = RTL_R8 (ChipCmd);
+ if (tmp8 & CmdTxEnb)
+ RTL_W8 (ChipCmd, CmdRxEnb);
+
+ spin_lock_bh(&tp->rx_lock);
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0x0000);
+
+ /* Stop a shared interrupt from scavenging while we are. */
+ spin_lock_irq(&tp->lock);
+ rtl8139_tx_clear (tp);
+ spin_unlock_irq(&tp->lock);
+
+ /* ...and finally, reset everything */
+ if (netif_running(dev)) {
+ rtl8139_hw_start (dev);
+ netif_wake_queue (dev);
+ }
+ spin_unlock_bh(&tp->rx_lock);
+}
+
+static void rtl8139_tx_timeout (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ if (!tp->have_thread) {
+ INIT_WORK(&tp->thread, rtl8139_tx_timeout_task, dev);
+ schedule_delayed_work(&tp->thread, next_tick);
+ } else
+ tp->watchdog_fired = 1;
+
+}
+
+static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned int entry;
+ unsigned int len = skb->len;
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = tp->cur_tx % NUM_TX_DESC;
+
+ /* Note: the chip doesn't have auto-pad! */
+ if (likely(len < TX_BUF_SIZE)) {
+ if (len < ETH_ZLEN)
+ memset(tp->tx_buf[entry], 0, ETH_ZLEN);
+ skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
+ dev_kfree_skb(skb);
+ } else {
+ dev_kfree_skb(skb);
+ tp->stats.tx_dropped++;
+ return 0;
+ }
+
+ spin_lock_irq(&tp->lock);
+ RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
+ tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
+
+ dev->trans_start = jiffies;
+
+ tp->cur_tx++;
+ wmb();
+
+ if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
+ netif_stop_queue (dev);
+ spin_unlock_irq(&tp->lock);
+
+ if (netif_msg_tx_queued(tp))
+ printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n",
+ dev->name, len, entry);
+
+ return 0;
+}
+
+
+static void rtl8139_tx_interrupt (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr)
+{
+ unsigned long dirty_tx, tx_left;
+
+ assert (dev != NULL);
+ assert (ioaddr != NULL);
+
+ dirty_tx = tp->dirty_tx;
+ tx_left = tp->cur_tx - dirty_tx;
+ while (tx_left > 0) {
+ int entry = dirty_tx % NUM_TX_DESC;
+ int txstatus;
+
+ txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32)));
+
+ if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
+ break; /* It still hasn't been Txed */
+
+ /* Note: TxCarrierLost is always asserted at 100mbps. */
+ if (txstatus & (TxOutOfWindow | TxAborted)) {
+ /* There was an major error, log it. */
+ if (netif_msg_tx_err(tp))
+ printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+ dev->name, txstatus);
+ tp->stats.tx_errors++;
+ if (txstatus & TxAborted) {
+ tp->stats.tx_aborted_errors++;
+ RTL_W32 (TxConfig, TxClearAbt);
+ RTL_W16 (IntrStatus, TxErr);
+ wmb();
+ }
+ if (txstatus & TxCarrierLost)
+ tp->stats.tx_carrier_errors++;
+ if (txstatus & TxOutOfWindow)
+ tp->stats.tx_window_errors++;
+ } else {
+ if (txstatus & TxUnderrun) {
+ /* Add 64 to the Tx FIFO threshold. */
+ if (tp->tx_flag < 0x00300000)
+ tp->tx_flag += 0x00020000;
+ tp->stats.tx_fifo_errors++;
+ }
+ tp->stats.collisions += (txstatus >> 24) & 15;
+ tp->stats.tx_bytes += txstatus & 0x7ff;
+ tp->stats.tx_packets++;
+ }
+
+ dirty_tx++;
+ tx_left--;
+ }
+
+#ifndef RTL8139_NDEBUG
+ if (tp->cur_tx - dirty_tx > NUM_TX_DESC) {
+ printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n",
+ dev->name, dirty_tx, tp->cur_tx);
+ dirty_tx += NUM_TX_DESC;
+ }
+#endif /* RTL8139_NDEBUG */
+
+ /* only wake the queue if we did work, and the queue is stopped */
+ if (tp->dirty_tx != dirty_tx) {
+ tp->dirty_tx = dirty_tx;
+ mb();
+ netif_wake_queue (dev);
+ }
+}
+
+
+/* TODO: clean this up! Rx reset need not be this intensive */
+static void rtl8139_rx_err (u32 rx_status, struct net_device *dev,
+ struct rtl8139_private *tp, void __iomem *ioaddr)
+{
+ u8 tmp8;
+#ifdef CONFIG_8139_OLD_RX_RESET
+ int tmp_work;
+#endif
+
+ if (netif_msg_rx_err (tp))
+ printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n",
+ dev->name, rx_status);
+ tp->stats.rx_errors++;
+ if (!(rx_status & RxStatusOK)) {
+ if (rx_status & RxTooLong) {
+ DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
+ dev->name, rx_status);
+ /* A.C.: The chip hangs here. */
+ }
+ if (rx_status & (RxBadSymbol | RxBadAlign))
+ tp->stats.rx_frame_errors++;
+ if (rx_status & (RxRunt | RxTooLong))
+ tp->stats.rx_length_errors++;
+ if (rx_status & RxCRCErr)
+ tp->stats.rx_crc_errors++;
+ } else {
+ tp->xstats.rx_lost_in_ring++;
+ }
+
+#ifndef CONFIG_8139_OLD_RX_RESET
+ tmp8 = RTL_R8 (ChipCmd);
+ RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb);
+ RTL_W8 (ChipCmd, tmp8);
+ RTL_W32 (RxConfig, tp->rx_config);
+ tp->cur_rx = 0;
+#else
+ /* Reset the receiver, based on RealTek recommendation. (Bug?) */
+
+ /* disable receive */
+ RTL_W8_F (ChipCmd, CmdTxEnb);
+ tmp_work = 200;
+ while (--tmp_work > 0) {
+ udelay(1);
+ tmp8 = RTL_R8 (ChipCmd);
+ if (!(tmp8 & CmdRxEnb))
+ break;
+ }
+ if (tmp_work <= 0)
+ printk (KERN_WARNING PFX "rx stop wait too long\n");
+ /* restart receive */
+ tmp_work = 200;
+ while (--tmp_work > 0) {
+ RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb);
+ udelay(1);
+ tmp8 = RTL_R8 (ChipCmd);
+ if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
+ break;
+ }
+ if (tmp_work <= 0)
+ printk (KERN_WARNING PFX "tx/rx enable wait too long\n");
+
+ /* and reinitialize all rx related registers */
+ RTL_W8_F (Cfg9346, Cfg9346_Unlock);
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
+
+ tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
+ RTL_W32 (RxConfig, tp->rx_config);
+ tp->cur_rx = 0;
+
+ DPRINTK("init buffer addresses\n");
+
+ /* Lock Config[01234] and BMCR register writes */
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ /* init Rx ring buffer DMA address */
+ RTL_W32_F (RxBuf, tp->rx_ring_dma);
+
+ /* A.C.: Reset the multicast list. */
+ __set_rx_mode (dev);
+#endif
+}
+
+#if RX_BUF_IDX == 3
+static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring,
+ u32 offset, unsigned int size)
+{
+ u32 left = RX_BUF_LEN - offset;
+
+ if (size > left) {
+ memcpy(skb->data, ring + offset, left);
+ memcpy(skb->data+left, ring, size - left);
+ } else
+ memcpy(skb->data, ring + offset, size);
+}
+#endif
+
+static void rtl8139_isr_ack(struct rtl8139_private *tp)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 status;
+
+ status = RTL_R16 (IntrStatus) & RxAckBits;
+
+ /* Clear out errors and receive interrupts */
+ if (likely(status != 0)) {
+ if (unlikely(status & (RxFIFOOver | RxOverflow))) {
+ tp->stats.rx_errors++;
+ if (status & RxFIFOOver)
+ tp->stats.rx_fifo_errors++;
+ }
+ RTL_W16_F (IntrStatus, RxAckBits);
+ }
+}
+
+static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
+ int budget)
+{
+ void __iomem *ioaddr = tp->mmio_addr;
+ int received = 0;
+ unsigned char *rx_ring = tp->rx_ring;
+ unsigned int cur_rx = tp->cur_rx;
+ unsigned int rx_size = 0;
+
+ DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+ " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx,
+ RTL_R16 (RxBufAddr),
+ RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+
+ while (netif_running(dev) && received < budget
+ && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
+ u32 ring_offset = cur_rx % RX_BUF_LEN;
+ u32 rx_status;
+ unsigned int pkt_size;
+ struct sk_buff *skb;
+
+ rmb();
+
+ /* read size+status of next frame from DMA ring buffer */
+ rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
+ rx_size = rx_status >> 16;
+ pkt_size = rx_size - 4;
+
+ if (netif_msg_rx_status(tp))
+ printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x,"
+ " cur %4.4x.\n", dev->name, rx_status,
+ rx_size, cur_rx);
+#if RTL8139_DEBUG > 2
+ {
+ int i;
+ DPRINTK ("%s: Frame contents ", dev->name);
+ for (i = 0; i < 70; i++)
+ printk (" %2.2x",
+ rx_ring[ring_offset + i]);
+ printk (".\n");
+ }
+#endif
+
+ /* Packet copy from FIFO still in progress.
+ * Theoretically, this should never happen
+ * since EarlyRx is disabled.
+ */
+ if (unlikely(rx_size == 0xfff0)) {
+ if (!tp->fifo_copy_timeout)
+ tp->fifo_copy_timeout = jiffies + 2;
+ else if (time_after(jiffies, tp->fifo_copy_timeout)) {
+ DPRINTK ("%s: hung FIFO. Reset.", dev->name);
+ rx_size = 0;
+ goto no_early_rx;
+ }
+ if (netif_msg_intr(tp)) {
+ printk(KERN_DEBUG "%s: fifo copy in progress.",
+ dev->name);
+ }
+ tp->xstats.early_rx++;
+ break;
+ }
+
+no_early_rx:
+ tp->fifo_copy_timeout = 0;
+
+ /* If Rx err or invalid rx_size/rx_status received
+ * (which happens if we get lost in the ring),
+ * Rx process gets reset, so we abort any further
+ * Rx processing.
+ */
+ if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
+ (rx_size < 8) ||
+ (!(rx_status & RxStatusOK)))) {
+ rtl8139_rx_err (rx_status, dev, tp, ioaddr);
+ received = -1;
+ goto out;
+ }
+
+ /* Malloc up new buffer, compatible with net-2e. */
+ /* Omit the four octet CRC from the length. */
+
+ skb = dev_alloc_skb (pkt_size + 2);
+ if (likely(skb)) {
+ skb->dev = dev;
+ skb_reserve (skb, 2); /* 16 byte align the IP fields. */
+#if RX_BUF_IDX == 3
+ wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
+#else
+ eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
+#endif
+ skb_put (skb, pkt_size);
+
+ skb->protocol = eth_type_trans (skb, dev);
+
+ dev->last_rx = jiffies;
+ tp->stats.rx_bytes += pkt_size;
+ tp->stats.rx_packets++;
+
+ netif_receive_skb (skb);
+ } else {
+ if (net_ratelimit())
+ printk (KERN_WARNING
+ "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ tp->stats.rx_dropped++;
+ }
+ received++;
+
+ cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
+ RTL_W16 (RxBufPtr, (u16) (cur_rx - 16));
+
+ rtl8139_isr_ack(tp);
+ }
+
+ if (unlikely(!received || rx_size == 0xfff0))
+ rtl8139_isr_ack(tp);
+
+#if RTL8139_DEBUG > 1
+ DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
+ " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
+ RTL_R16 (RxBufAddr),
+ RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
+#endif
+
+ tp->cur_rx = cur_rx;
+
+ /*
+ * The receive buffer should be mostly empty.
+ * Tell NAPI to reenable the Rx irq.
+ */
+ if (tp->fifo_copy_timeout)
+ received = budget;
+
+out:
+ return received;
+}
+
+
+static void rtl8139_weird_interrupt (struct net_device *dev,
+ struct rtl8139_private *tp,
+ void __iomem *ioaddr,
+ int status, int link_changed)
+{
+ DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",
+ dev->name, status);
+
+ assert (dev != NULL);
+ assert (tp != NULL);
+ assert (ioaddr != NULL);
+
+ /* Update the error count. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ if ((status & RxUnderrun) && link_changed &&
+ (tp->drv_flags & HAS_LNK_CHNG)) {
+ rtl_check_media(dev, 0);
+ status &= ~RxUnderrun;
+ }
+
+ if (status & (RxUnderrun | RxErr))
+ tp->stats.rx_errors++;
+
+ if (status & PCSTimeout)
+ tp->stats.rx_length_errors++;
+ if (status & RxUnderrun)
+ tp->stats.rx_fifo_errors++;
+ if (status & PCIErr) {
+ u16 pci_cmd_status;
+ pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status);
+ pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status);
+
+ printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",
+ dev->name, pci_cmd_status);
+ }
+}
+
+static int rtl8139_poll(struct net_device *dev, int *budget)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ int orig_budget = min(*budget, dev->quota);
+ int done = 1;
+
+ spin_lock(&tp->rx_lock);
+ if (likely(RTL_R16(IntrStatus) & RxAckBits)) {
+ int work_done;
+
+ work_done = rtl8139_rx(dev, tp, orig_budget);
+ if (likely(work_done > 0)) {
+ *budget -= work_done;
+ dev->quota -= work_done;
+ done = (work_done < orig_budget);
+ }
+ }
+
+ if (done) {
+ /*
+ * Order is important since data can get interrupted
+ * again when we think we are done.
+ */
+ local_irq_disable();
+ RTL_W16_F(IntrMask, rtl8139_intr_mask);
+ __netif_rx_complete(dev);
+ local_irq_enable();
+ }
+ spin_unlock(&tp->rx_lock);
+
+ return !done;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_instance;
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 status, ackstat;
+ int link_changed = 0; /* avoid bogus "uninit" warning */
+ int handled = 0;
+
+ spin_lock (&tp->lock);
+ status = RTL_R16 (IntrStatus);
+
+ /* shared irq? */
+ if (unlikely((status & rtl8139_intr_mask) == 0))
+ goto out;
+
+ handled = 1;
+
+ /* h/w no longer present (hotplug?) or major error, bail */
+ if (unlikely(status == 0xFFFF))
+ goto out;
+
+ /* close possible race's with dev_close */
+ if (unlikely(!netif_running(dev))) {
+ RTL_W16 (IntrMask, 0);
+ goto out;
+ }
+
+ /* Acknowledge all of the current interrupt sources ASAP, but
+ an first get an additional status bit from CSCR. */
+ if (unlikely(status & RxUnderrun))
+ link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
+
+ ackstat = status & ~(RxAckBits | TxErr);
+ if (ackstat)
+ RTL_W16 (IntrStatus, ackstat);
+
+ /* Receive packets are processed by poll routine.
+ If not running start it now. */
+ if (status & RxAckBits){
+ if (netif_rx_schedule_prep(dev)) {
+ RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
+ __netif_rx_schedule (dev);
+ }
+ }
+
+ /* Check uncommon events with one test. */
+ if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr)))
+ rtl8139_weird_interrupt (dev, tp, ioaddr,
+ status, link_changed);
+
+ if (status & (TxOK | TxErr)) {
+ rtl8139_tx_interrupt (dev, tp, ioaddr);
+ if (status & TxErr)
+ RTL_W16 (IntrStatus, TxErr);
+ }
+ out:
+ spin_unlock (&tp->lock);
+
+ DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
+ dev->name, RTL_R16 (IntrStatus));
+ return IRQ_RETVAL(handled);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void rtl8139_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ rtl8139_interrupt(dev->irq, dev, NULL);
+ enable_irq(dev->irq);
+}
+#endif
+
+static int rtl8139_close (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ netif_stop_queue (dev);
+
+ rtl8139_stop_thread(tp);
+
+ if (netif_msg_ifdown(tp))
+ printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n",
+ dev->name, RTL_R16 (IntrStatus));
+
+ spin_lock_irqsave (&tp->lock, flags);
+
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8 (ChipCmd, 0);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16 (IntrMask, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ synchronize_irq (dev->irq); /* racy, but that's ok here */
+ free_irq (dev->irq, dev);
+
+ rtl8139_tx_clear (tp);
+
+ pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
+ tp->rx_ring, tp->rx_ring_dma);
+ pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
+ tp->tx_bufs, tp->tx_bufs_dma);
+ tp->rx_ring = NULL;
+ tp->tx_bufs = NULL;
+
+ /* Green! Put the chip in low-power mode. */
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+
+ if (rtl_chip_info[tp->chipset].flags & HasHltClk)
+ RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
+
+ return 0;
+}
+
+
+/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to
+ kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and
+ other threads or interrupts aren't messing with the 8139. */
+static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->mmio_addr;
+
+ spin_lock_irq(&np->lock);
+ if (rtl_chip_info[np->chipset].flags & HasLWake) {
+ u8 cfg3 = RTL_R8 (Config3);
+ u8 cfg5 = RTL_R8 (Config5);
+
+ wol->supported = WAKE_PHY | WAKE_MAGIC
+ | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST;
+
+ wol->wolopts = 0;
+ if (cfg3 & Cfg3_LinkUp)
+ wol->wolopts |= WAKE_PHY;
+ if (cfg3 & Cfg3_Magic)
+ wol->wolopts |= WAKE_MAGIC;
+ /* (KON)FIXME: See how netdev_set_wol() handles the
+ following constants. */
+ if (cfg5 & Cfg5_UWF)
+ wol->wolopts |= WAKE_UCAST;
+ if (cfg5 & Cfg5_MWF)
+ wol->wolopts |= WAKE_MCAST;
+ if (cfg5 & Cfg5_BWF)
+ wol->wolopts |= WAKE_BCAST;
+ }
+ spin_unlock_irq(&np->lock);
+}
+
+
+/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes
+ that wol points to kernel memory and other threads or interrupts
+ aren't messing with the 8139. */
+static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->mmio_addr;
+ u32 support;
+ u8 cfg3, cfg5;
+
+ support = ((rtl_chip_info[np->chipset].flags & HasLWake)
+ ? (WAKE_PHY | WAKE_MAGIC
+ | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)
+ : 0);
+ if (wol->wolopts & ~support)
+ return -EINVAL;
+
+ spin_lock_irq(&np->lock);
+ cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic);
+ if (wol->wolopts & WAKE_PHY)
+ cfg3 |= Cfg3_LinkUp;
+ if (wol->wolopts & WAKE_MAGIC)
+ cfg3 |= Cfg3_Magic;
+ RTL_W8 (Cfg9346, Cfg9346_Unlock);
+ RTL_W8 (Config3, cfg3);
+ RTL_W8 (Cfg9346, Cfg9346_Lock);
+
+ cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF);
+ /* (KON)FIXME: These are untested. We may have to set the
+ CRC0, Wakeup0 and LSBCRC0 registers too, but I have no
+ documentation. */
+ if (wol->wolopts & WAKE_UCAST)
+ cfg5 |= Cfg5_UWF;
+ if (wol->wolopts & WAKE_MCAST)
+ cfg5 |= Cfg5_MWF;
+ if (wol->wolopts & WAKE_BCAST)
+ cfg5 |= Cfg5_BWF;
+ RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */
+ spin_unlock_irq(&np->lock);
+
+ return 0;
+}
+
+static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, pci_name(np->pci_dev));
+ info->regdump_len = np->regs_len;
+}
+
+static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ spin_lock_irq(&np->lock);
+ mii_ethtool_gset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+ return 0;
+}
+
+static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ int rc;
+ spin_lock_irq(&np->lock);
+ rc = mii_ethtool_sset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+ return rc;
+}
+
+static int rtl8139_nway_reset(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return mii_nway_restart(&np->mii);
+}
+
+static u32 rtl8139_get_link(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return mii_link_ok(&np->mii);
+}
+
+static u32 rtl8139_get_msglevel(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return np->msg_enable;
+}
+
+static void rtl8139_set_msglevel(struct net_device *dev, u32 datum)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ np->msg_enable = datum;
+}
+
+/* TODO: we are too slack to do reg dumping for pio, for now */
+#ifdef CONFIG_8139TOO_PIO
+#define rtl8139_get_regs_len NULL
+#define rtl8139_get_regs NULL
+#else
+static int rtl8139_get_regs_len(struct net_device *dev)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ return np->regs_len;
+}
+
+static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+
+ regs->version = RTL_REGS_VER;
+
+ spin_lock_irq(&np->lock);
+ memcpy_fromio(regbuf, np->mmio_addr, regs->len);
+ spin_unlock_irq(&np->lock);
+}
+#endif /* CONFIG_8139TOO_MMIO */
+
+static int rtl8139_get_stats_count(struct net_device *dev)
+{
+ return RTL_NUM_STATS;
+}
+
+static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+
+ data[0] = np->xstats.early_rx;
+ data[1] = np->xstats.tx_buf_mapped;
+ data[2] = np->xstats.tx_timeouts;
+ data[3] = np->xstats.rx_lost_in_ring;
+}
+
+static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys));
+}
+
+static struct ethtool_ops rtl8139_ethtool_ops = {
+ .get_drvinfo = rtl8139_get_drvinfo,
+ .get_settings = rtl8139_get_settings,
+ .set_settings = rtl8139_set_settings,
+ .get_regs_len = rtl8139_get_regs_len,
+ .get_regs = rtl8139_get_regs,
+ .nway_reset = rtl8139_nway_reset,
+ .get_link = rtl8139_get_link,
+ .get_msglevel = rtl8139_get_msglevel,
+ .set_msglevel = rtl8139_set_msglevel,
+ .get_wol = rtl8139_get_wol,
+ .set_wol = rtl8139_set_wol,
+ .get_strings = rtl8139_get_strings,
+ .get_stats_count = rtl8139_get_stats_count,
+ .get_ethtool_stats = rtl8139_get_ethtool_stats,
+ .get_perm_addr = ethtool_op_get_perm_addr,
+};
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct rtl8139_private *np = netdev_priv(dev);
+ int rc;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ spin_lock_irq(&np->lock);
+ rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
+ spin_unlock_irq(&np->lock);
+
+ return rc;
+}
+
+
+static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ if (netif_running(dev)) {
+ spin_lock_irqsave (&tp->lock, flags);
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+ spin_unlock_irqrestore (&tp->lock, flags);
+ }
+
+ return &tp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ This routine is not state sensitive and need not be SMP locked. */
+
+static void __set_rx_mode (struct net_device *dev)
+{
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u32 mc_filter[2]; /* Multicast hash filter */
+ int i, rx_mode;
+ u32 tmp;
+
+ DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n",
+ dev->name, dev->flags, RTL_R32 (RxConfig));
+
+ /* Note: do not reorder, GCC is clever about common statements. */
+ if (dev->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n",
+ dev->name);
+ rx_mode =
+ AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
+ AcceptAllPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else if ((dev->mc_count > multicast_filter_limit)
+ || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+ } else {
+ struct dev_mc_list *mclist;
+ rx_mode = AcceptBroadcast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0;
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
+
+ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
+ rx_mode |= AcceptMulticast;
+ }
+ }
+
+ /* We can safely update without stopping the chip. */
+ tmp = rtl8139_rx_config | rx_mode;
+ if (tp->rx_config != tmp) {
+ RTL_W32_F (RxConfig, tmp);
+ tp->rx_config = tmp;
+ }
+ RTL_W32_F (MAR0 + 0, mc_filter[0]);
+ RTL_W32_F (MAR0 + 4, mc_filter[1]);
+}
+
+static void rtl8139_set_rx_mode (struct net_device *dev)
+{
+ unsigned long flags;
+ struct rtl8139_private *tp = netdev_priv(dev);
+
+ spin_lock_irqsave (&tp->lock, flags);
+ __set_rx_mode(dev);
+ spin_unlock_irqrestore (&tp->lock, flags);
+}
+
+#ifdef CONFIG_PM
+
+static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+ struct rtl8139_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ unsigned long flags;
+
+ pci_save_state (pdev);
+
+ if (!netif_running (dev))
+ return 0;
+
+ netif_device_detach (dev);
+
+ spin_lock_irqsave (&tp->lock, flags);
+
+ /* Disable interrupts, stop Tx and Rx. */
+ RTL_W16 (IntrMask, 0);
+ RTL_W8 (ChipCmd, 0);
+
+ /* Update the error counts. */
+ tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
+ RTL_W32 (RxMissed, 0);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
+
+ pci_set_power_state (pdev, PCI_D3hot);
+
+ return 0;
+}
+
+
+static int rtl8139_resume (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata (pdev);
+
+ pci_restore_state (pdev);
+ if (!netif_running (dev))
+ return 0;
+ pci_set_power_state (pdev, PCI_D0);
+ rtl8139_init_ring (dev);
+ rtl8139_hw_start (dev);
+ netif_device_attach (dev);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver rtl8139_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = rtl8139_pci_tbl,
+ .probe = rtl8139_init_one,
+ .remove = __devexit_p(rtl8139_remove_one),
+#ifdef CONFIG_PM
+ .suspend = rtl8139_suspend,
+ .resume = rtl8139_resume,
+#endif /* CONFIG_PM */
+};
+
+
+static int __init rtl8139_init_module (void)
+{
+ /* when we're a module, we always print a version message,
+ * even if no 8139 board is found.
+ */
+#ifdef MODULE
+ printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
+#endif
+
+ return pci_module_init (&rtl8139_pci_driver);
+}
+
+
+static void __exit rtl8139_cleanup_module (void)
+{
+ pci_unregister_driver (&rtl8139_pci_driver);
+}
+
+
+module_init(rtl8139_init_module);
+module_exit(rtl8139_cleanup_module);
--- a/devices/8139too.c Fri Oct 13 10:07:10 2006 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2965 +0,0 @@
-/******************************************************************************
- *
- * $Id$
- *
- * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
- *
- * This file is part of the IgH EtherCAT Master.
- *
- * The IgH EtherCAT Master 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.
- *
- * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * The right to use EtherCAT Technology is granted and comes free of
- * charge under condition of compatibility of product made by
- * Licensee. People intending to distribute/sell products based on the
- * code, have to sign an agreement to guarantee that products using
- * software based on IgH EtherCAT master stay compatible with the actual
- * EtherCAT specification (which are released themselves as an open
- * standard) as the (only) precondition to have the right to use EtherCAT
- * Technology, IP and trade marks.
- *
- *****************************************************************************/
-
-/**
- \file
- EtherCAT driver for RTL8139-compatible NICs.
-*/
-
-/*****************************************************************************/
-
-/*
- Former documentation:
-
- 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux.
-
- Maintained by Jeff Garzik <jgarzik@pobox.com>
- Copyright 2000-2002 Jeff Garzik
-
- Much code comes from Donald Becker's rtl8139.c driver,
- versions 1.13 and older. This driver was originally based
- on rtl8139.c version 1.07. Header of rtl8139.c version 1.13:
-
- -----<snip>-----
-
- Written 1997-2001 by Donald Becker.
- This software may be used and distributed according to the
- terms of the GNU General Public License (GPL), incorporated
- herein by reference. Drivers based on or derived from this
- code fall under the GPL and must retain the authorship,
- copyright and license notice. This file is not a complete
- program and may only be used when the entire operating
- system is licensed under the GPL.
-
- This driver is for boards based on the RTL8129 and RTL8139
- PCI ethernet chips.
-
- The author may be reached as becker@scyld.com, or C/O Scyld
- Computing Corporation 410 Severn Ave., Suite 210 Annapolis
- MD 21403
-
- Support and updates available at
- http://www.scyld.com/network/rtl8139.html
-
- Twister-tuning table provided by Kinston
- <shangh@realtek.com.tw>.
-
- -----<snip>-----
-
- This software may be used and distributed according to the terms
- of the GNU General Public License, incorporated herein by reference.
-
- Contributors:
-
- Donald Becker - he wrote the original driver, kudos to him!
- (but please don't e-mail him for support, this isn't his driver)
-
- Tigran Aivazian - bug fixes, skbuff free cleanup
-
- Martin Mares - suggestions for PCI cleanup
-
- David S. Miller - PCI DMA and softnet updates
-
- Ernst Gill - fixes ported from BSD driver
-
- Daniel Kobras - identified specific locations of
- posted MMIO write bugginess
-
- Gerard Sharp - bug fix, testing and feedback
-
- David Ford - Rx ring wrap fix
-
- Dan DeMaggio - swapped RTL8139 cards with me, and allowed me
- to find and fix a crucial bug on older chipsets.
-
- Donald Becker/Chris Butterworth/Marcus Westergren -
- Noticed various Rx packet size-related buglets.
-
- Santiago Garcia Mantinan - testing and feedback
-
- Jens David - 2.2.x kernel backports
-
- Martin Dennett - incredibly helpful insight on undocumented
- features of the 8139 chips
-
- Jean-Jacques Michel - bug fix
-
- Tobias Ringström - Rx interrupt status checking suggestion
-
- Andrew Morton - Clear blocked signals, avoid
- buffer overrun setting current->comm.
-
- Kalle Olavi Niemitalo - Wake-on-LAN ioctls
-
- Robert Kuebel - Save kernel thread from dying on any signal.
-
- Submitting bug reports:
-
- "rtl8139-diag -mmmaaavvveefN" output
- enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log
-
-*/
-
-#define DRV_NAME "ec_8139too"
-#define DRV_VERSION "0.9.27"
-
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/compiler.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/rtnetlink.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/mii.h>
-#include <linux/completion.h>
-#include <linux/crc32.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/irq.h>
-
-/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
-#include "ecdev.h"
-#include "../master/globals.h"
-
-#define LIT(X) #X
-#define STR(X) LIT(X)
-
-#define RTL8139_DRIVER_NAME DRV_NAME \
- " EtherCAT-capable Fast Ethernet driver " \
- DRV_VERSION ", master " EC_MASTER_VERSION
-
-/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
-#define PFX DRV_NAME ": "
-
-/* Default Message level */
-#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \
- NETIF_MSG_PROBE | \
- NETIF_MSG_LINK)
-
-
-/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */
-#ifdef CONFIG_8139TOO_PIO
-#define USE_IO_OPS 1
-#endif
-
-/* define to 1, 2 or 3 to enable copious debugging info */
-#define RTL8139_DEBUG 0
-
-/* define to 1 to disable lightweight runtime debugging checks */
-#undef RTL8139_NDEBUG
-
-
-#if RTL8139_DEBUG
-/* note: prints function name for you */
-# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
-#else
-# define DPRINTK(fmt, args...)
-#endif
-
-#ifdef RTL8139_NDEBUG
-# define assert(expr) do {} while (0)
-#else
-# define assert(expr) \
- if(unlikely(!(expr))) { \
- printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
- #expr,__FILE__,__FUNCTION__,__LINE__); \
- }
-#endif
-
-
-/* A few user-configurable values. */
-/* media options */
-#define MAX_UNITS 8
-static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
-
-/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
- The RTL chips use a 64 element hash table based on the Ethernet CRC. */
-static int multicast_filter_limit = 32;
-
-/* bitmapped message enable number */
-static int debug = -1;
-
-/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
-static int ec_device_index = -1;
-static int ec_device_master_index = 0;
-static ec_device_t *rtl_ec_dev;
-struct net_device *rtl_ec_net_dev = NULL;
-
-/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
-/*
- * Receive ring size
- * Warning: 64K ring has hardware issues and may lock up.
- */
-#if defined(CONFIG_SH_DREAMCAST)
-#define RX_BUF_IDX 1 /* 16K ring */
-#else
-#define RX_BUF_IDX 2 /* 32K ring */
-#endif
-#define RX_BUF_LEN (8192 << RX_BUF_IDX)
-#define RX_BUF_PAD 16
-#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
-
-#if RX_BUF_LEN == 65536
-#define RX_BUF_TOT_LEN RX_BUF_LEN
-#else
-#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
-#endif
-
-/* Number of Tx descriptor registers. */
-#define NUM_TX_DESC 4
-
-/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/
-#define MAX_ETH_FRAME_SIZE 1536
-
-/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
-#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE
-#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC)
-
-/* PCI Tuning Parameters
- Threshold is bytes transferred to chip before transmission starts. */
-#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
-
-/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */
-#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */
-#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */
-#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
-#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (6*HZ)
-
-
-enum {
- HAS_MII_XCVR = 0x010000,
- HAS_CHIP_XCVR = 0x020000,
- HAS_LNK_CHNG = 0x040000,
-};
-
-#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */
-#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */
-#define RTL_MIN_IO_SIZE 0x80
-#define RTL8139B_IO_SIZE 256
-
-#define RTL8129_CAPS HAS_MII_XCVR
-#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG
-
-typedef enum {
- RTL8139 = 0,
- RTL8129,
-} board_t;
-
-
-/* indexed by board_t, above */
-static struct {
- const char *name;
- u32 hw_flags;
-} board_info[] __devinitdata = {
- { "RealTek RTL8139", RTL8139_CAPS },
- { "RealTek RTL8129", RTL8129_CAPS },
-};
-
-
-static struct pci_device_id rtl8139_pci_tbl[] = {
- {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
-
-#ifdef CONFIG_SH_SECUREEDGE5410
- /* Bogus 8139 silicon reports 8129 without external PROM :-( */
- {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
-#endif
-#ifdef CONFIG_8139TOO_8129
- {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
-#endif
-
- /* some crazy cards report invalid vendor ids like
- * 0x0001 here. The other ids are valid and constant,
- * so we simply don't match on the main vendor id.
- */
- {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
- {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },
- {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },
-
- {0,}
-};
-
-/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
-/* prevent driver from being loaded automatically */
-//MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
-
-/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
-static struct {
- const char str[ETH_GSTRING_LEN];
-} ethtool_stats_keys[] = {
- { "early_rx" },
- { "tx_buf_mapped" },
- { "tx_timeouts" },
- { "rx_lost_in_ring" },
-};
-
-/* The rest of these values should never change. */
-
-/* Symbolic offsets to registers. */
-enum RTL8139_registers {
- MAC0 = 0, /* Ethernet hardware address. */
- MAR0 = 8, /* Multicast filter. */
- TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */
- TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
- RxBuf = 0x30,
- ChipCmd = 0x37,
- RxBufPtr = 0x38,
- RxBufAddr = 0x3A,
- IntrMask = 0x3C,
- IntrStatus = 0x3E,
- TxConfig = 0x40,
- RxConfig = 0x44,
- Timer = 0x48, /* A general-purpose counter. */
- RxMissed = 0x4C, /* 24 bits valid, write clears. */
- Cfg9346 = 0x50,
- Config0 = 0x51,
- Config1 = 0x52,
- FlashReg = 0x54,
- MediaStatus = 0x58,
- Config3 = 0x59,
- Config4 = 0x5A, /* absent on RTL-8139A */
- HltClk = 0x5B,
- MultiIntr = 0x5C,
- TxSummary = 0x60,
- BasicModeCtrl = 0x62,
- BasicModeStatus = 0x64,
- NWayAdvert = 0x66,
- NWayLPAR = 0x68,
- NWayExpansion = 0x6A,
- /* Undocumented registers, but required for proper operation. */
- FIFOTMS = 0x70, /* FIFO Control and test. */
- CSCR = 0x74, /* Chip Status and Configuration Register. */
- PARA78 = 0x78,
- PARA7c = 0x7c, /* Magic transceiver parameter register. */
- Config5 = 0xD8, /* absent on RTL-8139A */
-};
-
-enum ClearBitMasks {
- MultiIntrClear = 0xF000,
- ChipCmdClear = 0xE2,
- Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
-};
-
-enum ChipCmdBits {
- CmdReset = 0x10,
- CmdRxEnb = 0x08,
- CmdTxEnb = 0x04,
- RxBufEmpty = 0x01,
-};
-
-/* Interrupt register bits, using my own meaningful names. */
-enum IntrStatusBits {
- PCIErr = 0x8000,
- PCSTimeout = 0x4000,
- RxFIFOOver = 0x40,
- RxUnderrun = 0x20,
- RxOverflow = 0x10,
- TxErr = 0x08,
- TxOK = 0x04,
- RxErr = 0x02,
- RxOK = 0x01,
-
- RxAckBits = RxFIFOOver | RxOverflow | RxOK,
-};
-
-enum TxStatusBits {
- TxHostOwns = 0x2000,
- TxUnderrun = 0x4000,
- TxStatOK = 0x8000,
- TxOutOfWindow = 0x20000000,
- TxAborted = 0x40000000,
- TxCarrierLost = 0x80000000,
-};
-enum RxStatusBits {
- RxMulticast = 0x8000,
- RxPhysical = 0x4000,
- RxBroadcast = 0x2000,
- RxBadSymbol = 0x0020,
- RxRunt = 0x0010,
- RxTooLong = 0x0008,
- RxCRCErr = 0x0004,
- RxBadAlign = 0x0002,
- RxStatusOK = 0x0001,
-};
-
-/* Bits in RxConfig. */
-enum rx_mode_bits {
- AcceptErr = 0x20,
- AcceptRunt = 0x10,
- AcceptBroadcast = 0x08,
- AcceptMulticast = 0x04,
- AcceptMyPhys = 0x02,
- AcceptAllPhys = 0x01,
-};
-
-/* Bits in TxConfig. */
-enum tx_config_bits {
-
- /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
- TxIFGShift = 24,
- TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
- TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
- TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
- TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
-
- TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
- TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
- TxClearAbt = (1 << 0), /* Clear abort (WO) */
- TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */
- TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */
-
- TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
-};
-
-/* Bits in Config1 */
-enum Config1Bits {
- Cfg1_PM_Enable = 0x01,
- Cfg1_VPD_Enable = 0x02,
- Cfg1_PIO = 0x04,
- Cfg1_MMIO = 0x08,
- LWAKE = 0x10, /* not on 8139, 8139A */
- Cfg1_Driver_Load = 0x20,
- Cfg1_LED0 = 0x40,
- Cfg1_LED1 = 0x80,
- SLEEP = (1 << 1), /* only on 8139, 8139A */
- PWRDN = (1 << 0), /* only on 8139, 8139A */
-};
-
-/* Bits in Config3 */
-enum Config3Bits {
- Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */
- Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
- Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
- Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */
- Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */
- Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
- Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */
- Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
-};
-
-/* Bits in Config4 */
-enum Config4Bits {
- LWPTN = (1 << 2), /* not on 8139, 8139A */
-};
-
-/* Bits in Config5 */
-enum Config5Bits {
- Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */
- Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */
- Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */
- Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
- Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */
- Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */
- Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */
-};
-
-enum RxConfigBits {
- /* rx fifo threshold */
- RxCfgFIFOShift = 13,
- RxCfgFIFONone = (7 << RxCfgFIFOShift),
-
- /* Max DMA burst */
- RxCfgDMAShift = 8,
- RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
-
- /* rx ring buffer length */
- RxCfgRcv8K = 0,
- RxCfgRcv16K = (1 << 11),
- RxCfgRcv32K = (1 << 12),
- RxCfgRcv64K = (1 << 11) | (1 << 12),
-
- /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
- RxNoWrap = (1 << 7),
-};
-
-/* Twister tuning parameters from RealTek.
- Completely undocumented, but required to tune bad links on some boards. */
-enum CSCRBits {
- CSCR_LinkOKBit = 0x0400,
- CSCR_LinkChangeBit = 0x0800,
- CSCR_LinkStatusBits = 0x0f000,
- CSCR_LinkDownOffCmd = 0x003c0,
- CSCR_LinkDownCmd = 0x0f3c0,
-};
-
-enum Cfg9346Bits {
- Cfg9346_Lock = 0x00,
- Cfg9346_Unlock = 0xC0,
-};
-
-typedef enum {
- CH_8139 = 0,
- CH_8139_K,
- CH_8139A,
- CH_8139A_G,
- CH_8139B,
- CH_8130,
- CH_8139C,
- CH_8100,
- CH_8100B_8139D,
- CH_8101,
-} chip_t;
-
-enum chip_flags {
- HasHltClk = (1 << 0),
- HasLWake = (1 << 1),
-};
-
-#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
- (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
-#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1)
-
-/* directly indexed by chip_t, above */
-const static struct {
- const char *name;
- u32 version; /* from RTL8139C/RTL8139D docs */
- u32 flags;
-} rtl_chip_info[] = {
- { "RTL-8139",
- HW_REVID(1, 0, 0, 0, 0, 0, 0),
- HasHltClk,
- },
-
- { "RTL-8139 rev K",
- HW_REVID(1, 1, 0, 0, 0, 0, 0),
- HasHltClk,
- },
-
- { "RTL-8139A",
- HW_REVID(1, 1, 1, 0, 0, 0, 0),
- HasHltClk, /* XXX undocumented? */
- },
-
- { "RTL-8139A rev G",
- HW_REVID(1, 1, 1, 0, 0, 1, 0),
- HasHltClk, /* XXX undocumented? */
- },
-
- { "RTL-8139B",
- HW_REVID(1, 1, 1, 1, 0, 0, 0),
- HasLWake,
- },
-
- { "RTL-8130",
- HW_REVID(1, 1, 1, 1, 1, 0, 0),
- HasLWake,
- },
-
- { "RTL-8139C",
- HW_REVID(1, 1, 1, 0, 1, 0, 0),
- HasLWake,
- },
-
- { "RTL-8100",
- HW_REVID(1, 1, 1, 1, 0, 1, 0),
- HasLWake,
- },
-
- { "RTL-8100B/8139D",
- HW_REVID(1, 1, 1, 0, 1, 0, 1),
- HasLWake,
- },
-
- { "RTL-8101",
- HW_REVID(1, 1, 1, 0, 1, 1, 1),
- HasLWake,
- },
-};
-
-struct rtl_extra_stats {
- unsigned long early_rx;
- unsigned long tx_buf_mapped;
- unsigned long tx_timeouts;
- unsigned long rx_lost_in_ring;
-};
-
-struct rtl8139_private {
- void __iomem *mmio_addr;
- int drv_flags;
- struct pci_dev *pci_dev;
- u32 msg_enable;
- struct net_device_stats stats;
- unsigned char *rx_ring;
- unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */
- unsigned int tx_flag;
- unsigned long cur_tx;
- unsigned long dirty_tx;
- unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */
- unsigned char *tx_bufs; /* Tx bounce buffer region. */
- dma_addr_t rx_ring_dma;
- dma_addr_t tx_bufs_dma;
- signed char phys[4]; /* MII device addresses. */
- char twistie, twist_row, twist_col; /* Twister tune state. */
- unsigned int default_port:4; /* Last dev->if_port value. */
- spinlock_t lock;
- spinlock_t rx_lock;
- chip_t chipset;
- pid_t thr_pid;
- wait_queue_head_t thr_wait;
- struct completion thr_exited;
- u32 rx_config;
- struct rtl_extra_stats xstats;
- int time_to_die;
- struct mii_if_info mii;
- unsigned int regs_len;
- unsigned long fifo_copy_timeout;
-};
-
-/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
-MODULE_AUTHOR("Florian Pose <fp@igh-essen.com>");
-MODULE_DESCRIPTION("RealTek RTL-8139 EtherCAT driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(EC_MASTER_VERSION);
-
-/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
-module_param(multicast_filter_limit, int, 0);
-module_param_array(media, int, NULL, 0);
-module_param_array(full_duplex, int, NULL, 0);
-module_param(debug, int, 0);
-MODULE_PARM_DESC (debug, "8139too bitmapped message enable number");
-MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses");
-MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps");
-MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)");
-
-/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
-module_param(ec_device_index, int, -1);
-module_param(ec_device_master_index, int, 0);
-MODULE_PARM_DESC(ec_device_index,
- "Index of the device reserved for EtherCAT.");
-MODULE_PARM_DESC(ec_device_master_index,
- "Index of the EtherCAT master to register the device.");
-
-/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
-static int read_eeprom (void __iomem *ioaddr, int location, int addr_len);
-static int rtl8139_open (struct net_device *dev);
-static int mdio_read (struct net_device *dev, int phy_id, int location);
-static void mdio_write (struct net_device *dev, int phy_id, int location,
- int val);
-static void rtl8139_start_thread(struct net_device *dev);
-static void rtl8139_tx_timeout (struct net_device *dev);
-static void rtl8139_init_ring (struct net_device *dev);
-static int rtl8139_start_xmit (struct sk_buff *skb,
- struct net_device *dev);
-int rtl8139_poll(struct net_device *dev, int *budget);
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void rtl8139_poll_controller(struct net_device *dev);
-#endif
-irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
- struct pt_regs *regs);
-static int rtl8139_close (struct net_device *dev);
-static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
-static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
-static void rtl8139_set_rx_mode (struct net_device *dev);
-static void __set_rx_mode (struct net_device *dev);
-static void rtl8139_hw_start (struct net_device *dev);
-static struct ethtool_ops rtl8139_ethtool_ops;
-
-/* write MMIO register, with flush */
-/* Flush avoids rtl8139 bug w/ posted MMIO writes */
-#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0)
-#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0)
-#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0)
-
-
-#define MMIO_FLUSH_AUDIT_COMPLETE 1
-#if MMIO_FLUSH_AUDIT_COMPLETE
-
-/* write MMIO register */
-#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg))
-#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg))
-#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg))
-
-#else
-
-/* write MMIO register, then flush */
-#define RTL_W8 RTL_W8_F
-#define RTL_W16 RTL_W16_F
-#define RTL_W32 RTL_W32_F
-
-#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
-
-/* read MMIO register */
-#define RTL_R8(reg) ioread8 (ioaddr + (reg))
-#define RTL_R16(reg) ioread16 (ioaddr + (reg))
-#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg)))
-
-
-static const u16 rtl8139_intr_mask =
- PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
- TxErr | TxOK | RxErr | RxOK;
-
-static const u16 rtl8139_norx_intr_mask =
- PCIErr | PCSTimeout | RxUnderrun |
- TxErr | TxOK | RxErr ;
-
-#if RX_BUF_IDX == 0
-static const unsigned int rtl8139_rx_config =
- RxCfgRcv8K | RxNoWrap |
- (RX_FIFO_THRESH << RxCfgFIFOShift) |
- (RX_DMA_BURST << RxCfgDMAShift);
-#elif RX_BUF_IDX == 1
-static const unsigned int rtl8139_rx_config =
- RxCfgRcv16K | RxNoWrap |
- (RX_FIFO_THRESH << RxCfgFIFOShift) |
- (RX_DMA_BURST << RxCfgDMAShift);
-#elif RX_BUF_IDX == 2
-static const unsigned int rtl8139_rx_config =
- RxCfgRcv32K | RxNoWrap |
- (RX_FIFO_THRESH << RxCfgFIFOShift) |
- (RX_DMA_BURST << RxCfgDMAShift);
-#elif RX_BUF_IDX == 3
-static const unsigned int rtl8139_rx_config =
- RxCfgRcv64K |
- (RX_FIFO_THRESH << RxCfgFIFOShift) |
- (RX_DMA_BURST << RxCfgDMAShift);
-#else
-#error "Invalid configuration for 8139_RXBUF_IDX"
-#endif
-
-static const unsigned int rtl8139_tx_config =
- TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift);
-
-static void __rtl8139_cleanup_dev (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- struct pci_dev *pdev;
-
- assert (dev != NULL);
- assert (tp->pci_dev != NULL);
- pdev = tp->pci_dev;
-
-#ifdef USE_IO_OPS
- if (tp->mmio_addr)
- ioport_unmap (tp->mmio_addr);
-#else
- if (tp->mmio_addr)
- pci_iounmap (pdev, tp->mmio_addr);
-#endif /* USE_IO_OPS */
-
- /* it's ok to call this even if we have no regions to free */
- pci_release_regions (pdev);
-
- free_netdev(dev);
- pci_set_drvdata (pdev, NULL);
-}
-
-
-static void rtl8139_chip_reset (void __iomem *ioaddr)
-{
- int i;
-
- /* Soft reset the chip. */
- RTL_W8 (ChipCmd, CmdReset);
-
- /* Check that the chip has finished the reset. */
- for (i = 1000; i > 0; i--) {
- barrier();
- if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
- break;
- udelay (10);
- }
-}
-
-
-static int __devinit rtl8139_init_board (struct pci_dev *pdev,
- struct net_device **dev_out)
-{
- void __iomem *ioaddr;
- struct net_device *dev;
- struct rtl8139_private *tp;
- u8 tmp8;
- int rc, disable_dev_on_err = 0;
- unsigned int i;
- unsigned long pio_start, pio_end, pio_flags, pio_len;
- unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
- u32 version;
-
- assert (pdev != NULL);
-
- *dev_out = NULL;
-
- /* dev and priv zeroed in alloc_etherdev */
- dev = alloc_etherdev (sizeof (*tp));
- if (dev == NULL) {
- printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev));
- return -ENOMEM;
- }
- SET_MODULE_OWNER(dev);
- SET_NETDEV_DEV(dev, &pdev->dev);
-
- tp = netdev_priv(dev);
- tp->pci_dev = pdev;
-
- /* enable device (incl. PCI PM wakeup and hotplug setup) */
- rc = pci_enable_device (pdev);
- if (rc)
- goto err_out;
-
- pio_start = pci_resource_start (pdev, 0);
- pio_end = pci_resource_end (pdev, 0);
- pio_flags = pci_resource_flags (pdev, 0);
- pio_len = pci_resource_len (pdev, 0);
-
- mmio_start = pci_resource_start (pdev, 1);
- mmio_end = pci_resource_end (pdev, 1);
- mmio_flags = pci_resource_flags (pdev, 1);
- mmio_len = pci_resource_len (pdev, 1);
-
- /* set this immediately, we need to know before
- * we talk to the chip directly */
- DPRINTK("PIO region size == 0x%02X\n", pio_len);
- DPRINTK("MMIO region size == 0x%02lX\n", mmio_len);
-
-#ifdef USE_IO_OPS
- /* make sure PCI base addr 0 is PIO */
- if (!(pio_flags & IORESOURCE_IO)) {
- printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev));
- rc = -ENODEV;
- goto err_out;
- }
- /* check for weird/broken PCI region reporting */
- if (pio_len < RTL_MIN_IO_SIZE) {
- printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev));
- rc = -ENODEV;
- goto err_out;
- }
-#else
- /* make sure PCI base addr 1 is MMIO */
- if (!(mmio_flags & IORESOURCE_MEM)) {
- printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev));
- rc = -ENODEV;
- goto err_out;
- }
- if (mmio_len < RTL_MIN_IO_SIZE) {
- printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev));
- rc = -ENODEV;
- goto err_out;
- }
-#endif
-
- rc = pci_request_regions (pdev, "8139too");
- if (rc)
- goto err_out;
- disable_dev_on_err = 1;
-
- /* enable PCI bus-mastering */
- pci_set_master (pdev);
-
-#ifdef USE_IO_OPS
- ioaddr = ioport_map(pio_start, pio_len);
- if (!ioaddr) {
- printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev));
- rc = -EIO;
- goto err_out;
- }
- dev->base_addr = pio_start;
- tp->mmio_addr = ioaddr;
- tp->regs_len = pio_len;
-#else
- /* ioremap MMIO region */
- ioaddr = pci_iomap(pdev, 1, 0);
- if (ioaddr == NULL) {
- printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev));
- rc = -EIO;
- goto err_out;
- }
- dev->base_addr = (long) ioaddr;
- tp->mmio_addr = ioaddr;
- tp->regs_len = mmio_len;
-#endif /* USE_IO_OPS */
-
- /* Bring old chips out of low-power mode. */
- RTL_W8 (HltClk, 'R');
-
- /* check for missing/broken hardware */
- if (RTL_R32 (TxConfig) == 0xFFFFFFFF) {
- printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n",
- pci_name(pdev));
- rc = -EIO;
- goto err_out;
- }
-
- /* identify chip attached to board */
- version = RTL_R32 (TxConfig) & HW_REVID_MASK;
- for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++)
- if (version == rtl_chip_info[i].version) {
- tp->chipset = i;
- goto match;
- }
-
- /* if unknown chip, assume array element #0, original RTL-8139 in this case */
- printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n",
- pci_name(pdev));
- printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig));
- tp->chipset = 0;
-
-match:
- DPRINTK ("chipset id (%d) == index %d, '%s'\n",
- version, i, rtl_chip_info[i].name);
-
- if (tp->chipset >= CH_8139B) {
- u8 new_tmp8 = tmp8 = RTL_R8 (Config1);
- DPRINTK("PCI PM wakeup\n");
- if ((rtl_chip_info[tp->chipset].flags & HasLWake) &&
- (tmp8 & LWAKE))
- new_tmp8 &= ~LWAKE;
- new_tmp8 |= Cfg1_PM_Enable;
- if (new_tmp8 != tmp8) {
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W8 (Config1, tmp8);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
- }
- if (rtl_chip_info[tp->chipset].flags & HasLWake) {
- tmp8 = RTL_R8 (Config4);
- if (tmp8 & LWPTN) {
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W8 (Config4, tmp8 & ~LWPTN);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
- }
- }
- } else {
- DPRINTK("Old chip wakeup\n");
- tmp8 = RTL_R8 (Config1);
- tmp8 &= ~(SLEEP | PWRDN);
- RTL_W8 (Config1, tmp8);
- }
-
- rtl8139_chip_reset (ioaddr);
-
- *dev_out = dev;
- return 0;
-
-err_out:
- __rtl8139_cleanup_dev (dev);
- if (disable_dev_on_err)
- pci_disable_device (pdev);
- return rc;
-}
-
-
-static int __devinit rtl8139_init_one (struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- struct net_device *dev = NULL;
- struct rtl8139_private *tp;
- int i, addr_len, option;
- void __iomem *ioaddr;
- static int board_idx = -1;
- u8 pci_rev;
-
- assert (pdev != NULL);
- assert (ent != NULL);
-
- board_idx++;
-
- /* when we're built into the kernel, the driver version message
- * is only printed if at least one 8139 board has been found
- */
-#ifndef MODULE
- {
- static int printed_version;
- if (!printed_version++)
- printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
- }
-#endif
-
- pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev);
-
- if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
- pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) {
- printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n",
- pci_name(pdev), pdev->vendor, pdev->device, pci_rev);
- printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n");
- }
-
- i = rtl8139_init_board (pdev, &dev);
- if (i < 0)
- return i;
-
- assert (dev != NULL);
- tp = netdev_priv(dev);
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (board_idx == ec_device_index) {
- rtl_ec_net_dev = dev;
- strcpy(dev->name, "ec0");
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- ioaddr = tp->mmio_addr;
- assert (ioaddr != NULL);
-
- addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6;
- for (i = 0; i < 3; i++)
- ((u16 *) (dev->dev_addr))[i] =
- le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len));
-
- /* The Rtl8139-specific entries in the device structure. */
- dev->open = rtl8139_open;
- dev->hard_start_xmit = rtl8139_start_xmit;
- dev->poll = rtl8139_poll;
- dev->weight = 64;
- dev->stop = rtl8139_close;
- dev->get_stats = rtl8139_get_stats;
- dev->set_multicast_list = rtl8139_set_rx_mode;
- dev->do_ioctl = netdev_ioctl;
- dev->ethtool_ops = &rtl8139_ethtool_ops;
- dev->tx_timeout = rtl8139_tx_timeout;
- dev->watchdog_timeo = TX_TIMEOUT;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = rtl8139_poll_controller;
-#endif
-
- /* note: the hardware is not capable of sg/csum/highdma, however
- * through the use of skb_copy_and_csum_dev we enable these
- * features
- */
- dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;
-
- dev->irq = pdev->irq;
-
- /* tp zeroed and aligned in alloc_etherdev */
- tp = netdev_priv(dev);
-
- /* note: tp->chipset set in rtl8139_init_board */
- tp->drv_flags = board_info[ent->driver_data].hw_flags;
- tp->mmio_addr = ioaddr;
- tp->msg_enable =
- (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1));
- spin_lock_init (&tp->lock);
- spin_lock_init (&tp->rx_lock);
- init_waitqueue_head (&tp->thr_wait);
- init_completion (&tp->thr_exited);
- tp->mii.dev = dev;
- tp->mii.mdio_read = mdio_read;
- tp->mii.mdio_write = mdio_write;
- tp->mii.phy_id_mask = 0x3f;
- tp->mii.reg_num_mask = 0x1f;
-
- /* dev is fully set up and ready to use now */
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- DPRINTK("about to register device named %s (%p)...\n", dev->name, dev);
- i = register_netdev (dev);
- if (i) goto err_out;
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- pci_set_drvdata (pdev, dev);
-
- printk (KERN_INFO "%s: %s at 0x%lx, "
- "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
- "IRQ %d\n",
- dev->name,
- board_info[ent->driver_data].name,
- dev->base_addr,
- dev->dev_addr[0], dev->dev_addr[1],
- dev->dev_addr[2], dev->dev_addr[3],
- dev->dev_addr[4], dev->dev_addr[5],
- dev->irq);
-
- printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n",
- dev->name, rtl_chip_info[tp->chipset].name);
-
- /* Find the connected MII xcvrs.
- Doing this in open() would allow detecting external xcvrs later, but
- takes too much time. */
-#ifdef CONFIG_8139TOO_8129
- if (tp->drv_flags & HAS_MII_XCVR) {
- int phy, phy_idx = 0;
- for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) {
- int mii_status = mdio_read(dev, phy, 1);
- if (mii_status != 0xffff && mii_status != 0x0000) {
- u16 advertising = mdio_read(dev, phy, 4);
- tp->phys[phy_idx++] = phy;
- printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x "
- "advertising %4.4x.\n",
- dev->name, phy, mii_status, advertising);
- }
- }
- if (phy_idx == 0) {
- printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM "
- "transceiver.\n",
- dev->name);
- tp->phys[0] = 32;
- }
- } else
-#endif
- tp->phys[0] = 32;
- tp->mii.phy_id = tp->phys[0];
-
- /* The lower four bits are the media type. */
- option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx];
- if (option > 0) {
- tp->mii.full_duplex = (option & 0x210) ? 1 : 0;
- tp->default_port = option & 0xFF;
- if (tp->default_port)
- tp->mii.force_media = 1;
- }
- if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0)
- tp->mii.full_duplex = full_duplex[board_idx];
- if (tp->mii.full_duplex) {
- printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
- /* Changing the MII-advertised media because might prevent
- re-connection. */
- tp->mii.force_media = 1;
- }
- if (tp->default_port) {
- printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n",
- (option & 0x20 ? 100 : 10),
- (option & 0x10 ? "full" : "half"));
- mdio_write(dev, tp->phys[0], 0,
- ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */
- ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
- }
-
- /* Put the chip into low-power mode. */
- if (rtl_chip_info[tp->chipset].flags & HasHltClk)
- RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
-
- return 0;
-
-err_out:
- __rtl8139_cleanup_dev (dev);
- pci_disable_device (pdev);
- return i;
-}
-
-
-static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata (pdev);
-
- assert (dev != NULL);
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- unregister_netdev (dev);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- __rtl8139_cleanup_dev (dev);
- pci_disable_device (pdev);
-}
-
-
-/* Serial EEPROM section. */
-
-/* EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
-#define EE_CS 0x08 /* EEPROM chip select. */
-#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
-#define EE_WRITE_0 0x00
-#define EE_WRITE_1 0x02
-#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
-#define EE_ENB (0x80 | EE_CS)
-
-/* Delay between EEPROM clock transitions.
- No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
- */
-
-#define eeprom_delay() RTL_R32(Cfg9346)
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD (5)
-#define EE_READ_CMD (6)
-#define EE_ERASE_CMD (7)
-
-static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len)
-{
- int i;
- unsigned retval = 0;
- int read_cmd = location | (EE_READ_CMD << addr_len);
-
- RTL_W8 (Cfg9346, EE_ENB & ~EE_CS);
- RTL_W8 (Cfg9346, EE_ENB);
- eeprom_delay ();
-
- /* Shift the read command bits out. */
- for (i = 4 + addr_len; i >= 0; i--) {
- int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
- RTL_W8 (Cfg9346, EE_ENB | dataval);
- eeprom_delay ();
- RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK);
- eeprom_delay ();
- }
- RTL_W8 (Cfg9346, EE_ENB);
- eeprom_delay ();
-
- for (i = 16; i > 0; i--) {
- RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK);
- eeprom_delay ();
- retval =
- (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 :
- 0);
- RTL_W8 (Cfg9346, EE_ENB);
- eeprom_delay ();
- }
-
- /* Terminate the EEPROM access. */
- RTL_W8 (Cfg9346, ~EE_CS);
- eeprom_delay ();
-
- return retval;
-}
-
-/* MII serial management: mostly bogus for now. */
-/* Read and write the MII management registers using software-generated
- serial MDIO protocol.
- The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
- met by back-to-back PCI I/O cycles, but we insert a delay to avoid
- "overclocking" issues. */
-#define MDIO_DIR 0x80
-#define MDIO_DATA_OUT 0x04
-#define MDIO_DATA_IN 0x02
-#define MDIO_CLK 0x01
-#define MDIO_WRITE0 (MDIO_DIR)
-#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
-
-#define mdio_delay() RTL_R8(Config4)
-
-
-static char mii_2_8139_map[8] = {
- BasicModeCtrl,
- BasicModeStatus,
- 0,
- 0,
- NWayAdvert,
- NWayLPAR,
- NWayExpansion,
- 0
-};
-
-
-#ifdef CONFIG_8139TOO_8129
-/* Syncronize the MII management interface by shifting 32 one bits out. */
-static void mdio_sync (void __iomem *ioaddr)
-{
- int i;
-
- for (i = 32; i >= 0; i--) {
- RTL_W8 (Config4, MDIO_WRITE1);
- mdio_delay ();
- RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK);
- mdio_delay ();
- }
-}
-#endif
-
-static int mdio_read (struct net_device *dev, int phy_id, int location)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- int retval = 0;
-#ifdef CONFIG_8139TOO_8129
- void __iomem *ioaddr = tp->mmio_addr;
- int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- int i;
-#endif
-
- if (phy_id > 31) { /* Really a 8139. Use internal registers. */
- void __iomem *ioaddr = tp->mmio_addr;
- return location < 8 && mii_2_8139_map[location] ?
- RTL_R16 (mii_2_8139_map[location]) : 0;
- }
-
-#ifdef CONFIG_8139TOO_8129
- mdio_sync (ioaddr);
- /* Shift the read command bits out. */
- for (i = 15; i >= 0; i--) {
- int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
-
- RTL_W8 (Config4, MDIO_DIR | dataval);
- mdio_delay ();
- RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK);
- mdio_delay ();
- }
-
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 19; i > 0; i--) {
- RTL_W8 (Config4, 0);
- mdio_delay ();
- retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0);
- RTL_W8 (Config4, MDIO_CLK);
- mdio_delay ();
- }
-#endif
-
- return (retval >> 1) & 0xffff;
-}
-
-
-static void mdio_write (struct net_device *dev, int phy_id, int location,
- int value)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
-#ifdef CONFIG_8139TOO_8129
- void __iomem *ioaddr = tp->mmio_addr;
- int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
- int i;
-#endif
-
- if (phy_id > 31) { /* Really a 8139. Use internal registers. */
- void __iomem *ioaddr = tp->mmio_addr;
- if (location == 0) {
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W16 (BasicModeCtrl, value);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
- } else if (location < 8 && mii_2_8139_map[location])
- RTL_W16 (mii_2_8139_map[location], value);
- return;
- }
-
-#ifdef CONFIG_8139TOO_8129
- mdio_sync (ioaddr);
-
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval =
- (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
- RTL_W8 (Config4, dataval);
- mdio_delay ();
- RTL_W8 (Config4, dataval | MDIO_CLK);
- mdio_delay ();
- }
- /* Clear out extra bits. */
- for (i = 2; i > 0; i--) {
- RTL_W8 (Config4, 0);
- mdio_delay ();
- RTL_W8 (Config4, MDIO_CLK);
- mdio_delay ();
- }
-#endif
-}
-
-
-static int rtl8139_open (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- int retval;
- void __iomem *ioaddr = tp->mmio_addr;
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- retval = request_irq(dev->irq, rtl8139_interrupt,
- SA_SHIRQ, dev->name, dev);
- if (retval)
- return retval;
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
- &tp->tx_bufs_dma);
- tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
- &tp->rx_ring_dma);
- if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- free_irq(dev->irq, dev);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- if (tp->tx_bufs)
- pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
- tp->tx_bufs, tp->tx_bufs_dma);
- if (tp->rx_ring)
- pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
- tp->rx_ring, tp->rx_ring_dma);
-
- return -ENOMEM;
-
- }
-
- tp->mii.full_duplex = tp->mii.force_media;
- tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
-
- rtl8139_init_ring (dev);
- rtl8139_hw_start (dev);
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- netif_start_queue (dev);
-
- if (netif_msg_ifup(tp))
- printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d"
- " GP Pins %2.2x %s-duplex.\n",
- dev->name, pci_resource_start (tp->pci_dev, 1),
- dev->irq, RTL_R8 (MediaStatus),
- tp->mii.full_duplex ? "full" : "half");
-
- rtl8139_start_thread(dev);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- return 0;
-}
-
-
-static void rtl_check_media (struct net_device *dev, unsigned int init_media)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- if (tp->phys[0] >= 0) {
- mii_check_media(&tp->mii, netif_msg_link(tp), init_media);
- }
- } else {
- void __iomem *ioaddr = tp->mmio_addr;
- uint16_t state = RTL_R16(BasicModeStatus) & BMSR_LSTATUS;
- ecdev_link_state(rtl_ec_dev, state ? 1 : 0);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
-}
-
-/* Start the hardware at open or resume. */
-static void rtl8139_hw_start (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u32 i;
- u8 tmp;
-
- /* Bring old chips out of low-power mode. */
- if (rtl_chip_info[tp->chipset].flags & HasHltClk)
- RTL_W8 (HltClk, 'R');
-
- rtl8139_chip_reset (ioaddr);
-
- /* unlock Config[01234] and BMCR register writes */
- RTL_W8_F (Cfg9346, Cfg9346_Unlock);
- /* Restore our idea of the MAC address. */
- RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
- RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
-
- /* Must enable Tx/Rx before setting transfer thresholds! */
- RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-
- tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
- RTL_W32 (RxConfig, tp->rx_config);
- RTL_W32 (TxConfig, rtl8139_tx_config);
-
- tp->cur_rx = 0;
-
- rtl_check_media (dev, 1);
-
- if (tp->chipset >= CH_8139B) {
- /* Disable magic packet scanning, which is enabled
- * when PM is enabled in Config1. It can be reenabled
- * via ETHTOOL_SWOL if desired. */
- RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic);
- }
-
- DPRINTK("init buffer addresses\n");
-
- /* Lock Config[01234] and BMCR register writes */
- RTL_W8 (Cfg9346, Cfg9346_Lock);
-
- /* init Rx ring buffer DMA address */
- RTL_W32_F (RxBuf, tp->rx_ring_dma);
-
- /* init Tx buffer DMA addresses */
- for (i = 0; i < NUM_TX_DESC; i++)
- RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
-
- RTL_W32 (RxMissed, 0);
-
- rtl8139_set_rx_mode (dev);
-
- /* no early-rx interrupts */
- RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear);
-
- /* make sure RxTx has started */
- tmp = RTL_R8 (ChipCmd);
- if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb)))
- RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- /* Enable all known interrupts by setting the interrupt mask. */
- RTL_W16 (IntrMask, rtl8139_intr_mask);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-}
-
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void rtl8139_init_ring (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- int i;
-
- tp->cur_rx = 0;
- tp->cur_tx = 0;
- tp->dirty_tx = 0;
-
- for (i = 0; i < NUM_TX_DESC; i++)
- tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE];
-}
-
-
-/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */
-static int next_tick = 3 * HZ;
-
-#ifndef CONFIG_8139TOO_TUNE_TWISTER
-static inline void rtl8139_tune_twister (struct net_device *dev,
- struct rtl8139_private *tp) {}
-#else
-enum TwisterParamVals {
- PARA78_default = 0x78fa8388,
- PARA7c_default = 0xcb38de43, /* param[0][3] */
- PARA7c_xxx = 0xcb38de43,
-};
-
-static const unsigned long param[4][4] = {
- {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
- {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
- {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
- {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
-};
-
-static void rtl8139_tune_twister (struct net_device *dev,
- struct rtl8139_private *tp)
-{
- int linkcase;
- void __iomem *ioaddr = tp->mmio_addr;
-
- /* This is a complicated state machine to configure the "twister" for
- impedance/echos based on the cable length.
- All of this is magic and undocumented.
- */
- switch (tp->twistie) {
- case 1:
- if (RTL_R16 (CSCR) & CSCR_LinkOKBit) {
- /* We have link beat, let us tune the twister. */
- RTL_W16 (CSCR, CSCR_LinkDownOffCmd);
- tp->twistie = 2; /* Change to state 2. */
- next_tick = HZ / 10;
- } else {
- /* Just put in some reasonable defaults for when beat returns. */
- RTL_W16 (CSCR, CSCR_LinkDownCmd);
- RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */
- RTL_W32 (PARA78, PARA78_default);
- RTL_W32 (PARA7c, PARA7c_default);
- tp->twistie = 0; /* Bail from future actions. */
- }
- break;
- case 2:
- /* Read how long it took to hear the echo. */
- linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits;
- if (linkcase == 0x7000)
- tp->twist_row = 3;
- else if (linkcase == 0x3000)
- tp->twist_row = 2;
- else if (linkcase == 0x1000)
- tp->twist_row = 1;
- else
- tp->twist_row = 0;
- tp->twist_col = 0;
- tp->twistie = 3; /* Change to state 2. */
- next_tick = HZ / 10;
- break;
- case 3:
- /* Put out four tuning parameters, one per 100msec. */
- if (tp->twist_col == 0)
- RTL_W16 (FIFOTMS, 0);
- RTL_W32 (PARA7c, param[(int) tp->twist_row]
- [(int) tp->twist_col]);
- next_tick = HZ / 10;
- if (++tp->twist_col >= 4) {
- /* For short cables we are done.
- For long cables (row == 3) check for mistune. */
- tp->twistie =
- (tp->twist_row == 3) ? 4 : 0;
- }
- break;
- case 4:
- /* Special case for long cables: check for mistune. */
- if ((RTL_R16 (CSCR) &
- CSCR_LinkStatusBits) == 0x7000) {
- tp->twistie = 0;
- break;
- } else {
- RTL_W32 (PARA7c, 0xfb38de03);
- tp->twistie = 5;
- next_tick = HZ / 10;
- }
- break;
- case 5:
- /* Retune for shorter cable (column 2). */
- RTL_W32 (FIFOTMS, 0x20);
- RTL_W32 (PARA78, PARA78_default);
- RTL_W32 (PARA7c, PARA7c_default);
- RTL_W32 (FIFOTMS, 0x00);
- tp->twist_row = 2;
- tp->twist_col = 0;
- tp->twistie = 3;
- next_tick = HZ / 10;
- break;
-
- default:
- /* do nothing */
- break;
- }
-}
-#endif /* CONFIG_8139TOO_TUNE_TWISTER */
-
-static inline void rtl8139_thread_iter (struct net_device *dev,
- struct rtl8139_private *tp,
- void __iomem *ioaddr)
-{
- int mii_lpa;
-
- mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA);
-
- if (!tp->mii.force_media && mii_lpa != 0xffff) {
- int duplex = (mii_lpa & LPA_100FULL)
- || (mii_lpa & 0x01C0) == 0x0040;
- if (tp->mii.full_duplex != duplex) {
- tp->mii.full_duplex = duplex;
-
- if (mii_lpa) {
- printk (KERN_INFO
- "%s: Setting %s-duplex based on MII #%d link"
- " partner ability of %4.4x.\n",
- dev->name,
- tp->mii.full_duplex ? "full" : "half",
- tp->phys[0], mii_lpa);
- } else {
- printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n",
- dev->name);
- }
-#if 0
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
-#endif
- }
- }
-
- next_tick = HZ * 60;
-
- rtl8139_tune_twister (dev, tp);
-
- DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n",
- dev->name, RTL_R16 (NWayLPAR));
- DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n",
- dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus));
- DPRINTK ("%s: Chip config %2.2x %2.2x.\n",
- dev->name, RTL_R8 (Config0),
- RTL_R8 (Config1));
-}
-
-static int rtl8139_thread (void *data)
-{
- struct net_device *dev = data;
- struct rtl8139_private *tp = netdev_priv(dev);
- unsigned long timeout;
-
- daemonize("%s", dev->name);
- allow_signal(SIGTERM);
-
- while (1) {
- timeout = next_tick;
- do {
- timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout);
- /* make swsusp happy with our thread */
- try_to_freeze();
- } while (!signal_pending (current) && (timeout > 0));
-
- if (signal_pending (current)) {
- flush_signals(current);
- }
-
- if (tp->time_to_die)
- break;
-
- if (rtnl_lock_interruptible ())
- break;
- rtl8139_thread_iter (dev, tp, tp->mmio_addr);
- rtnl_unlock ();
- }
-
- complete_and_exit (&tp->thr_exited, 0);
-}
-
-static void rtl8139_start_thread(struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
-
- tp->thr_pid = -1;
- tp->twistie = 0;
- tp->time_to_die = 0;
- if (tp->chipset == CH_8139_K)
- tp->twistie = 1;
- else if (tp->drv_flags & HAS_LNK_CHNG)
- return;
-
- tp->thr_pid = kernel_thread(rtl8139_thread, dev, CLONE_FS|CLONE_FILES);
- if (tp->thr_pid < 0) {
- printk (KERN_WARNING "%s: unable to start kernel thread\n",
- dev->name);
- }
-}
-
-static inline void rtl8139_tx_clear (struct rtl8139_private *tp)
-{
- tp->cur_tx = 0;
- tp->dirty_tx = 0;
-
- /* XXX account for unsent Tx packets in tp->stats.tx_dropped */
-}
-
-
-static void rtl8139_tx_timeout (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- int i;
- u8 tmp8;
- unsigned long flags;
-
- printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x "
- "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd),
- RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus));
- /* Emit info to figure out what went wrong. */
- printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n",
- dev->name, tp->cur_tx, tp->dirty_tx);
- for (i = 0; i < NUM_TX_DESC; i++)
- printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n",
- dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
- i == tp->dirty_tx % NUM_TX_DESC ?
- " (queue head)" : "");
-
- tp->xstats.tx_timeouts++;
-
- /* disable Tx ASAP, if not already */
- tmp8 = RTL_R8 (ChipCmd);
- if (tmp8 & CmdTxEnb)
- RTL_W8 (ChipCmd, CmdRxEnb);
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- spin_lock(&tp->rx_lock);
- /* Disable interrupts by clearing the interrupt mask. */
- RTL_W16 (IntrMask, 0x0000);
-
- /* Stop a shared interrupt from scavenging while we are. */
- spin_lock_irqsave (&tp->lock, flags);
- rtl8139_tx_clear (tp);
- spin_unlock_irqrestore (&tp->lock, flags);
-
- /* ...and finally, reset everything */
- if (netif_running(dev)) {
- rtl8139_hw_start (dev);
- netif_wake_queue (dev);
- }
- spin_unlock(&tp->rx_lock);
- } else {
- rtl8139_tx_clear (tp);
- rtl8139_hw_start(dev);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-}
-
-
-static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned int entry;
- unsigned int len = skb->len;
-
- /* Calculate the next Tx descriptor entry. */
- entry = tp->cur_tx % NUM_TX_DESC;
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- /* Note: the chip doesn't have auto-pad! */
- if (likely(len < TX_BUF_SIZE)) {
- if (len < ETH_ZLEN)
- memset(tp->tx_buf[entry], 0, ETH_ZLEN);
- skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
- if (dev != rtl_ec_net_dev) {
- dev_kfree_skb(skb);
- }
- } else {
- if (dev != rtl_ec_net_dev) {
- dev_kfree_skb(skb);
- }
- tp->stats.tx_dropped++;
- return 0;
- }
-
- if (dev != rtl_ec_net_dev) {
- spin_lock_irq(&tp->lock);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
- tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
-
- dev->trans_start = jiffies;
-
- tp->cur_tx++;
- wmb();
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
- netif_stop_queue (dev);
- spin_unlock_irq(&tp->lock);
-
- if (netif_msg_tx_queued(tp))
- printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n",
- dev->name, len, entry);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- return 0;
-}
-
-
-static void rtl8139_tx_interrupt (struct net_device *dev,
- struct rtl8139_private *tp,
- void __iomem *ioaddr)
-{
- unsigned long dirty_tx, tx_left;
-
- assert (dev != NULL);
- assert (ioaddr != NULL);
-
- dirty_tx = tp->dirty_tx;
- tx_left = tp->cur_tx - dirty_tx;
- while (tx_left > 0) {
- int entry = dirty_tx % NUM_TX_DESC;
- int txstatus;
-
- txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32)));
-
- if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
- break; /* It still hasn't been Txed */
-
- /* Note: TxCarrierLost is always asserted at 100mbps. */
- if (txstatus & (TxOutOfWindow | TxAborted)) {
- /* There was an major error, log it. */
- if (netif_msg_tx_err(tp))
- printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
- dev->name, txstatus);
- tp->stats.tx_errors++;
- if (txstatus & TxAborted) {
- tp->stats.tx_aborted_errors++;
- RTL_W32 (TxConfig, TxClearAbt);
- RTL_W16 (IntrStatus, TxErr);
- wmb();
- }
- if (txstatus & TxCarrierLost)
- tp->stats.tx_carrier_errors++;
- if (txstatus & TxOutOfWindow)
- tp->stats.tx_window_errors++;
- } else {
- if (txstatus & TxUnderrun) {
- /* Add 64 to the Tx FIFO threshold. */
- if (tp->tx_flag < 0x00300000)
- tp->tx_flag += 0x00020000;
- tp->stats.tx_fifo_errors++;
- }
- tp->stats.collisions += (txstatus >> 24) & 15;
- tp->stats.tx_bytes += txstatus & 0x7ff;
- tp->stats.tx_packets++;
- }
-
- dirty_tx++;
- tx_left--;
- }
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
-#ifndef RTL8139_NDEBUG
- if (dev != rtl_ec_net_dev && tp->cur_tx - dirty_tx > NUM_TX_DESC) {
- printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n",
- dev->name, dirty_tx, tp->cur_tx);
- dirty_tx += NUM_TX_DESC;
- }
-#endif /* RTL8139_NDEBUG */
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- /* only wake the queue if we did work, and the queue is stopped */
- if (tp->dirty_tx != dirty_tx) {
- tp->dirty_tx = dirty_tx;
- mb();
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- netif_wake_queue (dev);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
- }
-}
-
-
-/* TODO: clean this up! Rx reset need not be this intensive */
-static void rtl8139_rx_err (u32 rx_status, struct net_device *dev,
- struct rtl8139_private *tp, void __iomem *ioaddr)
-{
- u8 tmp8;
-#ifdef CONFIG_8139_OLD_RX_RESET
- int tmp_work;
-#endif
-
- if (netif_msg_rx_err (tp))
- printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n",
- dev->name, rx_status);
- tp->stats.rx_errors++;
- if (!(rx_status & RxStatusOK)) {
- if (rx_status & RxTooLong) {
- DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
- dev->name, rx_status);
- /* A.C.: The chip hangs here. */
- }
- if (rx_status & (RxBadSymbol | RxBadAlign))
- tp->stats.rx_frame_errors++;
- if (rx_status & (RxRunt | RxTooLong))
- tp->stats.rx_length_errors++;
- if (rx_status & RxCRCErr)
- tp->stats.rx_crc_errors++;
- } else {
- tp->xstats.rx_lost_in_ring++;
- }
-
-#ifndef CONFIG_8139_OLD_RX_RESET
- tmp8 = RTL_R8 (ChipCmd);
- RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb);
- RTL_W8 (ChipCmd, tmp8);
- RTL_W32 (RxConfig, tp->rx_config);
- tp->cur_rx = 0;
-#else
- /* Reset the receiver, based on RealTek recommendation. (Bug?) */
-
- /* disable receive */
- RTL_W8_F (ChipCmd, CmdTxEnb);
- tmp_work = 200;
- while (--tmp_work > 0) {
- udelay(1);
- tmp8 = RTL_R8 (ChipCmd);
- if (!(tmp8 & CmdRxEnb))
- break;
- }
- if (tmp_work <= 0)
- printk (KERN_WARNING PFX "rx stop wait too long\n");
- /* restart receive */
- tmp_work = 200;
- while (--tmp_work > 0) {
- RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb);
- udelay(1);
- tmp8 = RTL_R8 (ChipCmd);
- if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
- break;
- }
- if (tmp_work <= 0)
- printk (KERN_WARNING PFX "tx/rx enable wait too long\n");
-
- /* and reinitialize all rx related registers */
- RTL_W8_F (Cfg9346, Cfg9346_Unlock);
- /* Must enable Tx/Rx before setting transfer thresholds! */
- RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-
- tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
- RTL_W32 (RxConfig, tp->rx_config);
- tp->cur_rx = 0;
-
- DPRINTK("init buffer addresses\n");
-
- /* Lock Config[01234] and BMCR register writes */
- RTL_W8 (Cfg9346, Cfg9346_Lock);
-
- /* init Rx ring buffer DMA address */
- RTL_W32_F (RxBuf, tp->rx_ring_dma);
-
- /* A.C.: Reset the multicast list. */
- __set_rx_mode (dev);
-#endif
-}
-
-#if RX_BUF_IDX == 3
-static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring,
- u32 offset, unsigned int size)
-{
- u32 left = RX_BUF_LEN - offset;
-
- if (size > left) {
- memcpy(skb->data, ring + offset, left);
- memcpy(skb->data+left, ring, size - left);
- } else
- memcpy(skb->data, ring + offset, size);
-}
-#endif
-
-static void rtl8139_isr_ack(struct rtl8139_private *tp)
-{
- void __iomem *ioaddr = tp->mmio_addr;
- u16 status;
-
- status = RTL_R16 (IntrStatus) & RxAckBits;
-
- /* Clear out errors and receive interrupts */
- if (likely(status != 0)) {
- if (unlikely(status & (RxFIFOOver | RxOverflow))) {
- tp->stats.rx_errors++;
- if (status & RxFIFOOver)
- tp->stats.rx_fifo_errors++;
- }
- RTL_W16_F (IntrStatus, RxAckBits);
- }
-}
-
-static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
- int budget)
-{
- void __iomem *ioaddr = tp->mmio_addr;
- int received = 0;
- unsigned char *rx_ring = tp->rx_ring;
- unsigned int cur_rx = tp->cur_rx;
- unsigned int rx_size = 0;
-
- DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
- " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx,
- RTL_R16 (RxBufAddr),
- RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
-
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- while ((dev == rtl_ec_net_dev || netif_running(dev))
- && received < budget
- && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- u32 ring_offset = cur_rx % RX_BUF_LEN;
- u32 rx_status;
- unsigned int pkt_size;
- struct sk_buff *skb;
-
- rmb();
-
- /* read size+status of next frame from DMA ring buffer */
- rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
- rx_size = rx_status >> 16;
- pkt_size = rx_size - 4;
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- if (netif_msg_rx_status(tp))
- printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x,"
- " cur %4.4x.\n", dev->name, rx_status,
- rx_size, cur_rx);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
-#if RTL8139_DEBUG > 2
- {
- int i;
- DPRINTK ("%s: Frame contents ", dev->name);
- for (i = 0; i < 70; i++)
- printk (" %2.2x",
- rx_ring[ring_offset + i]);
- printk (".\n");
- }
-#endif
-
- /* Packet copy from FIFO still in progress.
- * Theoretically, this should never happen
- * since EarlyRx is disabled.
- */
- if (unlikely(rx_size == 0xfff0)) {
- if (!tp->fifo_copy_timeout)
- tp->fifo_copy_timeout = jiffies + 2;
- else if (time_after(jiffies, tp->fifo_copy_timeout)) {
- DPRINTK ("%s: hung FIFO. Reset.", dev->name);
- rx_size = 0;
- goto no_early_rx;
- }
- if (netif_msg_intr(tp)) {
- printk(KERN_DEBUG "%s: fifo copy in progress.",
- dev->name);
- }
- tp->xstats.early_rx++;
- break;
- }
-
-no_early_rx:
- tp->fifo_copy_timeout = 0;
-
- /* If Rx err or invalid rx_size/rx_status received
- * (which happens if we get lost in the ring),
- * Rx process gets reset, so we abort any further
- * Rx processing.
- */
- if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
- (rx_size < 8) ||
- (!(rx_status & RxStatusOK)))) {
- rtl8139_rx_err (rx_status, dev, tp, ioaddr);
- received = -1;
- goto out;
- }
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- /* Malloc up new buffer, compatible with net-2e. */
- /* Omit the four octet CRC from the length. */
-
- skb = dev_alloc_skb (pkt_size + 2);
- if (likely(skb)) {
- skb->dev = dev;
- skb_reserve (skb, 2); /* 16 byte align the IP fields. */
-#if RX_BUF_IDX == 3
- wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
-#else
- eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
-#endif
- skb_put (skb, pkt_size);
-
- skb->protocol = eth_type_trans (skb, dev);
-
- dev->last_rx = jiffies;
- tp->stats.rx_bytes += pkt_size;
- tp->stats.rx_packets++;
-
- netif_receive_skb (skb);
- } else {
- if (net_ratelimit())
- printk (KERN_WARNING
- "%s: Memory squeeze, dropping packet.\n",
- dev->name);
- tp->stats.rx_dropped++;
- }
- } else {
- ecdev_receive(rtl_ec_dev,
- &rx_ring[ring_offset + 4], pkt_size);
- dev->last_rx = jiffies;
- tp->stats.rx_bytes += pkt_size;
- tp->stats.rx_packets++;
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- received++;
-
- cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
- RTL_W16 (RxBufPtr, (u16) (cur_rx - 16));
-
- rtl8139_isr_ack(tp);
- }
-
- if (unlikely(!received || rx_size == 0xfff0))
- rtl8139_isr_ack(tp);
-
-#if RTL8139_DEBUG > 1
- DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
- " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
- RTL_R16 (RxBufAddr),
- RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
-#endif
-
- tp->cur_rx = cur_rx;
-
- /*
- * The receive buffer should be mostly empty.
- * Tell NAPI to reenable the Rx irq.
- */
- if (tp->fifo_copy_timeout)
- received = budget;
-
-out:
- return received;
-}
-
-
-static void rtl8139_weird_interrupt (struct net_device *dev,
- struct rtl8139_private *tp,
- void __iomem *ioaddr,
- int status, int link_changed)
-{
- DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",
- dev->name, status);
-
- assert (dev != NULL);
- assert (tp != NULL);
- assert (ioaddr != NULL);
-
- /* Update the error count. */
- tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
- RTL_W32 (RxMissed, 0);
-
- if ((status & RxUnderrun) && link_changed &&
- (tp->drv_flags & HAS_LNK_CHNG)) {
- rtl_check_media(dev, 0);
- status &= ~RxUnderrun;
- }
-
- if (status & (RxUnderrun | RxErr))
- tp->stats.rx_errors++;
-
- if (status & PCSTimeout)
- tp->stats.rx_length_errors++;
- if (status & RxUnderrun)
- tp->stats.rx_fifo_errors++;
- if (status & PCIErr) {
- u16 pci_cmd_status;
- pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status);
- pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status);
-
- printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",
- dev->name, pci_cmd_status);
- }
-}
-
-int rtl8139_poll(struct net_device *dev, int *budget)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- int orig_budget = min(*budget, dev->quota);
- int done = 1;
-
- spin_lock(&tp->rx_lock);
- if (likely(RTL_R16(IntrStatus) & RxAckBits)) {
- int work_done;
-
- work_done = rtl8139_rx(dev, tp, orig_budget);
- if (likely(work_done > 0)) {
- *budget -= work_done;
- dev->quota -= work_done;
- done = (work_done < orig_budget);
- }
- }
-
- if (done) {
- /*
- * Order is important since data can get interrupted
- * again when we think we are done.
- */
- local_irq_disable();
- RTL_W16_F(IntrMask, rtl8139_intr_mask);
- __netif_rx_complete(dev);
- local_irq_enable();
- }
- spin_unlock(&tp->rx_lock);
-
- return !done;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
- struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *) dev_instance;
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u16 status, ackstat;
- int link_changed = 0; /* avoid bogus "uninit" warning */
- int handled = 0;
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- spin_lock (&tp->lock);
- status = RTL_R16 (IntrStatus);
-
- /* shared irq? */
- if (unlikely((status & rtl8139_intr_mask) == 0))
- goto out;
- } else {
- status = RTL_R16 (IntrStatus);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- handled = 1;
-
- /* h/w no longer present (hotplug?) or major error, bail */
- if (unlikely(status == 0xFFFF))
- goto out;
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- /* close possible race's with dev_close */
- if (unlikely(!netif_running(dev))) {
- RTL_W16 (IntrMask, 0);
- goto out;
- }
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- /* Acknowledge all of the current interrupt sources ASAP, but
- an first get an additional status bit from CSCR. */
- if (unlikely(status & RxUnderrun))
- link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
-
- ackstat = status & ~(RxAckBits | TxErr);
- if (ackstat)
- RTL_W16 (IntrStatus, ackstat);
-
- /* Receive packets are processed by poll routine.
- If not running start it now. */
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (status & RxAckBits){
- if (dev != rtl_ec_net_dev) {
- /* Mark for polling */
- if (netif_rx_schedule_prep(dev)) {
- RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
- __netif_rx_schedule (dev);
- }
- } else {
- /* EtherCAT device: Just receive all frames */
- rtl8139_rx(dev, tp, 100); // FIXME
- }
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- /* Check uncommon events with one test. */
- if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr)))
- rtl8139_weird_interrupt (dev, tp, ioaddr,
- status, link_changed);
-
- if (status & (TxOK | TxErr)) {
- rtl8139_tx_interrupt (dev, tp, ioaddr);
- if (status & TxErr)
- RTL_W16 (IntrStatus, TxErr);
- }
- out:
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- spin_unlock (&tp->lock);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
- dev->name, RTL_R16 (IntrStatus));
- return IRQ_RETVAL(handled);
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling receive - used by netconsole and other diagnostic tools
- * to allow network i/o with interrupts disabled.
- */
-static void rtl8139_poll_controller(struct net_device *dev)
-{
- disable_irq(dev->irq);
- rtl8139_interrupt(dev->irq, dev, NULL);
- enable_irq(dev->irq);
-}
-#endif
-
-static int rtl8139_close (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- int ret = 0;
- unsigned long flags;
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev != rtl_ec_net_dev) {
- netif_stop_queue (dev);
-
- if (tp->thr_pid >= 0) {
- tp->time_to_die = 1;
- wmb();
- ret = kill_proc (tp->thr_pid, SIGTERM, 1);
- if (ret) {
- printk (KERN_ERR "%s: unable to signal thread\n", dev->name);
- return ret;
- }
- wait_for_completion (&tp->thr_exited);
- }
-
- if (netif_msg_ifdown(tp))
- printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n",
- dev->name, RTL_R16 (IntrStatus));
-
- spin_lock_irqsave (&tp->lock, flags);
-
- /* Stop the chip's Tx and Rx DMA processes. */
- RTL_W8 (ChipCmd, 0);
-
- /* Disable interrupts by clearing the interrupt mask. */
- RTL_W16 (IntrMask, 0);
-
- /* Update the error counts. */
- tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
- RTL_W32 (RxMissed, 0);
-
- spin_unlock_irqrestore (&tp->lock, flags);
-
- synchronize_irq (dev->irq); /* racy, but that's ok here */
- free_irq (dev->irq, dev);
- } else {
- /* Stop the chip's Tx and Rx DMA processes. */
- RTL_W8 (ChipCmd, 0);
-
- /* Disable interrupts by clearing the interrupt mask. */
- RTL_W16 (IntrMask, 0);
-
- /* Update the error counts. */
- tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
- RTL_W32 (RxMissed, 0);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- rtl8139_tx_clear (tp);
-
- pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
- tp->rx_ring, tp->rx_ring_dma);
- pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
- tp->tx_bufs, tp->tx_bufs_dma);
- tp->rx_ring = NULL;
- tp->tx_bufs = NULL;
-
- /* Green! Put the chip in low-power mode. */
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
-
- if (rtl_chip_info[tp->chipset].flags & HasHltClk)
- RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
-
- return 0;
-}
-
-
-/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to
- kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and
- other threads or interrupts aren't messing with the 8139. */
-static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- void __iomem *ioaddr = np->mmio_addr;
-
- spin_lock_irq(&np->lock);
- if (rtl_chip_info[np->chipset].flags & HasLWake) {
- u8 cfg3 = RTL_R8 (Config3);
- u8 cfg5 = RTL_R8 (Config5);
-
- wol->supported = WAKE_PHY | WAKE_MAGIC
- | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST;
-
- wol->wolopts = 0;
- if (cfg3 & Cfg3_LinkUp)
- wol->wolopts |= WAKE_PHY;
- if (cfg3 & Cfg3_Magic)
- wol->wolopts |= WAKE_MAGIC;
- /* (KON)FIXME: See how netdev_set_wol() handles the
- following constants. */
- if (cfg5 & Cfg5_UWF)
- wol->wolopts |= WAKE_UCAST;
- if (cfg5 & Cfg5_MWF)
- wol->wolopts |= WAKE_MCAST;
- if (cfg5 & Cfg5_BWF)
- wol->wolopts |= WAKE_BCAST;
- }
- spin_unlock_irq(&np->lock);
-}
-
-
-/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes
- that wol points to kernel memory and other threads or interrupts
- aren't messing with the 8139. */
-static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- void __iomem *ioaddr = np->mmio_addr;
- u32 support;
- u8 cfg3, cfg5;
-
- support = ((rtl_chip_info[np->chipset].flags & HasLWake)
- ? (WAKE_PHY | WAKE_MAGIC
- | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)
- : 0);
- if (wol->wolopts & ~support)
- return -EINVAL;
-
- spin_lock_irq(&np->lock);
- cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic);
- if (wol->wolopts & WAKE_PHY)
- cfg3 |= Cfg3_LinkUp;
- if (wol->wolopts & WAKE_MAGIC)
- cfg3 |= Cfg3_Magic;
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W8 (Config3, cfg3);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
-
- cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF);
- /* (KON)FIXME: These are untested. We may have to set the
- CRC0, Wakeup0 and LSBCRC0 registers too, but I have no
- documentation. */
- if (wol->wolopts & WAKE_UCAST)
- cfg5 |= Cfg5_UWF;
- if (wol->wolopts & WAKE_MCAST)
- cfg5 |= Cfg5_MWF;
- if (wol->wolopts & WAKE_BCAST)
- cfg5 |= Cfg5_BWF;
- RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */
- spin_unlock_irq(&np->lock);
-
- return 0;
-}
-
-static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- strcpy(info->driver, DRV_NAME);
- strcpy(info->version, DRV_VERSION);
- strcpy(info->bus_info, pci_name(np->pci_dev));
- info->regdump_len = np->regs_len;
-}
-
-static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- spin_lock_irq(&np->lock);
- mii_ethtool_gset(&np->mii, cmd);
- spin_unlock_irq(&np->lock);
- return 0;
-}
-
-static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- int rc;
- spin_lock_irq(&np->lock);
- rc = mii_ethtool_sset(&np->mii, cmd);
- spin_unlock_irq(&np->lock);
- return rc;
-}
-
-static int rtl8139_nway_reset(struct net_device *dev)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- return mii_nway_restart(&np->mii);
-}
-
-static u32 rtl8139_get_link(struct net_device *dev)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- return mii_link_ok(&np->mii);
-}
-
-static u32 rtl8139_get_msglevel(struct net_device *dev)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- return np->msg_enable;
-}
-
-static void rtl8139_set_msglevel(struct net_device *dev, u32 datum)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- np->msg_enable = datum;
-}
-
-/* TODO: we are too slack to do reg dumping for pio, for now */
-#ifdef CONFIG_8139TOO_PIO
-#define rtl8139_get_regs_len NULL
-#define rtl8139_get_regs NULL
-#else
-static int rtl8139_get_regs_len(struct net_device *dev)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- return np->regs_len;
-}
-
-static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
-{
- struct rtl8139_private *np = netdev_priv(dev);
-
- regs->version = RTL_REGS_VER;
-
- spin_lock_irq(&np->lock);
- memcpy_fromio(regbuf, np->mmio_addr, regs->len);
- spin_unlock_irq(&np->lock);
-}
-#endif /* CONFIG_8139TOO_MMIO */
-
-static int rtl8139_get_stats_count(struct net_device *dev)
-{
- return RTL_NUM_STATS;
-}
-
-static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
-{
- struct rtl8139_private *np = netdev_priv(dev);
-
- data[0] = np->xstats.early_rx;
- data[1] = np->xstats.tx_buf_mapped;
- data[2] = np->xstats.tx_timeouts;
- data[3] = np->xstats.rx_lost_in_ring;
-}
-
-static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data)
-{
- memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys));
-}
-
-static struct ethtool_ops rtl8139_ethtool_ops = {
- .get_drvinfo = rtl8139_get_drvinfo,
- .get_settings = rtl8139_get_settings,
- .set_settings = rtl8139_set_settings,
- .get_regs_len = rtl8139_get_regs_len,
- .get_regs = rtl8139_get_regs,
- .nway_reset = rtl8139_nway_reset,
- .get_link = rtl8139_get_link,
- .get_msglevel = rtl8139_get_msglevel,
- .set_msglevel = rtl8139_set_msglevel,
- .get_wol = rtl8139_get_wol,
- .set_wol = rtl8139_set_wol,
- .get_strings = rtl8139_get_strings,
- .get_stats_count = rtl8139_get_stats_count,
- .get_ethtool_stats = rtl8139_get_ethtool_stats,
-};
-
-static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- int rc;
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev == rtl_ec_net_dev || !netif_running(dev))
- return -EINVAL;
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- spin_lock_irq(&np->lock);
- rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
- spin_unlock_irq(&np->lock);
-
- return rc;
-}
-
-
-static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev == rtl_ec_net_dev || netif_running(dev)) {
- spin_lock_irqsave (&tp->lock, flags);
- tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
- RTL_W32 (RxMissed, 0);
- spin_unlock_irqrestore (&tp->lock, flags);
- }
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- return &tp->stats;
-}
-
-/* Set or clear the multicast filter for this adaptor.
- This routine is not state sensitive and need not be SMP locked. */
-
-static void __set_rx_mode (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u32 mc_filter[2]; /* Multicast hash filter */
- int i, rx_mode;
- u32 tmp;
-
- DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n",
- dev->name, dev->flags, RTL_R32 (RxConfig));
-
- /* Note: do not reorder, GCC is clever about common statements. */
- if (dev->flags & IFF_PROMISC) {
- /* Unconditionally log net taps. */
- printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n",
- dev->name);
- rx_mode =
- AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
- AcceptAllPhys;
- mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
- /* Too many to filter perfectly -- accept all multicasts. */
- rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
- mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else {
- struct dev_mc_list *mclist;
- rx_mode = AcceptBroadcast | AcceptMyPhys;
- mc_filter[1] = mc_filter[0] = 0;
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next) {
- int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
-
- mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
- rx_mode |= AcceptMulticast;
- }
- }
-
- /* We can safely update without stopping the chip. */
- tmp = rtl8139_rx_config | rx_mode;
- if (tp->rx_config != tmp) {
- RTL_W32_F (RxConfig, tmp);
- tp->rx_config = tmp;
- }
- RTL_W32_F (MAR0 + 0, mc_filter[0]);
- RTL_W32_F (MAR0 + 4, mc_filter[1]);
-}
-
-static void rtl8139_set_rx_mode (struct net_device *dev)
-{
- unsigned long flags;
- struct rtl8139_private *tp = netdev_priv(dev);
-
- spin_lock_irqsave (&tp->lock, flags);
- __set_rx_mode(dev);
- spin_unlock_irqrestore (&tp->lock, flags);
-}
-
-#ifdef CONFIG_PM
-
-static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state)
-{
- struct net_device *dev = pci_get_drvdata (pdev);
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
-
- pci_save_state (pdev);
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev == rtl_ec_net_dev || !netif_running (dev))
- return 0;
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- netif_device_detach (dev);
-
- spin_lock_irqsave (&tp->lock, flags);
-
- /* Disable interrupts, stop Tx and Rx. */
- RTL_W16 (IntrMask, 0);
- RTL_W8 (ChipCmd, 0);
-
- /* Update the error counts. */
- tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
- RTL_W32 (RxMissed, 0);
-
- spin_unlock_irqrestore (&tp->lock, flags);
-
- pci_set_power_state (pdev, PCI_D3hot);
-
- return 0;
-}
-
-
-static int rtl8139_resume (struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata (pdev);
-
- pci_restore_state (pdev);
-
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- if (dev == rtl_ec_net_dev || !netif_running (dev))
- return 0;
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-
- pci_set_power_state (pdev, PCI_D0);
- rtl8139_init_ring (dev);
- rtl8139_hw_start (dev);
- netif_device_attach (dev);
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-
-static struct pci_driver rtl8139_pci_driver = {
- .name = DRV_NAME,
- .id_table = rtl8139_pci_tbl,
- .probe = rtl8139_init_one,
- .remove = __devexit_p(rtl8139_remove_one),
-#ifdef CONFIG_PM
- .suspend = rtl8139_suspend,
- .resume = rtl8139_resume,
-#endif /* CONFIG_PM */
-};
-
-
-static int __init rtl8139_init_module (void)
-{
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- printk(KERN_INFO RTL8139_DRIVER_NAME "\n");
- printk(KERN_INFO "ec_device_index is %i\n", ec_device_index);
-
- if (pci_module_init(&rtl8139_pci_driver) < 0) {
- printk(KERN_ERR "Failed to init PCI module.\n");
- goto out_return;
- }
-
- if (rtl_ec_net_dev) {
- printk(KERN_INFO "Registering EtherCAT device...\n");
- if (!(rtl_ec_dev = ecdev_register(ec_device_master_index,
- rtl_ec_net_dev, rtl8139_interrupt,
- THIS_MODULE))) {
- printk(KERN_ERR "Failed to register EtherCAT device!\n");
- goto out_pci;
- }
-
- printk(KERN_INFO "Starting EtherCAT device...\n");
- if (ecdev_start(ec_device_master_index)) {
- printk(KERN_ERR "Failed to start EtherCAT device!\n");
- goto out_unregister;
- }
- } else {
- printk(KERN_WARNING "No EtherCAT device registered!\n");
- }
-
- return 0;
-
- out_unregister:
- ecdev_unregister(ec_device_master_index, rtl_ec_dev);
- out_pci:
- pci_unregister_driver(&rtl8139_pci_driver);
- out_return:
- return -1;
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-}
-
-
-static void __exit rtl8139_cleanup_module (void)
-{
- /* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
-
- printk(KERN_INFO "Cleaning up RTL8139-EtherCAT module...\n");
-
- if (rtl_ec_net_dev) {
- printk(KERN_INFO "Stopping device...\n");
- ecdev_stop(ec_device_master_index);
- printk(KERN_INFO "Unregistering device...\n");
- ecdev_unregister(ec_device_master_index, rtl_ec_dev);
- rtl_ec_dev = NULL;
- }
-
- pci_unregister_driver(&rtl8139_pci_driver);
-
- printk(KERN_INFO "RTL8139-EtherCAT module cleaned up.\n");
-
- /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
-}
-
-
-module_init(rtl8139_init_module);
-module_exit(rtl8139_cleanup_module);
--- a/devices/Kbuild Fri Oct 13 10:07:10 2006 +0000
+++ b/devices/Kbuild Tue Nov 07 12:13:30 2006 +0000
@@ -1,9 +1,5 @@
#------------------------------------------------------------------------------
#
-# Kbuild
-#
-# IgH EtherCAT master device modules
-#
# $Id$
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -35,9 +31,13 @@
#
#------------------------------------------------------------------------------
+include $(src)/../config.kbuild
+
+EC_8139TOO_OBJ = 8139too-$(EC_8139TOO_KERNEL)-ethercat.o
+
obj-m := ec_8139too.o
-ec_8139too-objs := 8139too.o
+ec_8139too-objs := $(EC_8139TOO_OBJ)
REV := $(shell if test -s $(src)/../svnrevision; then \
cat $(src)/../svnrevision; \
@@ -45,6 +45,6 @@
svnversion $(src)/.. 2>/dev/null || echo "unknown"; \
fi)
-EXTRA_CFLAGS = -DSVNREV=$(REV)
+CFLAGS_$(EC_8139TOO_OBJ) = -DSVNREV=$(REV)
#------------------------------------------------------------------------------
--- a/devices/Makefile.am Fri Oct 13 10:07:10 2006 +0000
+++ b/devices/Makefile.am Tue Nov 07 12:13:30 2006 +0000
@@ -1,9 +1,5 @@
#------------------------------------------------------------------------------
#
-# Makefile.am
-#
-# IgH EtherCAT master module
-#
# $Id: Makefile 545 2006-09-19 13:28:40Z fp $
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -38,18 +34,18 @@
EXTRA_DIST = \
Kbuild \
ecdev.h \
- 8139too.c \
- original_8139too.c
+ 8139too-2.6.13-ethercat.c \
+ 8139too-2.6.13-orig.c \
+ 8139too-2.6.17-ethercat.c \
+ 8139too-2.6.17-orig.c
-all:
- $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" modules
+modules:
+ $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_top_srcdir@" modules
+
+modules_install:
+ cp $(srcdir)/ec_8139too.ko $(DESTDIR)$(LINUX_MOD_PATH)
clean-local:
$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" clean
-install-data-local:
- $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" \
- INSTALL_MOD_PATH="$(DESTDIR)" \
- INSTALL_MOD_DIR="ethercat" modules_install
-
#------------------------------------------------------------------------------
--- a/devices/original_8139too.c Fri Oct 13 10:07:10 2006 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2646 +0,0 @@
-/*
-
- 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux.
-
- Maintained by Jeff Garzik <jgarzik@pobox.com>
- Copyright 2000-2002 Jeff Garzik
-
- Much code comes from Donald Becker's rtl8139.c driver,
- versions 1.13 and older. This driver was originally based
- on rtl8139.c version 1.07. Header of rtl8139.c version 1.13:
-
- -----<snip>-----
-
- Written 1997-2001 by Donald Becker.
- This software may be used and distributed according to the
- terms of the GNU General Public License (GPL), incorporated
- herein by reference. Drivers based on or derived from this
- code fall under the GPL and must retain the authorship,
- copyright and license notice. This file is not a complete
- program and may only be used when the entire operating
- system is licensed under the GPL.
-
- This driver is for boards based on the RTL8129 and RTL8139
- PCI ethernet chips.
-
- The author may be reached as becker@scyld.com, or C/O Scyld
- Computing Corporation 410 Severn Ave., Suite 210 Annapolis
- MD 21403
-
- Support and updates available at
- http://www.scyld.com/network/rtl8139.html
-
- Twister-tuning table provided by Kinston
- <shangh@realtek.com.tw>.
-
- -----<snip>-----
-
- This software may be used and distributed according to the terms
- of the GNU General Public License, incorporated herein by reference.
-
- Contributors:
-
- Donald Becker - he wrote the original driver, kudos to him!
- (but please don't e-mail him for support, this isn't his driver)
-
- Tigran Aivazian - bug fixes, skbuff free cleanup
-
- Martin Mares - suggestions for PCI cleanup
-
- David S. Miller - PCI DMA and softnet updates
-
- Ernst Gill - fixes ported from BSD driver
-
- Daniel Kobras - identified specific locations of
- posted MMIO write bugginess
-
- Gerard Sharp - bug fix, testing and feedback
-
- David Ford - Rx ring wrap fix
-
- Dan DeMaggio - swapped RTL8139 cards with me, and allowed me
- to find and fix a crucial bug on older chipsets.
-
- Donald Becker/Chris Butterworth/Marcus Westergren -
- Noticed various Rx packet size-related buglets.
-
- Santiago Garcia Mantinan - testing and feedback
-
- Jens David - 2.2.x kernel backports
-
- Martin Dennett - incredibly helpful insight on undocumented
- features of the 8139 chips
-
- Jean-Jacques Michel - bug fix
-
- Tobias Ringström - Rx interrupt status checking suggestion
-
- Andrew Morton - Clear blocked signals, avoid
- buffer overrun setting current->comm.
-
- Kalle Olavi Niemitalo - Wake-on-LAN ioctls
-
- Robert Kuebel - Save kernel thread from dying on any signal.
-
- Submitting bug reports:
-
- "rtl8139-diag -mmmaaavvveefN" output
- enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log
-
-*/
-
-#define DRV_NAME "8139too"
-#define DRV_VERSION "0.9.27"
-
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/compiler.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/rtnetlink.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/mii.h>
-#include <linux/completion.h>
-#include <linux/crc32.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/irq.h>
-
-#define RTL8139_DRIVER_NAME DRV_NAME " Fast Ethernet driver " DRV_VERSION
-#define PFX DRV_NAME ": "
-
-/* Default Message level */
-#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \
- NETIF_MSG_PROBE | \
- NETIF_MSG_LINK)
-
-
-/* enable PIO instead of MMIO, if CONFIG_8139TOO_PIO is selected */
-#ifdef CONFIG_8139TOO_PIO
-#define USE_IO_OPS 1
-#endif
-
-/* define to 1, 2 or 3 to enable copious debugging info */
-#define RTL8139_DEBUG 0
-
-/* define to 1 to disable lightweight runtime debugging checks */
-#undef RTL8139_NDEBUG
-
-
-#if RTL8139_DEBUG
-/* note: prints function name for you */
-# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
-#else
-# define DPRINTK(fmt, args...)
-#endif
-
-#ifdef RTL8139_NDEBUG
-# define assert(expr) do {} while (0)
-#else
-# define assert(expr) \
- if(unlikely(!(expr))) { \
- printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
- #expr,__FILE__,__FUNCTION__,__LINE__); \
- }
-#endif
-
-
-/* A few user-configurable values. */
-/* media options */
-#define MAX_UNITS 8
-static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
-
-/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
- The RTL chips use a 64 element hash table based on the Ethernet CRC. */
-static int multicast_filter_limit = 32;
-
-/* bitmapped message enable number */
-static int debug = -1;
-
-/*
- * Receive ring size
- * Warning: 64K ring has hardware issues and may lock up.
- */
-#if defined(CONFIG_SH_DREAMCAST)
-#define RX_BUF_IDX 1 /* 16K ring */
-#else
-#define RX_BUF_IDX 2 /* 32K ring */
-#endif
-#define RX_BUF_LEN (8192 << RX_BUF_IDX)
-#define RX_BUF_PAD 16
-#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
-
-#if RX_BUF_LEN == 65536
-#define RX_BUF_TOT_LEN RX_BUF_LEN
-#else
-#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
-#endif
-
-/* Number of Tx descriptor registers. */
-#define NUM_TX_DESC 4
-
-/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/
-#define MAX_ETH_FRAME_SIZE 1536
-
-/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
-#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE
-#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC)
-
-/* PCI Tuning Parameters
- Threshold is bytes transferred to chip before transmission starts. */
-#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
-
-/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */
-#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */
-#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */
-#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
-#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (6*HZ)
-
-
-enum {
- HAS_MII_XCVR = 0x010000,
- HAS_CHIP_XCVR = 0x020000,
- HAS_LNK_CHNG = 0x040000,
-};
-
-#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */
-#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */
-#define RTL_MIN_IO_SIZE 0x80
-#define RTL8139B_IO_SIZE 256
-
-#define RTL8129_CAPS HAS_MII_XCVR
-#define RTL8139_CAPS HAS_CHIP_XCVR|HAS_LNK_CHNG
-
-typedef enum {
- RTL8139 = 0,
- RTL8129,
-} board_t;
-
-
-/* indexed by board_t, above */
-static struct {
- const char *name;
- u32 hw_flags;
-} board_info[] __devinitdata = {
- { "RealTek RTL8139", RTL8139_CAPS },
- { "RealTek RTL8129", RTL8129_CAPS },
-};
-
-
-static struct pci_device_id rtl8139_pci_tbl[] = {
- {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
- {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
-
-#ifdef CONFIG_SH_SECUREEDGE5410
- /* Bogus 8139 silicon reports 8129 without external PROM :-( */
- {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
-#endif
-#ifdef CONFIG_8139TOO_8129
- {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
-#endif
-
- /* some crazy cards report invalid vendor ids like
- * 0x0001 here. The other ids are valid and constant,
- * so we simply don't match on the main vendor id.
- */
- {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
- {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },
- {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },
-
- {0,}
-};
-MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);
-
-static struct {
- const char str[ETH_GSTRING_LEN];
-} ethtool_stats_keys[] = {
- { "early_rx" },
- { "tx_buf_mapped" },
- { "tx_timeouts" },
- { "rx_lost_in_ring" },
-};
-
-/* The rest of these values should never change. */
-
-/* Symbolic offsets to registers. */
-enum RTL8139_registers {
- MAC0 = 0, /* Ethernet hardware address. */
- MAR0 = 8, /* Multicast filter. */
- TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */
- TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
- RxBuf = 0x30,
- ChipCmd = 0x37,
- RxBufPtr = 0x38,
- RxBufAddr = 0x3A,
- IntrMask = 0x3C,
- IntrStatus = 0x3E,
- TxConfig = 0x40,
- RxConfig = 0x44,
- Timer = 0x48, /* A general-purpose counter. */
- RxMissed = 0x4C, /* 24 bits valid, write clears. */
- Cfg9346 = 0x50,
- Config0 = 0x51,
- Config1 = 0x52,
- FlashReg = 0x54,
- MediaStatus = 0x58,
- Config3 = 0x59,
- Config4 = 0x5A, /* absent on RTL-8139A */
- HltClk = 0x5B,
- MultiIntr = 0x5C,
- TxSummary = 0x60,
- BasicModeCtrl = 0x62,
- BasicModeStatus = 0x64,
- NWayAdvert = 0x66,
- NWayLPAR = 0x68,
- NWayExpansion = 0x6A,
- /* Undocumented registers, but required for proper operation. */
- FIFOTMS = 0x70, /* FIFO Control and test. */
- CSCR = 0x74, /* Chip Status and Configuration Register. */
- PARA78 = 0x78,
- PARA7c = 0x7c, /* Magic transceiver parameter register. */
- Config5 = 0xD8, /* absent on RTL-8139A */
-};
-
-enum ClearBitMasks {
- MultiIntrClear = 0xF000,
- ChipCmdClear = 0xE2,
- Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
-};
-
-enum ChipCmdBits {
- CmdReset = 0x10,
- CmdRxEnb = 0x08,
- CmdTxEnb = 0x04,
- RxBufEmpty = 0x01,
-};
-
-/* Interrupt register bits, using my own meaningful names. */
-enum IntrStatusBits {
- PCIErr = 0x8000,
- PCSTimeout = 0x4000,
- RxFIFOOver = 0x40,
- RxUnderrun = 0x20,
- RxOverflow = 0x10,
- TxErr = 0x08,
- TxOK = 0x04,
- RxErr = 0x02,
- RxOK = 0x01,
-
- RxAckBits = RxFIFOOver | RxOverflow | RxOK,
-};
-
-enum TxStatusBits {
- TxHostOwns = 0x2000,
- TxUnderrun = 0x4000,
- TxStatOK = 0x8000,
- TxOutOfWindow = 0x20000000,
- TxAborted = 0x40000000,
- TxCarrierLost = 0x80000000,
-};
-enum RxStatusBits {
- RxMulticast = 0x8000,
- RxPhysical = 0x4000,
- RxBroadcast = 0x2000,
- RxBadSymbol = 0x0020,
- RxRunt = 0x0010,
- RxTooLong = 0x0008,
- RxCRCErr = 0x0004,
- RxBadAlign = 0x0002,
- RxStatusOK = 0x0001,
-};
-
-/* Bits in RxConfig. */
-enum rx_mode_bits {
- AcceptErr = 0x20,
- AcceptRunt = 0x10,
- AcceptBroadcast = 0x08,
- AcceptMulticast = 0x04,
- AcceptMyPhys = 0x02,
- AcceptAllPhys = 0x01,
-};
-
-/* Bits in TxConfig. */
-enum tx_config_bits {
-
- /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
- TxIFGShift = 24,
- TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
- TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
- TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
- TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
-
- TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
- TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
- TxClearAbt = (1 << 0), /* Clear abort (WO) */
- TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */
- TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */
-
- TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
-};
-
-/* Bits in Config1 */
-enum Config1Bits {
- Cfg1_PM_Enable = 0x01,
- Cfg1_VPD_Enable = 0x02,
- Cfg1_PIO = 0x04,
- Cfg1_MMIO = 0x08,
- LWAKE = 0x10, /* not on 8139, 8139A */
- Cfg1_Driver_Load = 0x20,
- Cfg1_LED0 = 0x40,
- Cfg1_LED1 = 0x80,
- SLEEP = (1 << 1), /* only on 8139, 8139A */
- PWRDN = (1 << 0), /* only on 8139, 8139A */
-};
-
-/* Bits in Config3 */
-enum Config3Bits {
- Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */
- Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
- Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
- Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */
- Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */
- Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
- Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */
- Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
-};
-
-/* Bits in Config4 */
-enum Config4Bits {
- LWPTN = (1 << 2), /* not on 8139, 8139A */
-};
-
-/* Bits in Config5 */
-enum Config5Bits {
- Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */
- Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */
- Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */
- Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
- Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */
- Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */
- Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */
-};
-
-enum RxConfigBits {
- /* rx fifo threshold */
- RxCfgFIFOShift = 13,
- RxCfgFIFONone = (7 << RxCfgFIFOShift),
-
- /* Max DMA burst */
- RxCfgDMAShift = 8,
- RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
-
- /* rx ring buffer length */
- RxCfgRcv8K = 0,
- RxCfgRcv16K = (1 << 11),
- RxCfgRcv32K = (1 << 12),
- RxCfgRcv64K = (1 << 11) | (1 << 12),
-
- /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
- RxNoWrap = (1 << 7),
-};
-
-/* Twister tuning parameters from RealTek.
- Completely undocumented, but required to tune bad links on some boards. */
-enum CSCRBits {
- CSCR_LinkOKBit = 0x0400,
- CSCR_LinkChangeBit = 0x0800,
- CSCR_LinkStatusBits = 0x0f000,
- CSCR_LinkDownOffCmd = 0x003c0,
- CSCR_LinkDownCmd = 0x0f3c0,
-};
-
-enum Cfg9346Bits {
- Cfg9346_Lock = 0x00,
- Cfg9346_Unlock = 0xC0,
-};
-
-typedef enum {
- CH_8139 = 0,
- CH_8139_K,
- CH_8139A,
- CH_8139A_G,
- CH_8139B,
- CH_8130,
- CH_8139C,
- CH_8100,
- CH_8100B_8139D,
- CH_8101,
-} chip_t;
-
-enum chip_flags {
- HasHltClk = (1 << 0),
- HasLWake = (1 << 1),
-};
-
-#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
- (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
-#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1)
-
-/* directly indexed by chip_t, above */
-const static struct {
- const char *name;
- u32 version; /* from RTL8139C/RTL8139D docs */
- u32 flags;
-} rtl_chip_info[] = {
- { "RTL-8139",
- HW_REVID(1, 0, 0, 0, 0, 0, 0),
- HasHltClk,
- },
-
- { "RTL-8139 rev K",
- HW_REVID(1, 1, 0, 0, 0, 0, 0),
- HasHltClk,
- },
-
- { "RTL-8139A",
- HW_REVID(1, 1, 1, 0, 0, 0, 0),
- HasHltClk, /* XXX undocumented? */
- },
-
- { "RTL-8139A rev G",
- HW_REVID(1, 1, 1, 0, 0, 1, 0),
- HasHltClk, /* XXX undocumented? */
- },
-
- { "RTL-8139B",
- HW_REVID(1, 1, 1, 1, 0, 0, 0),
- HasLWake,
- },
-
- { "RTL-8130",
- HW_REVID(1, 1, 1, 1, 1, 0, 0),
- HasLWake,
- },
-
- { "RTL-8139C",
- HW_REVID(1, 1, 1, 0, 1, 0, 0),
- HasLWake,
- },
-
- { "RTL-8100",
- HW_REVID(1, 1, 1, 1, 0, 1, 0),
- HasLWake,
- },
-
- { "RTL-8100B/8139D",
- HW_REVID(1, 1, 1, 0, 1, 0, 1),
- HasLWake,
- },
-
- { "RTL-8101",
- HW_REVID(1, 1, 1, 0, 1, 1, 1),
- HasLWake,
- },
-};
-
-struct rtl_extra_stats {
- unsigned long early_rx;
- unsigned long tx_buf_mapped;
- unsigned long tx_timeouts;
- unsigned long rx_lost_in_ring;
-};
-
-struct rtl8139_private {
- void __iomem *mmio_addr;
- int drv_flags;
- struct pci_dev *pci_dev;
- u32 msg_enable;
- struct net_device_stats stats;
- unsigned char *rx_ring;
- unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */
- unsigned int tx_flag;
- unsigned long cur_tx;
- unsigned long dirty_tx;
- unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */
- unsigned char *tx_bufs; /* Tx bounce buffer region. */
- dma_addr_t rx_ring_dma;
- dma_addr_t tx_bufs_dma;
- signed char phys[4]; /* MII device addresses. */
- char twistie, twist_row, twist_col; /* Twister tune state. */
- unsigned int default_port:4; /* Last dev->if_port value. */
- spinlock_t lock;
- spinlock_t rx_lock;
- chip_t chipset;
- pid_t thr_pid;
- wait_queue_head_t thr_wait;
- struct completion thr_exited;
- u32 rx_config;
- struct rtl_extra_stats xstats;
- int time_to_die;
- struct mii_if_info mii;
- unsigned int regs_len;
- unsigned long fifo_copy_timeout;
-};
-
-MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
-MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRV_VERSION);
-
-module_param(multicast_filter_limit, int, 0);
-module_param_array(media, int, NULL, 0);
-module_param_array(full_duplex, int, NULL, 0);
-module_param(debug, int, 0);
-MODULE_PARM_DESC (debug, "8139too bitmapped message enable number");
-MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses");
-MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps");
-MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)");
-
-static int read_eeprom (void __iomem *ioaddr, int location, int addr_len);
-static int rtl8139_open (struct net_device *dev);
-static int mdio_read (struct net_device *dev, int phy_id, int location);
-static void mdio_write (struct net_device *dev, int phy_id, int location,
- int val);
-static void rtl8139_start_thread(struct net_device *dev);
-static void rtl8139_tx_timeout (struct net_device *dev);
-static void rtl8139_init_ring (struct net_device *dev);
-static int rtl8139_start_xmit (struct sk_buff *skb,
- struct net_device *dev);
-static int rtl8139_poll(struct net_device *dev, int *budget);
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void rtl8139_poll_controller(struct net_device *dev);
-#endif
-static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
- struct pt_regs *regs);
-static int rtl8139_close (struct net_device *dev);
-static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
-static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
-static void rtl8139_set_rx_mode (struct net_device *dev);
-static void __set_rx_mode (struct net_device *dev);
-static void rtl8139_hw_start (struct net_device *dev);
-static struct ethtool_ops rtl8139_ethtool_ops;
-
-/* write MMIO register, with flush */
-/* Flush avoids rtl8139 bug w/ posted MMIO writes */
-#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0)
-#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0)
-#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0)
-
-
-#define MMIO_FLUSH_AUDIT_COMPLETE 1
-#if MMIO_FLUSH_AUDIT_COMPLETE
-
-/* write MMIO register */
-#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg))
-#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg))
-#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg))
-
-#else
-
-/* write MMIO register, then flush */
-#define RTL_W8 RTL_W8_F
-#define RTL_W16 RTL_W16_F
-#define RTL_W32 RTL_W32_F
-
-#endif /* MMIO_FLUSH_AUDIT_COMPLETE */
-
-/* read MMIO register */
-#define RTL_R8(reg) ioread8 (ioaddr + (reg))
-#define RTL_R16(reg) ioread16 (ioaddr + (reg))
-#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg)))
-
-
-static const u16 rtl8139_intr_mask =
- PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
- TxErr | TxOK | RxErr | RxOK;
-
-static const u16 rtl8139_norx_intr_mask =
- PCIErr | PCSTimeout | RxUnderrun |
- TxErr | TxOK | RxErr ;
-
-#if RX_BUF_IDX == 0
-static const unsigned int rtl8139_rx_config =
- RxCfgRcv8K | RxNoWrap |
- (RX_FIFO_THRESH << RxCfgFIFOShift) |
- (RX_DMA_BURST << RxCfgDMAShift);
-#elif RX_BUF_IDX == 1
-static const unsigned int rtl8139_rx_config =
- RxCfgRcv16K | RxNoWrap |
- (RX_FIFO_THRESH << RxCfgFIFOShift) |
- (RX_DMA_BURST << RxCfgDMAShift);
-#elif RX_BUF_IDX == 2
-static const unsigned int rtl8139_rx_config =
- RxCfgRcv32K | RxNoWrap |
- (RX_FIFO_THRESH << RxCfgFIFOShift) |
- (RX_DMA_BURST << RxCfgDMAShift);
-#elif RX_BUF_IDX == 3
-static const unsigned int rtl8139_rx_config =
- RxCfgRcv64K |
- (RX_FIFO_THRESH << RxCfgFIFOShift) |
- (RX_DMA_BURST << RxCfgDMAShift);
-#else
-#error "Invalid configuration for 8139_RXBUF_IDX"
-#endif
-
-static const unsigned int rtl8139_tx_config =
- TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift);
-
-static void __rtl8139_cleanup_dev (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- struct pci_dev *pdev;
-
- assert (dev != NULL);
- assert (tp->pci_dev != NULL);
- pdev = tp->pci_dev;
-
-#ifdef USE_IO_OPS
- if (tp->mmio_addr)
- ioport_unmap (tp->mmio_addr);
-#else
- if (tp->mmio_addr)
- pci_iounmap (pdev, tp->mmio_addr);
-#endif /* USE_IO_OPS */
-
- /* it's ok to call this even if we have no regions to free */
- pci_release_regions (pdev);
-
- free_netdev(dev);
- pci_set_drvdata (pdev, NULL);
-}
-
-
-static void rtl8139_chip_reset (void __iomem *ioaddr)
-{
- int i;
-
- /* Soft reset the chip. */
- RTL_W8 (ChipCmd, CmdReset);
-
- /* Check that the chip has finished the reset. */
- for (i = 1000; i > 0; i--) {
- barrier();
- if ((RTL_R8 (ChipCmd) & CmdReset) == 0)
- break;
- udelay (10);
- }
-}
-
-
-static int __devinit rtl8139_init_board (struct pci_dev *pdev,
- struct net_device **dev_out)
-{
- void __iomem *ioaddr;
- struct net_device *dev;
- struct rtl8139_private *tp;
- u8 tmp8;
- int rc, disable_dev_on_err = 0;
- unsigned int i;
- unsigned long pio_start, pio_end, pio_flags, pio_len;
- unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
- u32 version;
-
- assert (pdev != NULL);
-
- *dev_out = NULL;
-
- /* dev and priv zeroed in alloc_etherdev */
- dev = alloc_etherdev (sizeof (*tp));
- if (dev == NULL) {
- printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev));
- return -ENOMEM;
- }
- SET_MODULE_OWNER(dev);
- SET_NETDEV_DEV(dev, &pdev->dev);
-
- tp = netdev_priv(dev);
- tp->pci_dev = pdev;
-
- /* enable device (incl. PCI PM wakeup and hotplug setup) */
- rc = pci_enable_device (pdev);
- if (rc)
- goto err_out;
-
- pio_start = pci_resource_start (pdev, 0);
- pio_end = pci_resource_end (pdev, 0);
- pio_flags = pci_resource_flags (pdev, 0);
- pio_len = pci_resource_len (pdev, 0);
-
- mmio_start = pci_resource_start (pdev, 1);
- mmio_end = pci_resource_end (pdev, 1);
- mmio_flags = pci_resource_flags (pdev, 1);
- mmio_len = pci_resource_len (pdev, 1);
-
- /* set this immediately, we need to know before
- * we talk to the chip directly */
- DPRINTK("PIO region size == 0x%02X\n", pio_len);
- DPRINTK("MMIO region size == 0x%02lX\n", mmio_len);
-
-#ifdef USE_IO_OPS
- /* make sure PCI base addr 0 is PIO */
- if (!(pio_flags & IORESOURCE_IO)) {
- printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev));
- rc = -ENODEV;
- goto err_out;
- }
- /* check for weird/broken PCI region reporting */
- if (pio_len < RTL_MIN_IO_SIZE) {
- printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev));
- rc = -ENODEV;
- goto err_out;
- }
-#else
- /* make sure PCI base addr 1 is MMIO */
- if (!(mmio_flags & IORESOURCE_MEM)) {
- printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev));
- rc = -ENODEV;
- goto err_out;
- }
- if (mmio_len < RTL_MIN_IO_SIZE) {
- printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev));
- rc = -ENODEV;
- goto err_out;
- }
-#endif
-
- rc = pci_request_regions (pdev, "8139too");
- if (rc)
- goto err_out;
- disable_dev_on_err = 1;
-
- /* enable PCI bus-mastering */
- pci_set_master (pdev);
-
-#ifdef USE_IO_OPS
- ioaddr = ioport_map(pio_start, pio_len);
- if (!ioaddr) {
- printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev));
- rc = -EIO;
- goto err_out;
- }
- dev->base_addr = pio_start;
- tp->mmio_addr = ioaddr;
- tp->regs_len = pio_len;
-#else
- /* ioremap MMIO region */
- ioaddr = pci_iomap(pdev, 1, 0);
- if (ioaddr == NULL) {
- printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev));
- rc = -EIO;
- goto err_out;
- }
- dev->base_addr = (long) ioaddr;
- tp->mmio_addr = ioaddr;
- tp->regs_len = mmio_len;
-#endif /* USE_IO_OPS */
-
- /* Bring old chips out of low-power mode. */
- RTL_W8 (HltClk, 'R');
-
- /* check for missing/broken hardware */
- if (RTL_R32 (TxConfig) == 0xFFFFFFFF) {
- printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n",
- pci_name(pdev));
- rc = -EIO;
- goto err_out;
- }
-
- /* identify chip attached to board */
- version = RTL_R32 (TxConfig) & HW_REVID_MASK;
- for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++)
- if (version == rtl_chip_info[i].version) {
- tp->chipset = i;
- goto match;
- }
-
- /* if unknown chip, assume array element #0, original RTL-8139 in this case */
- printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n",
- pci_name(pdev));
- printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig));
- tp->chipset = 0;
-
-match:
- DPRINTK ("chipset id (%d) == index %d, '%s'\n",
- version, i, rtl_chip_info[i].name);
-
- if (tp->chipset >= CH_8139B) {
- u8 new_tmp8 = tmp8 = RTL_R8 (Config1);
- DPRINTK("PCI PM wakeup\n");
- if ((rtl_chip_info[tp->chipset].flags & HasLWake) &&
- (tmp8 & LWAKE))
- new_tmp8 &= ~LWAKE;
- new_tmp8 |= Cfg1_PM_Enable;
- if (new_tmp8 != tmp8) {
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W8 (Config1, tmp8);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
- }
- if (rtl_chip_info[tp->chipset].flags & HasLWake) {
- tmp8 = RTL_R8 (Config4);
- if (tmp8 & LWPTN) {
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W8 (Config4, tmp8 & ~LWPTN);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
- }
- }
- } else {
- DPRINTK("Old chip wakeup\n");
- tmp8 = RTL_R8 (Config1);
- tmp8 &= ~(SLEEP | PWRDN);
- RTL_W8 (Config1, tmp8);
- }
-
- rtl8139_chip_reset (ioaddr);
-
- *dev_out = dev;
- return 0;
-
-err_out:
- __rtl8139_cleanup_dev (dev);
- if (disable_dev_on_err)
- pci_disable_device (pdev);
- return rc;
-}
-
-
-static int __devinit rtl8139_init_one (struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- struct net_device *dev = NULL;
- struct rtl8139_private *tp;
- int i, addr_len, option;
- void __iomem *ioaddr;
- static int board_idx = -1;
- u8 pci_rev;
-
- assert (pdev != NULL);
- assert (ent != NULL);
-
- board_idx++;
-
- /* when we're built into the kernel, the driver version message
- * is only printed if at least one 8139 board has been found
- */
-#ifndef MODULE
- {
- static int printed_version;
- if (!printed_version++)
- printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
- }
-#endif
-
- pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev);
-
- if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
- pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) {
- printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n",
- pci_name(pdev), pdev->vendor, pdev->device, pci_rev);
- printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n");
- }
-
- i = rtl8139_init_board (pdev, &dev);
- if (i < 0)
- return i;
-
- assert (dev != NULL);
- tp = netdev_priv(dev);
-
- ioaddr = tp->mmio_addr;
- assert (ioaddr != NULL);
-
- addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6;
- for (i = 0; i < 3; i++)
- ((u16 *) (dev->dev_addr))[i] =
- le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len));
-
- /* The Rtl8139-specific entries in the device structure. */
- dev->open = rtl8139_open;
- dev->hard_start_xmit = rtl8139_start_xmit;
- dev->poll = rtl8139_poll;
- dev->weight = 64;
- dev->stop = rtl8139_close;
- dev->get_stats = rtl8139_get_stats;
- dev->set_multicast_list = rtl8139_set_rx_mode;
- dev->do_ioctl = netdev_ioctl;
- dev->ethtool_ops = &rtl8139_ethtool_ops;
- dev->tx_timeout = rtl8139_tx_timeout;
- dev->watchdog_timeo = TX_TIMEOUT;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = rtl8139_poll_controller;
-#endif
-
- /* note: the hardware is not capable of sg/csum/highdma, however
- * through the use of skb_copy_and_csum_dev we enable these
- * features
- */
- dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;
-
- dev->irq = pdev->irq;
-
- /* tp zeroed and aligned in alloc_etherdev */
- tp = netdev_priv(dev);
-
- /* note: tp->chipset set in rtl8139_init_board */
- tp->drv_flags = board_info[ent->driver_data].hw_flags;
- tp->mmio_addr = ioaddr;
- tp->msg_enable =
- (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1));
- spin_lock_init (&tp->lock);
- spin_lock_init (&tp->rx_lock);
- init_waitqueue_head (&tp->thr_wait);
- init_completion (&tp->thr_exited);
- tp->mii.dev = dev;
- tp->mii.mdio_read = mdio_read;
- tp->mii.mdio_write = mdio_write;
- tp->mii.phy_id_mask = 0x3f;
- tp->mii.reg_num_mask = 0x1f;
-
- /* dev is fully set up and ready to use now */
- DPRINTK("about to register device named %s (%p)...\n", dev->name, dev);
- i = register_netdev (dev);
- if (i) goto err_out;
-
- pci_set_drvdata (pdev, dev);
-
- printk (KERN_INFO "%s: %s at 0x%lx, "
- "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
- "IRQ %d\n",
- dev->name,
- board_info[ent->driver_data].name,
- dev->base_addr,
- dev->dev_addr[0], dev->dev_addr[1],
- dev->dev_addr[2], dev->dev_addr[3],
- dev->dev_addr[4], dev->dev_addr[5],
- dev->irq);
-
- printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n",
- dev->name, rtl_chip_info[tp->chipset].name);
-
- /* Find the connected MII xcvrs.
- Doing this in open() would allow detecting external xcvrs later, but
- takes too much time. */
-#ifdef CONFIG_8139TOO_8129
- if (tp->drv_flags & HAS_MII_XCVR) {
- int phy, phy_idx = 0;
- for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) {
- int mii_status = mdio_read(dev, phy, 1);
- if (mii_status != 0xffff && mii_status != 0x0000) {
- u16 advertising = mdio_read(dev, phy, 4);
- tp->phys[phy_idx++] = phy;
- printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x "
- "advertising %4.4x.\n",
- dev->name, phy, mii_status, advertising);
- }
- }
- if (phy_idx == 0) {
- printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM "
- "transceiver.\n",
- dev->name);
- tp->phys[0] = 32;
- }
- } else
-#endif
- tp->phys[0] = 32;
- tp->mii.phy_id = tp->phys[0];
-
- /* The lower four bits are the media type. */
- option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx];
- if (option > 0) {
- tp->mii.full_duplex = (option & 0x210) ? 1 : 0;
- tp->default_port = option & 0xFF;
- if (tp->default_port)
- tp->mii.force_media = 1;
- }
- if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0)
- tp->mii.full_duplex = full_duplex[board_idx];
- if (tp->mii.full_duplex) {
- printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
- /* Changing the MII-advertised media because might prevent
- re-connection. */
- tp->mii.force_media = 1;
- }
- if (tp->default_port) {
- printk(KERN_INFO " Forcing %dMbps %s-duplex operation.\n",
- (option & 0x20 ? 100 : 10),
- (option & 0x10 ? "full" : "half"));
- mdio_write(dev, tp->phys[0], 0,
- ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */
- ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
- }
-
- /* Put the chip into low-power mode. */
- if (rtl_chip_info[tp->chipset].flags & HasHltClk)
- RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
-
- return 0;
-
-err_out:
- __rtl8139_cleanup_dev (dev);
- pci_disable_device (pdev);
- return i;
-}
-
-
-static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata (pdev);
-
- assert (dev != NULL);
-
- unregister_netdev (dev);
-
- __rtl8139_cleanup_dev (dev);
- pci_disable_device (pdev);
-}
-
-
-/* Serial EEPROM section. */
-
-/* EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
-#define EE_CS 0x08 /* EEPROM chip select. */
-#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
-#define EE_WRITE_0 0x00
-#define EE_WRITE_1 0x02
-#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
-#define EE_ENB (0x80 | EE_CS)
-
-/* Delay between EEPROM clock transitions.
- No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
- */
-
-#define eeprom_delay() RTL_R32(Cfg9346)
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD (5)
-#define EE_READ_CMD (6)
-#define EE_ERASE_CMD (7)
-
-static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len)
-{
- int i;
- unsigned retval = 0;
- int read_cmd = location | (EE_READ_CMD << addr_len);
-
- RTL_W8 (Cfg9346, EE_ENB & ~EE_CS);
- RTL_W8 (Cfg9346, EE_ENB);
- eeprom_delay ();
-
- /* Shift the read command bits out. */
- for (i = 4 + addr_len; i >= 0; i--) {
- int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
- RTL_W8 (Cfg9346, EE_ENB | dataval);
- eeprom_delay ();
- RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK);
- eeprom_delay ();
- }
- RTL_W8 (Cfg9346, EE_ENB);
- eeprom_delay ();
-
- for (i = 16; i > 0; i--) {
- RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK);
- eeprom_delay ();
- retval =
- (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 :
- 0);
- RTL_W8 (Cfg9346, EE_ENB);
- eeprom_delay ();
- }
-
- /* Terminate the EEPROM access. */
- RTL_W8 (Cfg9346, ~EE_CS);
- eeprom_delay ();
-
- return retval;
-}
-
-/* MII serial management: mostly bogus for now. */
-/* Read and write the MII management registers using software-generated
- serial MDIO protocol.
- The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
- met by back-to-back PCI I/O cycles, but we insert a delay to avoid
- "overclocking" issues. */
-#define MDIO_DIR 0x80
-#define MDIO_DATA_OUT 0x04
-#define MDIO_DATA_IN 0x02
-#define MDIO_CLK 0x01
-#define MDIO_WRITE0 (MDIO_DIR)
-#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)
-
-#define mdio_delay() RTL_R8(Config4)
-
-
-static char mii_2_8139_map[8] = {
- BasicModeCtrl,
- BasicModeStatus,
- 0,
- 0,
- NWayAdvert,
- NWayLPAR,
- NWayExpansion,
- 0
-};
-
-
-#ifdef CONFIG_8139TOO_8129
-/* Syncronize the MII management interface by shifting 32 one bits out. */
-static void mdio_sync (void __iomem *ioaddr)
-{
- int i;
-
- for (i = 32; i >= 0; i--) {
- RTL_W8 (Config4, MDIO_WRITE1);
- mdio_delay ();
- RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK);
- mdio_delay ();
- }
-}
-#endif
-
-static int mdio_read (struct net_device *dev, int phy_id, int location)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- int retval = 0;
-#ifdef CONFIG_8139TOO_8129
- void __iomem *ioaddr = tp->mmio_addr;
- int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- int i;
-#endif
-
- if (phy_id > 31) { /* Really a 8139. Use internal registers. */
- void __iomem *ioaddr = tp->mmio_addr;
- return location < 8 && mii_2_8139_map[location] ?
- RTL_R16 (mii_2_8139_map[location]) : 0;
- }
-
-#ifdef CONFIG_8139TOO_8129
- mdio_sync (ioaddr);
- /* Shift the read command bits out. */
- for (i = 15; i >= 0; i--) {
- int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;
-
- RTL_W8 (Config4, MDIO_DIR | dataval);
- mdio_delay ();
- RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK);
- mdio_delay ();
- }
-
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 19; i > 0; i--) {
- RTL_W8 (Config4, 0);
- mdio_delay ();
- retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0);
- RTL_W8 (Config4, MDIO_CLK);
- mdio_delay ();
- }
-#endif
-
- return (retval >> 1) & 0xffff;
-}
-
-
-static void mdio_write (struct net_device *dev, int phy_id, int location,
- int value)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
-#ifdef CONFIG_8139TOO_8129
- void __iomem *ioaddr = tp->mmio_addr;
- int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
- int i;
-#endif
-
- if (phy_id > 31) { /* Really a 8139. Use internal registers. */
- void __iomem *ioaddr = tp->mmio_addr;
- if (location == 0) {
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W16 (BasicModeCtrl, value);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
- } else if (location < 8 && mii_2_8139_map[location])
- RTL_W16 (mii_2_8139_map[location], value);
- return;
- }
-
-#ifdef CONFIG_8139TOO_8129
- mdio_sync (ioaddr);
-
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval =
- (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
- RTL_W8 (Config4, dataval);
- mdio_delay ();
- RTL_W8 (Config4, dataval | MDIO_CLK);
- mdio_delay ();
- }
- /* Clear out extra bits. */
- for (i = 2; i > 0; i--) {
- RTL_W8 (Config4, 0);
- mdio_delay ();
- RTL_W8 (Config4, MDIO_CLK);
- mdio_delay ();
- }
-#endif
-}
-
-
-static int rtl8139_open (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- int retval;
- void __iomem *ioaddr = tp->mmio_addr;
-
- retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev);
- if (retval)
- return retval;
-
- tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
- &tp->tx_bufs_dma);
- tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
- &tp->rx_ring_dma);
- if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
- free_irq(dev->irq, dev);
-
- if (tp->tx_bufs)
- pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
- tp->tx_bufs, tp->tx_bufs_dma);
- if (tp->rx_ring)
- pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
- tp->rx_ring, tp->rx_ring_dma);
-
- return -ENOMEM;
-
- }
-
- tp->mii.full_duplex = tp->mii.force_media;
- tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
-
- rtl8139_init_ring (dev);
- rtl8139_hw_start (dev);
- netif_start_queue (dev);
-
- if (netif_msg_ifup(tp))
- printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d"
- " GP Pins %2.2x %s-duplex.\n",
- dev->name, pci_resource_start (tp->pci_dev, 1),
- dev->irq, RTL_R8 (MediaStatus),
- tp->mii.full_duplex ? "full" : "half");
-
- rtl8139_start_thread(dev);
-
- return 0;
-}
-
-
-static void rtl_check_media (struct net_device *dev, unsigned int init_media)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
-
- if (tp->phys[0] >= 0) {
- mii_check_media(&tp->mii, netif_msg_link(tp), init_media);
- }
-}
-
-/* Start the hardware at open or resume. */
-static void rtl8139_hw_start (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u32 i;
- u8 tmp;
-
- /* Bring old chips out of low-power mode. */
- if (rtl_chip_info[tp->chipset].flags & HasHltClk)
- RTL_W8 (HltClk, 'R');
-
- rtl8139_chip_reset (ioaddr);
-
- /* unlock Config[01234] and BMCR register writes */
- RTL_W8_F (Cfg9346, Cfg9346_Unlock);
- /* Restore our idea of the MAC address. */
- RTL_W32_F (MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0)));
- RTL_W32_F (MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4)));
-
- /* Must enable Tx/Rx before setting transfer thresholds! */
- RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-
- tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
- RTL_W32 (RxConfig, tp->rx_config);
- RTL_W32 (TxConfig, rtl8139_tx_config);
-
- tp->cur_rx = 0;
-
- rtl_check_media (dev, 1);
-
- if (tp->chipset >= CH_8139B) {
- /* Disable magic packet scanning, which is enabled
- * when PM is enabled in Config1. It can be reenabled
- * via ETHTOOL_SWOL if desired. */
- RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic);
- }
-
- DPRINTK("init buffer addresses\n");
-
- /* Lock Config[01234] and BMCR register writes */
- RTL_W8 (Cfg9346, Cfg9346_Lock);
-
- /* init Rx ring buffer DMA address */
- RTL_W32_F (RxBuf, tp->rx_ring_dma);
-
- /* init Tx buffer DMA addresses */
- for (i = 0; i < NUM_TX_DESC; i++)
- RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs));
-
- RTL_W32 (RxMissed, 0);
-
- rtl8139_set_rx_mode (dev);
-
- /* no early-rx interrupts */
- RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear);
-
- /* make sure RxTx has started */
- tmp = RTL_R8 (ChipCmd);
- if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb)))
- RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-
- /* Enable all known interrupts by setting the interrupt mask. */
- RTL_W16 (IntrMask, rtl8139_intr_mask);
-}
-
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void rtl8139_init_ring (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- int i;
-
- tp->cur_rx = 0;
- tp->cur_tx = 0;
- tp->dirty_tx = 0;
-
- for (i = 0; i < NUM_TX_DESC; i++)
- tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE];
-}
-
-
-/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */
-static int next_tick = 3 * HZ;
-
-#ifndef CONFIG_8139TOO_TUNE_TWISTER
-static inline void rtl8139_tune_twister (struct net_device *dev,
- struct rtl8139_private *tp) {}
-#else
-enum TwisterParamVals {
- PARA78_default = 0x78fa8388,
- PARA7c_default = 0xcb38de43, /* param[0][3] */
- PARA7c_xxx = 0xcb38de43,
-};
-
-static const unsigned long param[4][4] = {
- {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43},
- {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
- {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83},
- {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83}
-};
-
-static void rtl8139_tune_twister (struct net_device *dev,
- struct rtl8139_private *tp)
-{
- int linkcase;
- void __iomem *ioaddr = tp->mmio_addr;
-
- /* This is a complicated state machine to configure the "twister" for
- impedance/echos based on the cable length.
- All of this is magic and undocumented.
- */
- switch (tp->twistie) {
- case 1:
- if (RTL_R16 (CSCR) & CSCR_LinkOKBit) {
- /* We have link beat, let us tune the twister. */
- RTL_W16 (CSCR, CSCR_LinkDownOffCmd);
- tp->twistie = 2; /* Change to state 2. */
- next_tick = HZ / 10;
- } else {
- /* Just put in some reasonable defaults for when beat returns. */
- RTL_W16 (CSCR, CSCR_LinkDownCmd);
- RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */
- RTL_W32 (PARA78, PARA78_default);
- RTL_W32 (PARA7c, PARA7c_default);
- tp->twistie = 0; /* Bail from future actions. */
- }
- break;
- case 2:
- /* Read how long it took to hear the echo. */
- linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits;
- if (linkcase == 0x7000)
- tp->twist_row = 3;
- else if (linkcase == 0x3000)
- tp->twist_row = 2;
- else if (linkcase == 0x1000)
- tp->twist_row = 1;
- else
- tp->twist_row = 0;
- tp->twist_col = 0;
- tp->twistie = 3; /* Change to state 2. */
- next_tick = HZ / 10;
- break;
- case 3:
- /* Put out four tuning parameters, one per 100msec. */
- if (tp->twist_col == 0)
- RTL_W16 (FIFOTMS, 0);
- RTL_W32 (PARA7c, param[(int) tp->twist_row]
- [(int) tp->twist_col]);
- next_tick = HZ / 10;
- if (++tp->twist_col >= 4) {
- /* For short cables we are done.
- For long cables (row == 3) check for mistune. */
- tp->twistie =
- (tp->twist_row == 3) ? 4 : 0;
- }
- break;
- case 4:
- /* Special case for long cables: check for mistune. */
- if ((RTL_R16 (CSCR) &
- CSCR_LinkStatusBits) == 0x7000) {
- tp->twistie = 0;
- break;
- } else {
- RTL_W32 (PARA7c, 0xfb38de03);
- tp->twistie = 5;
- next_tick = HZ / 10;
- }
- break;
- case 5:
- /* Retune for shorter cable (column 2). */
- RTL_W32 (FIFOTMS, 0x20);
- RTL_W32 (PARA78, PARA78_default);
- RTL_W32 (PARA7c, PARA7c_default);
- RTL_W32 (FIFOTMS, 0x00);
- tp->twist_row = 2;
- tp->twist_col = 0;
- tp->twistie = 3;
- next_tick = HZ / 10;
- break;
-
- default:
- /* do nothing */
- break;
- }
-}
-#endif /* CONFIG_8139TOO_TUNE_TWISTER */
-
-static inline void rtl8139_thread_iter (struct net_device *dev,
- struct rtl8139_private *tp,
- void __iomem *ioaddr)
-{
- int mii_lpa;
-
- mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA);
-
- if (!tp->mii.force_media && mii_lpa != 0xffff) {
- int duplex = (mii_lpa & LPA_100FULL)
- || (mii_lpa & 0x01C0) == 0x0040;
- if (tp->mii.full_duplex != duplex) {
- tp->mii.full_duplex = duplex;
-
- if (mii_lpa) {
- printk (KERN_INFO
- "%s: Setting %s-duplex based on MII #%d link"
- " partner ability of %4.4x.\n",
- dev->name,
- tp->mii.full_duplex ? "full" : "half",
- tp->phys[0], mii_lpa);
- } else {
- printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n",
- dev->name);
- }
-#if 0
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
-#endif
- }
- }
-
- next_tick = HZ * 60;
-
- rtl8139_tune_twister (dev, tp);
-
- DPRINTK ("%s: Media selection tick, Link partner %4.4x.\n",
- dev->name, RTL_R16 (NWayLPAR));
- DPRINTK ("%s: Other registers are IntMask %4.4x IntStatus %4.4x\n",
- dev->name, RTL_R16 (IntrMask), RTL_R16 (IntrStatus));
- DPRINTK ("%s: Chip config %2.2x %2.2x.\n",
- dev->name, RTL_R8 (Config0),
- RTL_R8 (Config1));
-}
-
-static int rtl8139_thread (void *data)
-{
- struct net_device *dev = data;
- struct rtl8139_private *tp = netdev_priv(dev);
- unsigned long timeout;
-
- daemonize("%s", dev->name);
- allow_signal(SIGTERM);
-
- while (1) {
- timeout = next_tick;
- do {
- timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout);
- /* make swsusp happy with our thread */
- try_to_freeze();
- } while (!signal_pending (current) && (timeout > 0));
-
- if (signal_pending (current)) {
- flush_signals(current);
- }
-
- if (tp->time_to_die)
- break;
-
- if (rtnl_lock_interruptible ())
- break;
- rtl8139_thread_iter (dev, tp, tp->mmio_addr);
- rtnl_unlock ();
- }
-
- complete_and_exit (&tp->thr_exited, 0);
-}
-
-static void rtl8139_start_thread(struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
-
- tp->thr_pid = -1;
- tp->twistie = 0;
- tp->time_to_die = 0;
- if (tp->chipset == CH_8139_K)
- tp->twistie = 1;
- else if (tp->drv_flags & HAS_LNK_CHNG)
- return;
-
- tp->thr_pid = kernel_thread(rtl8139_thread, dev, CLONE_FS|CLONE_FILES);
- if (tp->thr_pid < 0) {
- printk (KERN_WARNING "%s: unable to start kernel thread\n",
- dev->name);
- }
-}
-
-static inline void rtl8139_tx_clear (struct rtl8139_private *tp)
-{
- tp->cur_tx = 0;
- tp->dirty_tx = 0;
-
- /* XXX account for unsent Tx packets in tp->stats.tx_dropped */
-}
-
-
-static void rtl8139_tx_timeout (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- int i;
- u8 tmp8;
- unsigned long flags;
-
- printk (KERN_DEBUG "%s: Transmit timeout, status %2.2x %4.4x %4.4x "
- "media %2.2x.\n", dev->name, RTL_R8 (ChipCmd),
- RTL_R16(IntrStatus), RTL_R16(IntrMask), RTL_R8(MediaStatus));
- /* Emit info to figure out what went wrong. */
- printk (KERN_DEBUG "%s: Tx queue start entry %ld dirty entry %ld.\n",
- dev->name, tp->cur_tx, tp->dirty_tx);
- for (i = 0; i < NUM_TX_DESC; i++)
- printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8lx.%s\n",
- dev->name, i, RTL_R32 (TxStatus0 + (i * 4)),
- i == tp->dirty_tx % NUM_TX_DESC ?
- " (queue head)" : "");
-
- tp->xstats.tx_timeouts++;
-
- /* disable Tx ASAP, if not already */
- tmp8 = RTL_R8 (ChipCmd);
- if (tmp8 & CmdTxEnb)
- RTL_W8 (ChipCmd, CmdRxEnb);
-
- spin_lock(&tp->rx_lock);
- /* Disable interrupts by clearing the interrupt mask. */
- RTL_W16 (IntrMask, 0x0000);
-
- /* Stop a shared interrupt from scavenging while we are. */
- spin_lock_irqsave (&tp->lock, flags);
- rtl8139_tx_clear (tp);
- spin_unlock_irqrestore (&tp->lock, flags);
-
- /* ...and finally, reset everything */
- if (netif_running(dev)) {
- rtl8139_hw_start (dev);
- netif_wake_queue (dev);
- }
- spin_unlock(&tp->rx_lock);
-}
-
-
-static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned int entry;
- unsigned int len = skb->len;
-
- /* Calculate the next Tx descriptor entry. */
- entry = tp->cur_tx % NUM_TX_DESC;
-
- /* Note: the chip doesn't have auto-pad! */
- if (likely(len < TX_BUF_SIZE)) {
- if (len < ETH_ZLEN)
- memset(tp->tx_buf[entry], 0, ETH_ZLEN);
- skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
- dev_kfree_skb(skb);
- } else {
- dev_kfree_skb(skb);
- tp->stats.tx_dropped++;
- return 0;
- }
-
- spin_lock_irq(&tp->lock);
- RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
- tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
-
- dev->trans_start = jiffies;
-
- tp->cur_tx++;
- wmb();
-
- if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
- netif_stop_queue (dev);
- spin_unlock_irq(&tp->lock);
-
- if (netif_msg_tx_queued(tp))
- printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n",
- dev->name, len, entry);
-
- return 0;
-}
-
-
-static void rtl8139_tx_interrupt (struct net_device *dev,
- struct rtl8139_private *tp,
- void __iomem *ioaddr)
-{
- unsigned long dirty_tx, tx_left;
-
- assert (dev != NULL);
- assert (ioaddr != NULL);
-
- dirty_tx = tp->dirty_tx;
- tx_left = tp->cur_tx - dirty_tx;
- while (tx_left > 0) {
- int entry = dirty_tx % NUM_TX_DESC;
- int txstatus;
-
- txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32)));
-
- if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
- break; /* It still hasn't been Txed */
-
- /* Note: TxCarrierLost is always asserted at 100mbps. */
- if (txstatus & (TxOutOfWindow | TxAborted)) {
- /* There was an major error, log it. */
- if (netif_msg_tx_err(tp))
- printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
- dev->name, txstatus);
- tp->stats.tx_errors++;
- if (txstatus & TxAborted) {
- tp->stats.tx_aborted_errors++;
- RTL_W32 (TxConfig, TxClearAbt);
- RTL_W16 (IntrStatus, TxErr);
- wmb();
- }
- if (txstatus & TxCarrierLost)
- tp->stats.tx_carrier_errors++;
- if (txstatus & TxOutOfWindow)
- tp->stats.tx_window_errors++;
- } else {
- if (txstatus & TxUnderrun) {
- /* Add 64 to the Tx FIFO threshold. */
- if (tp->tx_flag < 0x00300000)
- tp->tx_flag += 0x00020000;
- tp->stats.tx_fifo_errors++;
- }
- tp->stats.collisions += (txstatus >> 24) & 15;
- tp->stats.tx_bytes += txstatus & 0x7ff;
- tp->stats.tx_packets++;
- }
-
- dirty_tx++;
- tx_left--;
- }
-
-#ifndef RTL8139_NDEBUG
- if (tp->cur_tx - dirty_tx > NUM_TX_DESC) {
- printk (KERN_ERR "%s: Out-of-sync dirty pointer, %ld vs. %ld.\n",
- dev->name, dirty_tx, tp->cur_tx);
- dirty_tx += NUM_TX_DESC;
- }
-#endif /* RTL8139_NDEBUG */
-
- /* only wake the queue if we did work, and the queue is stopped */
- if (tp->dirty_tx != dirty_tx) {
- tp->dirty_tx = dirty_tx;
- mb();
- netif_wake_queue (dev);
- }
-}
-
-
-/* TODO: clean this up! Rx reset need not be this intensive */
-static void rtl8139_rx_err (u32 rx_status, struct net_device *dev,
- struct rtl8139_private *tp, void __iomem *ioaddr)
-{
- u8 tmp8;
-#ifdef CONFIG_8139_OLD_RX_RESET
- int tmp_work;
-#endif
-
- if (netif_msg_rx_err (tp))
- printk(KERN_DEBUG "%s: Ethernet frame had errors, status %8.8x.\n",
- dev->name, rx_status);
- tp->stats.rx_errors++;
- if (!(rx_status & RxStatusOK)) {
- if (rx_status & RxTooLong) {
- DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
- dev->name, rx_status);
- /* A.C.: The chip hangs here. */
- }
- if (rx_status & (RxBadSymbol | RxBadAlign))
- tp->stats.rx_frame_errors++;
- if (rx_status & (RxRunt | RxTooLong))
- tp->stats.rx_length_errors++;
- if (rx_status & RxCRCErr)
- tp->stats.rx_crc_errors++;
- } else {
- tp->xstats.rx_lost_in_ring++;
- }
-
-#ifndef CONFIG_8139_OLD_RX_RESET
- tmp8 = RTL_R8 (ChipCmd);
- RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb);
- RTL_W8 (ChipCmd, tmp8);
- RTL_W32 (RxConfig, tp->rx_config);
- tp->cur_rx = 0;
-#else
- /* Reset the receiver, based on RealTek recommendation. (Bug?) */
-
- /* disable receive */
- RTL_W8_F (ChipCmd, CmdTxEnb);
- tmp_work = 200;
- while (--tmp_work > 0) {
- udelay(1);
- tmp8 = RTL_R8 (ChipCmd);
- if (!(tmp8 & CmdRxEnb))
- break;
- }
- if (tmp_work <= 0)
- printk (KERN_WARNING PFX "rx stop wait too long\n");
- /* restart receive */
- tmp_work = 200;
- while (--tmp_work > 0) {
- RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb);
- udelay(1);
- tmp8 = RTL_R8 (ChipCmd);
- if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb))
- break;
- }
- if (tmp_work <= 0)
- printk (KERN_WARNING PFX "tx/rx enable wait too long\n");
-
- /* and reinitialize all rx related registers */
- RTL_W8_F (Cfg9346, Cfg9346_Unlock);
- /* Must enable Tx/Rx before setting transfer thresholds! */
- RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);
-
- tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys;
- RTL_W32 (RxConfig, tp->rx_config);
- tp->cur_rx = 0;
-
- DPRINTK("init buffer addresses\n");
-
- /* Lock Config[01234] and BMCR register writes */
- RTL_W8 (Cfg9346, Cfg9346_Lock);
-
- /* init Rx ring buffer DMA address */
- RTL_W32_F (RxBuf, tp->rx_ring_dma);
-
- /* A.C.: Reset the multicast list. */
- __set_rx_mode (dev);
-#endif
-}
-
-#if RX_BUF_IDX == 3
-static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring,
- u32 offset, unsigned int size)
-{
- u32 left = RX_BUF_LEN - offset;
-
- if (size > left) {
- memcpy(skb->data, ring + offset, left);
- memcpy(skb->data+left, ring, size - left);
- } else
- memcpy(skb->data, ring + offset, size);
-}
-#endif
-
-static void rtl8139_isr_ack(struct rtl8139_private *tp)
-{
- void __iomem *ioaddr = tp->mmio_addr;
- u16 status;
-
- status = RTL_R16 (IntrStatus) & RxAckBits;
-
- /* Clear out errors and receive interrupts */
- if (likely(status != 0)) {
- if (unlikely(status & (RxFIFOOver | RxOverflow))) {
- tp->stats.rx_errors++;
- if (status & RxFIFOOver)
- tp->stats.rx_fifo_errors++;
- }
- RTL_W16_F (IntrStatus, RxAckBits);
- }
-}
-
-static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
- int budget)
-{
- void __iomem *ioaddr = tp->mmio_addr;
- int received = 0;
- unsigned char *rx_ring = tp->rx_ring;
- unsigned int cur_rx = tp->cur_rx;
- unsigned int rx_size = 0;
-
- DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
- " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx,
- RTL_R16 (RxBufAddr),
- RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
-
- while (netif_running(dev) && received < budget
- && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
- u32 ring_offset = cur_rx % RX_BUF_LEN;
- u32 rx_status;
- unsigned int pkt_size;
- struct sk_buff *skb;
-
- rmb();
-
- /* read size+status of next frame from DMA ring buffer */
- rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
- rx_size = rx_status >> 16;
- pkt_size = rx_size - 4;
-
- if (netif_msg_rx_status(tp))
- printk(KERN_DEBUG "%s: rtl8139_rx() status %4.4x, size %4.4x,"
- " cur %4.4x.\n", dev->name, rx_status,
- rx_size, cur_rx);
-#if RTL8139_DEBUG > 2
- {
- int i;
- DPRINTK ("%s: Frame contents ", dev->name);
- for (i = 0; i < 70; i++)
- printk (" %2.2x",
- rx_ring[ring_offset + i]);
- printk (".\n");
- }
-#endif
-
- /* Packet copy from FIFO still in progress.
- * Theoretically, this should never happen
- * since EarlyRx is disabled.
- */
- if (unlikely(rx_size == 0xfff0)) {
- if (!tp->fifo_copy_timeout)
- tp->fifo_copy_timeout = jiffies + 2;
- else if (time_after(jiffies, tp->fifo_copy_timeout)) {
- DPRINTK ("%s: hung FIFO. Reset.", dev->name);
- rx_size = 0;
- goto no_early_rx;
- }
- if (netif_msg_intr(tp)) {
- printk(KERN_DEBUG "%s: fifo copy in progress.",
- dev->name);
- }
- tp->xstats.early_rx++;
- break;
- }
-
-no_early_rx:
- tp->fifo_copy_timeout = 0;
-
- /* If Rx err or invalid rx_size/rx_status received
- * (which happens if we get lost in the ring),
- * Rx process gets reset, so we abort any further
- * Rx processing.
- */
- if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
- (rx_size < 8) ||
- (!(rx_status & RxStatusOK)))) {
- rtl8139_rx_err (rx_status, dev, tp, ioaddr);
- received = -1;
- goto out;
- }
-
- /* Malloc up new buffer, compatible with net-2e. */
- /* Omit the four octet CRC from the length. */
-
- skb = dev_alloc_skb (pkt_size + 2);
- if (likely(skb)) {
- skb->dev = dev;
- skb_reserve (skb, 2); /* 16 byte align the IP fields. */
-#if RX_BUF_IDX == 3
- wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
-#else
- eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
-#endif
- skb_put (skb, pkt_size);
-
- skb->protocol = eth_type_trans (skb, dev);
-
- dev->last_rx = jiffies;
- tp->stats.rx_bytes += pkt_size;
- tp->stats.rx_packets++;
-
- netif_receive_skb (skb);
- } else {
- if (net_ratelimit())
- printk (KERN_WARNING
- "%s: Memory squeeze, dropping packet.\n",
- dev->name);
- tp->stats.rx_dropped++;
- }
- received++;
-
- cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
- RTL_W16 (RxBufPtr, (u16) (cur_rx - 16));
-
- rtl8139_isr_ack(tp);
- }
-
- if (unlikely(!received || rx_size == 0xfff0))
- rtl8139_isr_ack(tp);
-
-#if RTL8139_DEBUG > 1
- DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
- " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
- RTL_R16 (RxBufAddr),
- RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
-#endif
-
- tp->cur_rx = cur_rx;
-
- /*
- * The receive buffer should be mostly empty.
- * Tell NAPI to reenable the Rx irq.
- */
- if (tp->fifo_copy_timeout)
- received = budget;
-
-out:
- return received;
-}
-
-
-static void rtl8139_weird_interrupt (struct net_device *dev,
- struct rtl8139_private *tp,
- void __iomem *ioaddr,
- int status, int link_changed)
-{
- DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n",
- dev->name, status);
-
- assert (dev != NULL);
- assert (tp != NULL);
- assert (ioaddr != NULL);
-
- /* Update the error count. */
- tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
- RTL_W32 (RxMissed, 0);
-
- if ((status & RxUnderrun) && link_changed &&
- (tp->drv_flags & HAS_LNK_CHNG)) {
- rtl_check_media(dev, 0);
- status &= ~RxUnderrun;
- }
-
- if (status & (RxUnderrun | RxErr))
- tp->stats.rx_errors++;
-
- if (status & PCSTimeout)
- tp->stats.rx_length_errors++;
- if (status & RxUnderrun)
- tp->stats.rx_fifo_errors++;
- if (status & PCIErr) {
- u16 pci_cmd_status;
- pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status);
- pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status);
-
- printk (KERN_ERR "%s: PCI Bus error %4.4x.\n",
- dev->name, pci_cmd_status);
- }
-}
-
-static int rtl8139_poll(struct net_device *dev, int *budget)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- int orig_budget = min(*budget, dev->quota);
- int done = 1;
-
- spin_lock(&tp->rx_lock);
- if (likely(RTL_R16(IntrStatus) & RxAckBits)) {
- int work_done;
-
- work_done = rtl8139_rx(dev, tp, orig_budget);
- if (likely(work_done > 0)) {
- *budget -= work_done;
- dev->quota -= work_done;
- done = (work_done < orig_budget);
- }
- }
-
- if (done) {
- /*
- * Order is important since data can get interrupted
- * again when we think we are done.
- */
- local_irq_disable();
- RTL_W16_F(IntrMask, rtl8139_intr_mask);
- __netif_rx_complete(dev);
- local_irq_enable();
- }
- spin_unlock(&tp->rx_lock);
-
- return !done;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance,
- struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *) dev_instance;
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u16 status, ackstat;
- int link_changed = 0; /* avoid bogus "uninit" warning */
- int handled = 0;
-
- spin_lock (&tp->lock);
- status = RTL_R16 (IntrStatus);
-
- /* shared irq? */
- if (unlikely((status & rtl8139_intr_mask) == 0))
- goto out;
-
- handled = 1;
-
- /* h/w no longer present (hotplug?) or major error, bail */
- if (unlikely(status == 0xFFFF))
- goto out;
-
- /* close possible race's with dev_close */
- if (unlikely(!netif_running(dev))) {
- RTL_W16 (IntrMask, 0);
- goto out;
- }
-
- /* Acknowledge all of the current interrupt sources ASAP, but
- an first get an additional status bit from CSCR. */
- if (unlikely(status & RxUnderrun))
- link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;
-
- ackstat = status & ~(RxAckBits | TxErr);
- if (ackstat)
- RTL_W16 (IntrStatus, ackstat);
-
- /* Receive packets are processed by poll routine.
- If not running start it now. */
- if (status & RxAckBits){
- if (netif_rx_schedule_prep(dev)) {
- RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
- __netif_rx_schedule (dev);
- }
- }
-
- /* Check uncommon events with one test. */
- if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr)))
- rtl8139_weird_interrupt (dev, tp, ioaddr,
- status, link_changed);
-
- if (status & (TxOK | TxErr)) {
- rtl8139_tx_interrupt (dev, tp, ioaddr);
- if (status & TxErr)
- RTL_W16 (IntrStatus, TxErr);
- }
- out:
- spin_unlock (&tp->lock);
-
- DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n",
- dev->name, RTL_R16 (IntrStatus));
- return IRQ_RETVAL(handled);
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling receive - used by netconsole and other diagnostic tools
- * to allow network i/o with interrupts disabled.
- */
-static void rtl8139_poll_controller(struct net_device *dev)
-{
- disable_irq(dev->irq);
- rtl8139_interrupt(dev->irq, dev, NULL);
- enable_irq(dev->irq);
-}
-#endif
-
-static int rtl8139_close (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- int ret = 0;
- unsigned long flags;
-
- netif_stop_queue (dev);
-
- if (tp->thr_pid >= 0) {
- tp->time_to_die = 1;
- wmb();
- ret = kill_proc (tp->thr_pid, SIGTERM, 1);
- if (ret) {
- printk (KERN_ERR "%s: unable to signal thread\n", dev->name);
- return ret;
- }
- wait_for_completion (&tp->thr_exited);
- }
-
- if (netif_msg_ifdown(tp))
- printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n",
- dev->name, RTL_R16 (IntrStatus));
-
- spin_lock_irqsave (&tp->lock, flags);
-
- /* Stop the chip's Tx and Rx DMA processes. */
- RTL_W8 (ChipCmd, 0);
-
- /* Disable interrupts by clearing the interrupt mask. */
- RTL_W16 (IntrMask, 0);
-
- /* Update the error counts. */
- tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
- RTL_W32 (RxMissed, 0);
-
- spin_unlock_irqrestore (&tp->lock, flags);
-
- synchronize_irq (dev->irq); /* racy, but that's ok here */
- free_irq (dev->irq, dev);
-
- rtl8139_tx_clear (tp);
-
- pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
- tp->rx_ring, tp->rx_ring_dma);
- pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
- tp->tx_bufs, tp->tx_bufs_dma);
- tp->rx_ring = NULL;
- tp->tx_bufs = NULL;
-
- /* Green! Put the chip in low-power mode. */
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
-
- if (rtl_chip_info[tp->chipset].flags & HasHltClk)
- RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */
-
- return 0;
-}
-
-
-/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to
- kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and
- other threads or interrupts aren't messing with the 8139. */
-static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- void __iomem *ioaddr = np->mmio_addr;
-
- spin_lock_irq(&np->lock);
- if (rtl_chip_info[np->chipset].flags & HasLWake) {
- u8 cfg3 = RTL_R8 (Config3);
- u8 cfg5 = RTL_R8 (Config5);
-
- wol->supported = WAKE_PHY | WAKE_MAGIC
- | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST;
-
- wol->wolopts = 0;
- if (cfg3 & Cfg3_LinkUp)
- wol->wolopts |= WAKE_PHY;
- if (cfg3 & Cfg3_Magic)
- wol->wolopts |= WAKE_MAGIC;
- /* (KON)FIXME: See how netdev_set_wol() handles the
- following constants. */
- if (cfg5 & Cfg5_UWF)
- wol->wolopts |= WAKE_UCAST;
- if (cfg5 & Cfg5_MWF)
- wol->wolopts |= WAKE_MCAST;
- if (cfg5 & Cfg5_BWF)
- wol->wolopts |= WAKE_BCAST;
- }
- spin_unlock_irq(&np->lock);
-}
-
-
-/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes
- that wol points to kernel memory and other threads or interrupts
- aren't messing with the 8139. */
-static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- void __iomem *ioaddr = np->mmio_addr;
- u32 support;
- u8 cfg3, cfg5;
-
- support = ((rtl_chip_info[np->chipset].flags & HasLWake)
- ? (WAKE_PHY | WAKE_MAGIC
- | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)
- : 0);
- if (wol->wolopts & ~support)
- return -EINVAL;
-
- spin_lock_irq(&np->lock);
- cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic);
- if (wol->wolopts & WAKE_PHY)
- cfg3 |= Cfg3_LinkUp;
- if (wol->wolopts & WAKE_MAGIC)
- cfg3 |= Cfg3_Magic;
- RTL_W8 (Cfg9346, Cfg9346_Unlock);
- RTL_W8 (Config3, cfg3);
- RTL_W8 (Cfg9346, Cfg9346_Lock);
-
- cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF);
- /* (KON)FIXME: These are untested. We may have to set the
- CRC0, Wakeup0 and LSBCRC0 registers too, but I have no
- documentation. */
- if (wol->wolopts & WAKE_UCAST)
- cfg5 |= Cfg5_UWF;
- if (wol->wolopts & WAKE_MCAST)
- cfg5 |= Cfg5_MWF;
- if (wol->wolopts & WAKE_BCAST)
- cfg5 |= Cfg5_BWF;
- RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */
- spin_unlock_irq(&np->lock);
-
- return 0;
-}
-
-static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- strcpy(info->driver, DRV_NAME);
- strcpy(info->version, DRV_VERSION);
- strcpy(info->bus_info, pci_name(np->pci_dev));
- info->regdump_len = np->regs_len;
-}
-
-static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- spin_lock_irq(&np->lock);
- mii_ethtool_gset(&np->mii, cmd);
- spin_unlock_irq(&np->lock);
- return 0;
-}
-
-static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- int rc;
- spin_lock_irq(&np->lock);
- rc = mii_ethtool_sset(&np->mii, cmd);
- spin_unlock_irq(&np->lock);
- return rc;
-}
-
-static int rtl8139_nway_reset(struct net_device *dev)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- return mii_nway_restart(&np->mii);
-}
-
-static u32 rtl8139_get_link(struct net_device *dev)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- return mii_link_ok(&np->mii);
-}
-
-static u32 rtl8139_get_msglevel(struct net_device *dev)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- return np->msg_enable;
-}
-
-static void rtl8139_set_msglevel(struct net_device *dev, u32 datum)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- np->msg_enable = datum;
-}
-
-/* TODO: we are too slack to do reg dumping for pio, for now */
-#ifdef CONFIG_8139TOO_PIO
-#define rtl8139_get_regs_len NULL
-#define rtl8139_get_regs NULL
-#else
-static int rtl8139_get_regs_len(struct net_device *dev)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- return np->regs_len;
-}
-
-static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
-{
- struct rtl8139_private *np = netdev_priv(dev);
-
- regs->version = RTL_REGS_VER;
-
- spin_lock_irq(&np->lock);
- memcpy_fromio(regbuf, np->mmio_addr, regs->len);
- spin_unlock_irq(&np->lock);
-}
-#endif /* CONFIG_8139TOO_MMIO */
-
-static int rtl8139_get_stats_count(struct net_device *dev)
-{
- return RTL_NUM_STATS;
-}
-
-static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data)
-{
- struct rtl8139_private *np = netdev_priv(dev);
-
- data[0] = np->xstats.early_rx;
- data[1] = np->xstats.tx_buf_mapped;
- data[2] = np->xstats.tx_timeouts;
- data[3] = np->xstats.rx_lost_in_ring;
-}
-
-static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data)
-{
- memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys));
-}
-
-static struct ethtool_ops rtl8139_ethtool_ops = {
- .get_drvinfo = rtl8139_get_drvinfo,
- .get_settings = rtl8139_get_settings,
- .set_settings = rtl8139_set_settings,
- .get_regs_len = rtl8139_get_regs_len,
- .get_regs = rtl8139_get_regs,
- .nway_reset = rtl8139_nway_reset,
- .get_link = rtl8139_get_link,
- .get_msglevel = rtl8139_get_msglevel,
- .set_msglevel = rtl8139_set_msglevel,
- .get_wol = rtl8139_get_wol,
- .set_wol = rtl8139_set_wol,
- .get_strings = rtl8139_get_strings,
- .get_stats_count = rtl8139_get_stats_count,
- .get_ethtool_stats = rtl8139_get_ethtool_stats,
-};
-
-static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct rtl8139_private *np = netdev_priv(dev);
- int rc;
-
- if (!netif_running(dev))
- return -EINVAL;
-
- spin_lock_irq(&np->lock);
- rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
- spin_unlock_irq(&np->lock);
-
- return rc;
-}
-
-
-static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
-
- if (netif_running(dev)) {
- spin_lock_irqsave (&tp->lock, flags);
- tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
- RTL_W32 (RxMissed, 0);
- spin_unlock_irqrestore (&tp->lock, flags);
- }
-
- return &tp->stats;
-}
-
-/* Set or clear the multicast filter for this adaptor.
- This routine is not state sensitive and need not be SMP locked. */
-
-static void __set_rx_mode (struct net_device *dev)
-{
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u32 mc_filter[2]; /* Multicast hash filter */
- int i, rx_mode;
- u32 tmp;
-
- DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8lx.\n",
- dev->name, dev->flags, RTL_R32 (RxConfig));
-
- /* Note: do not reorder, GCC is clever about common statements. */
- if (dev->flags & IFF_PROMISC) {
- /* Unconditionally log net taps. */
- printk (KERN_NOTICE "%s: Promiscuous mode enabled.\n",
- dev->name);
- rx_mode =
- AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
- AcceptAllPhys;
- mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
- /* Too many to filter perfectly -- accept all multicasts. */
- rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
- mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else {
- struct dev_mc_list *mclist;
- rx_mode = AcceptBroadcast | AcceptMyPhys;
- mc_filter[1] = mc_filter[0] = 0;
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next) {
- int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
-
- mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
- rx_mode |= AcceptMulticast;
- }
- }
-
- /* We can safely update without stopping the chip. */
- tmp = rtl8139_rx_config | rx_mode;
- if (tp->rx_config != tmp) {
- RTL_W32_F (RxConfig, tmp);
- tp->rx_config = tmp;
- }
- RTL_W32_F (MAR0 + 0, mc_filter[0]);
- RTL_W32_F (MAR0 + 4, mc_filter[1]);
-}
-
-static void rtl8139_set_rx_mode (struct net_device *dev)
-{
- unsigned long flags;
- struct rtl8139_private *tp = netdev_priv(dev);
-
- spin_lock_irqsave (&tp->lock, flags);
- __set_rx_mode(dev);
- spin_unlock_irqrestore (&tp->lock, flags);
-}
-
-#ifdef CONFIG_PM
-
-static int rtl8139_suspend (struct pci_dev *pdev, pm_message_t state)
-{
- struct net_device *dev = pci_get_drvdata (pdev);
- struct rtl8139_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
-
- pci_save_state (pdev);
-
- if (!netif_running (dev))
- return 0;
-
- netif_device_detach (dev);
-
- spin_lock_irqsave (&tp->lock, flags);
-
- /* Disable interrupts, stop Tx and Rx. */
- RTL_W16 (IntrMask, 0);
- RTL_W8 (ChipCmd, 0);
-
- /* Update the error counts. */
- tp->stats.rx_missed_errors += RTL_R32 (RxMissed);
- RTL_W32 (RxMissed, 0);
-
- spin_unlock_irqrestore (&tp->lock, flags);
-
- pci_set_power_state (pdev, PCI_D3hot);
-
- return 0;
-}
-
-
-static int rtl8139_resume (struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata (pdev);
-
- pci_restore_state (pdev);
- if (!netif_running (dev))
- return 0;
- pci_set_power_state (pdev, PCI_D0);
- rtl8139_init_ring (dev);
- rtl8139_hw_start (dev);
- netif_device_attach (dev);
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-
-static struct pci_driver rtl8139_pci_driver = {
- .name = DRV_NAME,
- .id_table = rtl8139_pci_tbl,
- .probe = rtl8139_init_one,
- .remove = __devexit_p(rtl8139_remove_one),
-#ifdef CONFIG_PM
- .suspend = rtl8139_suspend,
- .resume = rtl8139_resume,
-#endif /* CONFIG_PM */
-};
-
-
-static int __init rtl8139_init_module (void)
-{
- /* when we're a module, we always print a version message,
- * even if no 8139 board is found.
- */
-#ifdef MODULE
- printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
-#endif
-
- return pci_module_init (&rtl8139_pci_driver);
-}
-
-
-static void __exit rtl8139_cleanup_module (void)
-{
- pci_unregister_driver (&rtl8139_pci_driver);
-}
-
-
-module_init(rtl8139_init_module);
-module_exit(rtl8139_cleanup_module);
--- a/documentation/ethercat_doc.tex Fri Oct 13 10:07:10 2006 +0000
+++ b/documentation/ethercat_doc.tex Tue Nov 07 12:13:30 2006 +0000
@@ -41,6 +41,8 @@
\SVN $Date$
\SVN $Revision$
+\newcommand{\masterversion}{1.1.1}
+
\makeindex
\makeglossary
@@ -56,7 +58,7 @@
\rule{\textwidth}{1.5mm}
{\Huge\bf IgH \includegraphics[height=2.4ex]{images/ethercat}
- Master 1.1\\[1ex]
+ Master \masterversion\\[1ex]
Documentation}
\vspace{1ex}
@@ -1290,7 +1292,7 @@
The master module gives information about it's state and events via
the syslog interface. The module loading command above should result
-in the following syslog messages:
+in the syslog messages below (or similar):
\begin{lstlisting}
EtherCAT: Master driver, 1.1 (stable) - rev. 513,
@@ -4299,20 +4301,20 @@
The current EtherCAT master code is available at~\cite{etherlab} or
can be obtained from the EtherLab\textsuperscript{\textregistered} CD.
-The \textit{tar.bz2} file has to be unpacked with the command below
+The \textit{tar.bz2} file has to be unpacked with the commands below
(or similar):
\begin{lstlisting}
- `\$` `\textbf{tar xjf ethercat-1.1-rXXX.tar.bz2}`
- `\$` `\textbf{cd ethercat-1.1-rXXX/}`
+ `\$` `\textbf{tar xjf ethercat-\masterversion.tar.bz2}`
+ `\$` `\textbf{cd ethercat-\masterversion/}`
\end{lstlisting}
The tarball was created with GNU Autotools, so the build process
-follows the usual commands:
+follows the below commands:
\begin{lstlisting}
`\$` `\textbf{./configure}`
- `\$` `\textbf{make}`
+ `\$` `\textbf{make modules}`
\end{lstlisting}
The default installation prefix is \textit{/opt/etherlab}. It can be
@@ -4327,17 +4329,30 @@
\begin{lstlisting}
`\$` `\textbf{./configure --with-linux="2.6.17-ipipe"}`
- `\$` `\textbf{make}`
-\end{lstlisting}
-
-The following commands have to be entered as \textit{root}: To install
-the kernel modules, headers, the init script, the sysconfig file and
-the user space tools, the below command has to be executed:
+ `\$` `\textbf{make modules}`
+\end{lstlisting}
+
+The below commands have to be entered as \textit{root}: The first one
+will install the kernel modules to the kernel's modules directory. The
+second one will install EtherCAT headers, the init script, the
+sysconfig file and the user space tools to the prefix path.
\begin{lstlisting}
+ # `\textbf{make modules\_install}`
# `\textbf{make install}`
\end{lstlisting}
+If the target kernel's modules directory is not under
+\textit{/lib/modules}, a different destination directory can be
+specified with the \textit{DESTDIR} make variable. For example:
+
+\begin{lstlisting}
+ # `\textbf{make DESTDIR=/vol/nfs/root modules\_install}`
+\end{lstlisting}
+
+This command will install the compiled kernel modules to
+\textit{/vol/nfs/root/lib/modules}, prepended by the kernel release.
+
If the EtherCAT master shall be run as a service
(recommended\footnote{Even if the EtherCAT master shall not be loaded
on system startup, the use of the init script is recommended for
--- a/examples/mini/Kbuild Fri Oct 13 10:07:10 2006 +0000
+++ b/examples/mini/Kbuild Tue Nov 07 12:13:30 2006 +0000
@@ -1,7 +1,5 @@
#------------------------------------------------------------------------------
#
-# Kbuild
-#
# $Id$
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
--- a/examples/mini/Makefile.am Fri Oct 13 10:07:10 2006 +0000
+++ b/examples/mini/Makefile.am Tue Nov 07 12:13:30 2006 +0000
@@ -43,9 +43,9 @@
clean-local:
$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" clean
-install-data-local:
- $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" \
- INSTALL_MOD_PATH="$(DESTDIR)" \
- INSTALL_MOD_DIR="ethercat" modules_install
+install-data-local: modules_install
+
+modules_install:
+ cp $(srcdir)/ec_mini.ko $(DESTDIR)$(LINUX_MOD_PATH)
#------------------------------------------------------------------------------
--- a/examples/mini/mini.c Fri Oct 13 10:07:10 2006 +0000
+++ b/examples/mini/mini.c Tue Nov 07 12:13:30 2006 +0000
@@ -1,9 +1,5 @@
/******************************************************************************
*
- * m i n i . c
- *
- * Minimal module for EtherCAT.
- *
* $Id$
*
* Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -46,6 +42,8 @@
#define FREQUENCY 100
+#define KBUS
+
/*****************************************************************************/
struct timer_list timer;
@@ -56,23 +54,26 @@
spinlock_t master_lock = SPIN_LOCK_UNLOCKED;
// data fields
-void *r_ana_out;
-
-// channels
-uint32_t k_pos;
-uint8_t k_stat;
-
+#ifdef KBUS
+void *r_inputs;
+void *r_outputs;
+#endif
+
+void *r_ana_in;
+
+#if 1
ec_pdo_reg_t domain1_pdos[] = {
- {"2", Beckhoff_EL4132_Output1, &r_ana_out},
- {"3", Beckhoff_EL5001_Value, NULL},
+ {"2", Beckhoff_EL3102_Input1, &r_ana_in},
{}
};
+#endif
/*****************************************************************************/
void run(unsigned long data)
{
static unsigned int counter = 0;
+ static unsigned int einaus = 0;
spin_lock(&master_lock);
@@ -82,6 +83,9 @@
// process data
//k_pos = EC_READ_U32(r_ssi);
+#ifdef KBUS
+ EC_WRITE_U8(r_outputs + 2, einaus ? 0xFF : 0x00);
+#endif
// send
ecrt_master_run(master);
@@ -94,10 +98,7 @@
}
else {
counter = FREQUENCY;
- //printk(KERN_INFO "input = ");
- //for (i = 0; i < 22; i++)
- // printk("%02X ", *((uint8_t *) r_kbus_in + i));
- //printk("\n");
+ einaus = !einaus;
}
// restart timer
@@ -124,7 +125,9 @@
int __init init_mini_module(void)
{
+#if 1
ec_slave_t *slave;
+#endif
printk(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n");
@@ -143,16 +146,33 @@
}
printk(KERN_INFO "Registering PDOs...\n");
+#if 1
if (ecrt_domain_register_pdo_list(domain1, domain1_pdos)) {
printk(KERN_ERR "PDO registration failed!\n");
goto out_release_master;
}
-
- if (!(slave = ecrt_master_get_slave(master, "3")))
+#endif
+
+#ifdef KBUS
+ if (!ecrt_domain_register_pdo_range(domain1, "0", Beckhoff_BK1120,
+ EC_DIR_OUTPUT, 0, 4, &r_outputs)) {
+ printk(KERN_ERR "PDO registration failed!\n");
+ goto out_release_master;
+ }
+ if (!ecrt_domain_register_pdo_range(domain1, "0", Beckhoff_BK1120,
+ EC_DIR_INPUT, 0, 4, &r_inputs)) {
+ printk(KERN_ERR "PDO registration failed!\n");
+ goto out_release_master;
+ }
+#endif
+
+#if 1
+ if (!(slave = ecrt_master_get_slave(master, "2")))
goto out_release_master;
if (ecrt_slave_conf_sdo8(slave, 0x4061, 1, 0))
goto out_release_master;
+#endif
printk(KERN_INFO "Activating master...\n");
if (ecrt_master_activate(master)) {
@@ -183,12 +203,9 @@
{
printk(KERN_INFO "=== Stopping Minimal EtherCAT environment... ===\n");
- if (master) {
- del_timer_sync(&timer);
- printk(KERN_INFO "Deactivating master...\n");
- ecrt_master_deactivate(master);
- ecrt_release_master(master);
- }
+ del_timer_sync(&timer);
+ printk(KERN_INFO "Releasing master...\n");
+ ecrt_release_master(master);
printk(KERN_INFO "=== Minimal EtherCAT environment stopped. ===\n");
}
--- a/examples/msr/Kbuild Fri Oct 13 10:07:10 2006 +0000
+++ b/examples/msr/Kbuild Tue Nov 07 12:13:30 2006 +0000
@@ -1,7 +1,5 @@
#------------------------------------------------------------------------------
#
-# Kbuild
-#
# $Id$
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -33,6 +31,8 @@
#
#------------------------------------------------------------------------------
+include $(src)/../../config.kbuild
+
MODULE := ec_msr_sample
obj-m := $(MODULE).o
@@ -52,7 +52,7 @@
rt_lib/msr-math/msr_hex_bin.o \
libm.o
-EXTRA_CFLAGS := -I$(src)/rt_lib/msr-include -I/usr/realtime/include \
+EXTRA_CFLAGS := -I$(EC_MSR_DIR)/include -I$(EC_RTAI_DIR)/include \
-D_SIMULATION -mhard-float
#------------------------------------------------------------------------------
--- a/examples/msr/Makefile.am Fri Oct 13 10:07:10 2006 +0000
+++ b/examples/msr/Makefile.am Tue Nov 07 12:13:30 2006 +0000
@@ -1,9 +1,5 @@
#------------------------------------------------------------------------------
#
-# Makefile.am
-#
-# IgH EtherCAT master module
-#
# $Id$
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -38,11 +34,9 @@
EXTRA_DIST = \
Kbuild \
libm.o_shipped \
- msr_load \
- msr_unload \
- msr_param.h \
msr_sample.c \
- msrserv.pl
+ msrserv.pl \
+ init.sh
all:
$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" modules
@@ -50,9 +44,9 @@
clean-local:
$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" clean
-install-data-local:
- $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" \
- INSTALL_MOD_PATH="$(DESTDIR)" \
- INSTALL_MOD_DIR="ethercat" modules_install
+install-data-local: modules_install
+
+modules_install:
+ cp $(srcdir)/ec_msr_sample.ko $(DESTDIR)$(LINUX_MOD_PATH)
#------------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/msr/init.sh Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+#------------------------------------------------------------------------------
+#
+# MSR Init Script
+#
+# $Id$
+#
+#------------------------------------------------------------------------------
+
+### BEGIN INIT INFO
+# Provides: msr
+# Required-Start: $local_fs $syslog $network
+# Should-Start: $time ntp ethercat
+# Required-Stop: $local_fs $syslog $network
+# Should-Stop: $time ntp ethercat
+# Default-Start: 3 5
+# Default-Stop: 0 1 2 6
+# Short-Description: MSR module
+# Description:
+### END INIT INFO
+
+#------------------------------------------------------------------------------
+
+# <Customizing>
+
+NAME="MSR EtherCAT sample"
+BASE=/opt/etherlab
+MSR_SERVER=$BASE/bin/msrserv.pl
+MODULE=ec_msr_sample
+RTAI_PATH=/usr/realtime
+RTAI_MODULES="hal up" # sem math
+DEVICE=msr
+DEVMASK=664
+GROUP=users
+
+# </Customizing>
+
+#------------------------------------------------------------------------------
+
+. /etc/rc.status
+rc_reset
+
+#------------------------------------------------------------------------------
+
+case "$1" in
+ start)
+ echo -n Starting $NAME" "
+
+ # Insert RTAI modules
+ for mod in $RTAI_MODULES; do
+ if ! lsmod | grep -q "^rtai_$mod"; then
+ if ! insmod $RTAI_PATH/modules/rtai_$mod.ko; then
+ /bin/false
+ rc_status -v
+ rc_exit
+ fi
+ fi
+ done
+
+ # Insert realtime module
+ if ! modprobe $MODULE; then
+ /bin/false
+ rc_status -v
+ rc_exit
+ fi
+
+ #sleep 2
+
+ # Create MSR device
+ MAJOR=`cat /proc/devices | awk "\\$2==\"$DEVICE\" {print \\$1}"`
+ rm -f /dev/${DEVICE}
+ mknod /dev/${DEVICE} c $MAJOR 0
+ chgrp $GROUP /dev/${DEVICE}
+ chmod $DEVMASK /dev/${DEVICE}
+
+ #sleep 1
+
+ # Start MSR-Server
+ startproc $MSR_SERVER 1>/dev/null 2>/dev/null
+ rc_status -v
+ ;;
+
+ stop)
+ echo -n Shutting down $NAME" "
+
+ if ! killproc $MSR_SERVER; then
+ /bin/false
+ rc_status -v
+ rc_exit
+ fi
+
+ if ! /sbin/rmmod $MODULE; then
+ /bin/false
+ rc_status -v
+ rc_exit
+ fi
+
+ # Remove stale nodes
+ rm -f /dev/${DEVICE} /dev/${DEVICE}0
+
+ rc_status -v
+ ;;
+
+ restart)
+ $0 stop || exit 1
+ sleep 1
+ $0 start
+
+ rc_status
+ ;;
+
+ status)
+ echo -n "Checking for MSR module: "
+ /sbin/lsmod | grep -q "^$MODULE"
+ rc_status -v
+
+ echo -n "Checking for MSR server: "
+ checkproc $MSR_SERVER
+ rc_status -v
+ ;;
+
+ *)
+ echo "Usage: $0 {start|stop|status|restart}"
+ exit 1
+ ;;
+esac
+
+rc_exit
\ No newline at end of file
--- a/examples/msr/msr_load Fri Oct 13 10:07:10 2006 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#!/bin/sh
-module="ec_msr_sample"
-device="msr"
-mode="664"
-
-# Group: since distributions do it differently, look for wheel or use staff
-if grep '^staff:' /etc/group > /dev/null; then
- group="staff"
-else
- group="wheel"
-fi
-
-# invoke insmod with all arguments we got
-# and use a pathname, as newer modutils don't look in . by default
-/sbin/insmod -f ./$module.ko $* || exit 1
-
-major=`cat /proc/devices | awk "\\$2==\"$device\" {print \\$1}"`
-
-echo $major
-# Remove stale nodes and replace them, then give gid and perms
-# Usually the script is shorter, it's scull that has several devices in it.
-
-rm -f /dev/${device}
-mknod /dev/${device} c $major 0
-# ln -sf ${device}0 /dev/${device}
-chgrp users /dev/${device}
-chmod $mode /dev/${device}
-
-
-
-
-
-
-
-
-
--- a/examples/msr/msr_param.h Fri Oct 13 10:07:10 2006 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/******************************************************************************
- *
- * $Id$
- *
- * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
- *
- * This file is part of the IgH EtherCAT Master.
- *
- * The IgH EtherCAT Master 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.
- *
- * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * The right to use EtherCAT Technology is granted and comes free of
- * charge under condition of compatibility of product made by
- * Licensee. People intending to distribute/sell products based on the
- * code, have to sign an agreement to guarantee that products using
- * software based on IgH EtherCAT master stay compatible with the actual
- * EtherCAT specification (which are released themselves as an open
- * standard) as the (only) precondition to have the right to use EtherCAT
- * Technology, IP and trade marks.
- *
- *****************************************************************************/
-
-#ifndef _MSR_PARAM_H_
-#define _MSR_PARAM_H_
-
-#define MSR_ABTASTFREQUENZ 1000
-
-#endif
-
-/*****************************************************************************/
--- a/examples/msr/msr_sample.c Fri Oct 13 10:07:10 2006 +0000
+++ b/examples/msr/msr_sample.c Tue Nov 07 12:13:30 2006 +0000
@@ -1,7 +1,5 @@
/******************************************************************************
*
- * Sample module for use with IgH MSR library.
- *
* $Id$
*
* Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -44,12 +42,13 @@
#include <msr_main.h>
#include <msr_reg.h>
#include <msr_time.h>
-#include "msr_param.h"
// EtherCAT
#include "../../include/ecrt.h"
#include "../../include/ecdb.h"
+#define MSR_ABTASTFREQUENZ 1000
+
#define HZREDUCTION (MSR_ABTASTFREQUENZ / HZ)
#define TIMERTICKS (1000000000 / MSR_ABTASTFREQUENZ)
@@ -214,7 +213,6 @@
rt_task_delete(&task);
stop_rt_timer();
- ecrt_master_deactivate(master);
ecrt_release_master(master);
rt_sem_delete(&master_sem);
msr_rtlib_cleanup();
--- a/examples/msr/msr_unload Fri Oct 13 10:07:10 2006 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#!/bin/sh
-module="ec_msr_sample"
-device="msr"
-
-# invoke rmmod with all arguments we got
-/sbin/rmmod $module $* || exit 1
-
-# Remove stale nodes
-rm -f /dev/${device} /dev/${device}0
--- a/examples/rtai/Kbuild Fri Oct 13 10:07:10 2006 +0000
+++ b/examples/rtai/Kbuild Tue Nov 07 12:13:30 2006 +0000
@@ -1,7 +1,5 @@
#------------------------------------------------------------------------------
#
-# Kbuild
-#
# $Id$
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -33,8 +31,12 @@
#
#------------------------------------------------------------------------------
+include $(src)/../../config.kbuild
+
obj-m := ec_rtai_sample.o
ec_rtai_sample-objs := rtai_sample.o
+EXTRA_CFLAGS := -I$(EC_RTAI_DIR)/include
+
#------------------------------------------------------------------------------
--- a/examples/rtai/Makefile.am Fri Oct 13 10:07:10 2006 +0000
+++ b/examples/rtai/Makefile.am Tue Nov 07 12:13:30 2006 +0000
@@ -1,7 +1,5 @@
#------------------------------------------------------------------------------
#
-# Makefile.am
-#
# IgH EtherCAT master module
#
# $Id$
@@ -43,9 +41,9 @@
clean-local:
$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" clean
-install-data-local:
- $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" \
- INSTALL_MOD_PATH="$(DESTDIR)" \
- INSTALL_MOD_DIR="ethercat" modules_install
+install-data-local: modules_install
+
+modules_install:
+ cp $(srcdir)/ec_rtai_sample.ko $(DESTDIR)$(LINUX_MOD_PATH)
#------------------------------------------------------------------------------
--- a/examples/rtai/rtai_sample.c Fri Oct 13 10:07:10 2006 +0000
+++ b/examples/rtai/rtai_sample.c Tue Nov 07 12:13:30 2006 +0000
@@ -202,7 +202,6 @@
rt_task_delete(&task);
stop_rt_timer();
- ecrt_master_deactivate(master);
ecrt_release_master(master);
rt_sem_delete(&master_sem);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/globals.h Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,72 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ Global definitions and macros.
+*/
+
+/*****************************************************************************/
+
+#ifndef _EC_GLOBALS_
+#define _EC_GLOBALS_
+
+#include "config.h"
+
+/******************************************************************************
+ * Overall macros
+ *****************************************************************************/
+
+/**
+ Helper macro for EC_STR(), literates a macro argument.
+ \param X argument to literate.
+*/
+
+#define EC_LIT(X) #X
+
+/**
+ Converts a macro argument to a string.
+ \param X argument to stringify.
+*/
+
+#define EC_STR(X) EC_LIT(X)
+
+/**
+ Master version string
+*/
+
+#define EC_MASTER_VERSION VERSION " " BRANCH " r" EC_STR(SVNREV)
+
+/*****************************************************************************/
+
+#endif
--- a/include/ecdb.h Fri Oct 13 10:07:10 2006 +0000
+++ b/include/ecdb.h Tue Nov 07 12:13:30 2006 +0000
@@ -45,6 +45,8 @@
/** \cond */
+#define Beckhoff_BK1120 0x00000002, 0x04602C22
+
#define Beckhoff_EL1004_Inputs 0x00000002, 0x03EC3052, 0x3101, 1
#define Beckhoff_EL1014_Inputs 0x00000002, 0x03F63052, 0x3101, 1
--- a/include/ecrt.h Fri Oct 13 10:07:10 2006 +0000
+++ b/include/ecrt.h Tue Nov 07 12:13:30 2006 +0000
@@ -86,6 +86,12 @@
}
ec_pdo_reg_t;
+/**
+ Direction type for ec_domain_register_pdo_range()
+*/
+
+typedef enum {EC_DIR_INPUT, EC_DIR_OUTPUT} ec_direction_t;
+
/******************************************************************************
* Master request functions
*****************************************************************************/
@@ -103,7 +109,7 @@
ec_domain_t *ecrt_master_create_domain(ec_master_t *master);
int ecrt_master_activate(ec_master_t *master);
-void ecrt_master_deactivate(ec_master_t *master);
+void ecrt_master_deactivate(ec_master_t *master); // deprecated!
void ecrt_master_prepare(ec_master_t *master);
@@ -127,9 +133,19 @@
uint16_t pdo_index,
uint8_t pdo_subindex,
void **data_ptr);
+
int ecrt_domain_register_pdo_list(ec_domain_t *domain,
const ec_pdo_reg_t *pdos);
+ec_slave_t *ecrt_domain_register_pdo_range(ec_domain_t *domain,
+ const char *address,
+ uint32_t vendor_id,
+ uint32_t product_code,
+ ec_direction_t direction,
+ uint16_t offset,
+ uint16_t length,
+ void **data_ptr);
+
void ecrt_domain_process(ec_domain_t *domain);
int ecrt_domain_state(const ec_domain_t *domain);
@@ -145,7 +161,7 @@
uint8_t sdo_subindex, uint32_t value);
int ecrt_slave_pdo_size(ec_slave_t *slave, uint16_t pdo_index,
- uint8_t pdo_subindex, size_t size);
+ uint8_t pdo_subindex, size_t size); // deprecated
/******************************************************************************
* Bitwise read/write macros
--- a/master/Kbuild Fri Oct 13 10:07:10 2006 +0000
+++ b/master/Kbuild Tue Nov 07 12:13:30 2006 +0000
@@ -1,9 +1,5 @@
#------------------------------------------------------------------------------
#
-# Kbuild
-#
-# IgH EtherCAT master module
-#
# $Id$
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -35,10 +31,13 @@
#
#------------------------------------------------------------------------------
+include $(src)/../config.kbuild
+
obj-m := ec_master.o
ec_master-objs := module.o master.o device.o slave.o datagram.o \
- domain.o mailbox.o ethernet.o fsm.o
+ domain.o mailbox.o canopen.o ethernet.o \
+ fsm_sii.o fsm_change.o fsm_coe.o fsm.o
# xmldev.o
ifeq ($(EC_DBG_IF),1)
@@ -51,6 +50,6 @@
svnversion $(src) 2>/dev/null || echo "unknown"; \
fi)
-EXTRA_CFLAGS := -DSVNREV=$(REV)
+CFLAGS_module.o := -DSVNREV=$(REV)
#------------------------------------------------------------------------------
--- a/master/Makefile.am Fri Oct 13 10:07:10 2006 +0000
+++ b/master/Makefile.am Tue Nov 07 12:13:30 2006 +0000
@@ -1,9 +1,5 @@
#------------------------------------------------------------------------------
#
-# Makefile.am
-#
-# IgH EtherCAT master module
-#
# $Id: Makefile 545 2006-09-19 13:28:40Z fp $
#
# Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
@@ -37,12 +33,16 @@
EXTRA_DIST = \
Kbuild \
+ canopen.c canopen.h \
datagram.c datagram.h \
debug.c debug.h \
device.c device.h \
domain.c domain.h \
doxygen.c \
ethernet.c ethernet.h \
+ fsm_sii.c fsm_sii.h \
+ fsm_change.c fsm_change.h \
+ fsm_coe.c fsm_coe.h \
fsm.c fsm.h \
globals.h \
mailbox.c mailbox.h \
@@ -51,16 +51,13 @@
slave.c slave.h
# xmldev.c xmldev.h
-all:
- $(MAKE) -C "$(LINUX_SOURCE_DIR)" \
- M="@abs_srcdir@" EC_DBG_IF="$(EC_DBG_IF)" modules
+modules:
+ $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_top_srcdir@" modules
+
+modules_install:
+ cp $(srcdir)/ec_master.ko $(DESTDIR)$(LINUX_MOD_PATH)
clean-local:
$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" clean
-install-data-local:
- $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" \
- INSTALL_MOD_PATH="$(DESTDIR)" \
- INSTALL_MOD_DIR="ethercat" modules_install
-
#------------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/canopen.c Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,422 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ Canopen-over-EtherCAT functions.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include "canopen.h"
+#include "master.h"
+
+/*****************************************************************************/
+
+ssize_t ec_show_sdo_attribute(struct kobject *, struct attribute *, char *);
+ssize_t ec_show_sdo_entry_attribute(struct kobject *, struct attribute *,
+ char *);
+void ec_sdo_clear(struct kobject *);
+void ec_sdo_entry_clear(struct kobject *);
+
+void ec_sdo_request_init_read(ec_sdo_request_t *, ec_sdo_t *,
+ ec_sdo_entry_t *);
+void ec_sdo_request_clear(ec_sdo_request_t *);
+
+/*****************************************************************************/
+
+/** \cond */
+
+EC_SYSFS_READ_ATTR(info);
+EC_SYSFS_READ_ATTR(value);
+
+static struct attribute *sdo_def_attrs[] = {
+ &attr_info,
+ NULL,
+};
+
+static struct sysfs_ops sdo_sysfs_ops = {
+ .show = &ec_show_sdo_attribute,
+ .store = NULL
+};
+
+static struct kobj_type ktype_ec_sdo = {
+ .release = ec_sdo_clear,
+ .sysfs_ops = &sdo_sysfs_ops,
+ .default_attrs = sdo_def_attrs
+};
+
+static struct attribute *sdo_entry_def_attrs[] = {
+ &attr_info,
+ &attr_value,
+ NULL,
+};
+
+static struct sysfs_ops sdo_entry_sysfs_ops = {
+ .show = &ec_show_sdo_entry_attribute,
+ .store = NULL
+};
+
+static struct kobj_type ktype_ec_sdo_entry = {
+ .release = ec_sdo_entry_clear,
+ .sysfs_ops = &sdo_entry_sysfs_ops,
+ .default_attrs = sdo_entry_def_attrs
+};
+
+/** \endcond */
+
+/*****************************************************************************/
+
+/**
+ SDO constructor.
+*/
+
+int ec_sdo_init(ec_sdo_t *sdo, /**< SDO */
+ uint16_t index, /**< SDO index */
+ ec_slave_t *slave /**< parent slave */
+ )
+{
+ sdo->slave = slave;
+ sdo->index = index;
+ sdo->object_code = 0x00;
+ sdo->name = NULL;
+ sdo->subindices = 0;
+ INIT_LIST_HEAD(&sdo->entries);
+
+ // init kobject and add it to the hierarchy
+ memset(&sdo->kobj, 0x00, sizeof(struct kobject));
+ kobject_init(&sdo->kobj);
+ sdo->kobj.ktype = &ktype_ec_sdo;
+ sdo->kobj.parent = &slave->sdo_kobj;
+ if (kobject_set_name(&sdo->kobj, "%4X", sdo->index)) {
+ EC_ERR("Failed to set kobj name.\n");
+ kobject_put(&sdo->kobj);
+ return -1;
+ }
+ if (kobject_add(&sdo->kobj)) {
+ EC_ERR("Failed to add SDO kobject.\n");
+ kobject_put(&sdo->kobj);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ SDO destructor.
+ Clears and frees an SDO object.
+*/
+
+void ec_sdo_destroy(ec_sdo_t *sdo /**< SDO */)
+{
+ ec_sdo_entry_t *entry, *next;
+
+ // free all entries
+ list_for_each_entry_safe(entry, next, &sdo->entries, list) {
+ list_del(&entry->list);
+ ec_sdo_entry_destroy(entry);
+ }
+
+ // destroy self
+ kobject_del(&sdo->kobj);
+ kobject_put(&sdo->kobj);
+}
+
+/*****************************************************************************/
+
+/**
+ Clear and free SDO.
+ This method is called by the kobject,
+ once there are no more references to it.
+*/
+
+void ec_sdo_clear(struct kobject *kobj /**< SDO's kobject */)
+{
+ ec_sdo_t *sdo = container_of(kobj, ec_sdo_t, kobj);
+
+ if (sdo->name) kfree(sdo->name);
+
+ kfree(sdo);
+}
+
+/*****************************************************************************/
+
+ssize_t ec_sdo_info(ec_sdo_t *sdo, /**< SDO */
+ char *buffer /**< target buffer */
+ )
+{
+ off_t off = 0;
+
+ off += sprintf(buffer + off, "Index: 0x%04X\n", sdo->index);
+ off += sprintf(buffer + off, "Name: %s\n", sdo->name ? sdo->name : "");
+ off += sprintf(buffer + off, "Subindices: %i\n", sdo->subindices);
+
+ return off;
+}
+
+/*****************************************************************************/
+
+ssize_t ec_show_sdo_attribute(struct kobject *kobj, /**< kobject */
+ struct attribute *attr,
+ char *buffer
+ )
+{
+ ec_sdo_t *sdo = container_of(kobj, ec_sdo_t, kobj);
+
+ if (attr == &attr_info) {
+ return ec_sdo_info(sdo, buffer);
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ SDO entry constructor.
+*/
+
+int ec_sdo_entry_init(ec_sdo_entry_t *entry, /**< SDO entry */
+ uint8_t subindex, /**< SDO entry subindex */
+ ec_sdo_t *sdo /**< parent SDO */
+ )
+{
+ entry->sdo = sdo;
+ entry->subindex = subindex;
+ entry->data_type = 0x0000;
+ entry->bit_length = 0;
+ entry->description = NULL;
+
+ // init kobject and add it to the hierarchy
+ memset(&entry->kobj, 0x00, sizeof(struct kobject));
+ kobject_init(&entry->kobj);
+ entry->kobj.ktype = &ktype_ec_sdo_entry;
+ entry->kobj.parent = &sdo->kobj;
+ if (kobject_set_name(&entry->kobj, "%i", entry->subindex)) {
+ EC_ERR("Failed to set kobj name.\n");
+ kobject_put(&entry->kobj);
+ return -1;
+ }
+ if (kobject_add(&entry->kobj)) {
+ EC_ERR("Failed to add entry kobject.\n");
+ kobject_put(&entry->kobj);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ SDO entry destructor.
+ Clears and frees an SDO entry object.
+*/
+
+void ec_sdo_entry_destroy(ec_sdo_entry_t *entry /**< SDO entry */)
+{
+ // destroy self
+ kobject_del(&entry->kobj);
+ kobject_put(&entry->kobj);
+}
+
+/*****************************************************************************/
+
+/**
+ Clear and free SDO entry.
+ This method is called by the kobject,
+ once there are no more references to it.
+*/
+
+void ec_sdo_entry_clear(struct kobject *kobj /**< SDO entry's kobject */)
+{
+ ec_sdo_entry_t *entry = container_of(kobj, ec_sdo_entry_t, kobj);
+
+ if (entry->description) kfree(entry->description);
+
+ kfree(entry);
+}
+
+/*****************************************************************************/
+
+ssize_t ec_sdo_entry_info(ec_sdo_entry_t *entry, /**< SDO entry */
+ char *buffer /**< target buffer */
+ )
+{
+ off_t off = 0;
+
+ off += sprintf(buffer + off, "Subindex: 0x%02X\n", entry->subindex);
+ off += sprintf(buffer + off, "Description: %s\n",
+ entry->description ? entry->description : "");
+ off += sprintf(buffer + off, "Data type: 0x%04X\n", entry->data_type);
+ off += sprintf(buffer + off, "Bit length: %i\n", entry->bit_length);
+
+ return off;
+}
+
+/*****************************************************************************/
+
+ssize_t ec_sdo_entry_format_data(ec_sdo_entry_t *entry, /**< SDO entry */
+ ec_sdo_request_t *request, /**< SDO request */
+ char *buffer /**< target buffer */
+ )
+{
+ off_t off = 0;
+ unsigned int i;
+
+ if (entry->data_type == 0x0002 && entry->bit_length == 8) { // int8
+ off += sprintf(buffer + off, "%i\n", *((int8_t *) request->data));
+ }
+ else if (entry->data_type == 0x0003 && entry->bit_length == 16) { // int16
+ off += sprintf(buffer + off, "%i\n", *((int16_t *) request->data));
+ }
+ else if (entry->data_type == 0x0004 && entry->bit_length == 32) { // int32
+ off += sprintf(buffer + off, "%i\n", *((int32_t *) request->data));
+ }
+ else if (entry->data_type == 0x0005 && entry->bit_length == 8) { // uint8
+ off += sprintf(buffer + off, "%i\n", *((uint8_t *) request->data));
+ }
+ else if (entry->data_type == 0x0006 && entry->bit_length == 16) { // uint16
+ off += sprintf(buffer + off, "%i\n", *((uint16_t *) request->data));
+ }
+ else if (entry->data_type == 0x0007 && entry->bit_length == 32) { // uint32
+ off += sprintf(buffer + off, "%i\n", *((uint32_t *) request->data));
+ }
+ else if (entry->data_type == 0x0009) { // string
+ off += sprintf(buffer + off, "%s\n", request->data);
+ }
+ else {
+ for (i = 0; i < request->size; i++)
+ off += sprintf(buffer + off, "%02X (%c)\n",
+ request->data[i], request->data[i]);
+ }
+
+ return off;
+}
+
+/*****************************************************************************/
+
+ssize_t ec_sdo_entry_read_value(ec_sdo_entry_t *entry, /**< SDO entry */
+ char *buffer /**< target buffer */
+ )
+{
+ ec_sdo_t *sdo = entry->sdo;
+ ec_master_t *master = sdo->slave->master;
+ off_t off = 0;
+ ec_sdo_request_t request;
+
+ if (down_interruptible(&master->sdo_sem)) {
+ // interrupted by signal
+ return -ERESTARTSYS;
+ }
+
+ ec_sdo_request_init_read(&request, sdo, entry);
+
+ // this is necessary, because the completion object
+ // is completed by the ec_master_flush_sdo_requests() function.
+ INIT_COMPLETION(master->sdo_complete);
+
+ master->sdo_request = &request;
+ master->sdo_seq_user++;
+ master->sdo_timer.expires = jiffies + 10;
+ add_timer(&master->sdo_timer);
+
+ wait_for_completion(&master->sdo_complete);
+
+ master->sdo_request = NULL;
+ up(&master->sdo_sem);
+
+ if (request.return_code == 1 && request.data) {
+ off += ec_sdo_entry_format_data(entry, &request, buffer);
+ }
+ else {
+ off = -EINVAL;
+ }
+
+ ec_sdo_request_clear(&request);
+ return off;
+}
+
+/*****************************************************************************/
+
+ssize_t ec_show_sdo_entry_attribute(struct kobject *kobj, /**< kobject */
+ struct attribute *attr,
+ char *buffer
+ )
+{
+ ec_sdo_entry_t *entry = container_of(kobj, ec_sdo_entry_t, kobj);
+
+ if (attr == &attr_info) {
+ return ec_sdo_entry_info(entry, buffer);
+ }
+ else if (attr == &attr_value) {
+ return ec_sdo_entry_read_value(entry, buffer);
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ SDO request constructor.
+*/
+
+void ec_sdo_request_init_read(ec_sdo_request_t *req, /**< SDO request */
+ ec_sdo_t *sdo, /**< SDO */
+ ec_sdo_entry_t *entry /**< SDO entry */
+ )
+{
+ req->sdo = sdo;
+ req->entry = entry;
+ req->data = NULL;
+ req->size = 0;
+ req->return_code = 0;
+}
+
+/*****************************************************************************/
+
+/**
+ SDO request destructor.
+*/
+
+void ec_sdo_request_clear(ec_sdo_request_t *req /**< SDO request */)
+{
+ if (req->data) kfree(req->data);
+}
+
+/*****************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/canopen.h Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,130 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT CANopen structures.
+*/
+
+/*****************************************************************************/
+
+#ifndef _EC_CANOPEN_H_
+#define _EC_CANOPEN_H_
+
+#include <linux/list.h>
+#include <linux/kobject.h>
+
+#include "globals.h"
+#include "slave.h"
+
+/*****************************************************************************/
+
+/**
+ CANopen SDO.
+*/
+
+typedef struct
+{
+ struct kobject kobj; /**< kobject */
+ struct list_head list; /**< list item */
+ ec_slave_t *slave; /**< parent slave */
+ uint16_t index; /**< SDO index */
+ uint8_t object_code; /**< object code */
+ char *name; /**< SDO name */
+ uint8_t subindices; /**< subindices */
+ struct list_head entries; /**< entry list */
+}
+ec_sdo_t;
+
+/*****************************************************************************/
+
+/**
+ CANopen SDO entry.
+*/
+
+typedef struct
+{
+ struct kobject kobj; /**< kobject */
+ struct list_head list; /**< list item */
+ ec_sdo_t *sdo; /**< parent SDO */
+ uint8_t subindex; /**< entry subindex */
+ uint16_t data_type; /**< entry data type */
+ uint16_t bit_length; /**< entry length in bit */
+ char *description; /**< entry description */
+}
+ec_sdo_entry_t;
+
+/*****************************************************************************/
+
+/**
+ CANopen SDO configuration data.
+*/
+
+typedef struct
+{
+ struct list_head list; /**< list item */
+ uint16_t index; /**< SDO index */
+ uint8_t subindex; /**< SDO subindex */
+ uint8_t *data; /**< pointer to SDO data */
+ size_t size; /**< size of SDO data */
+}
+ec_sdo_data_t;
+
+/*****************************************************************************/
+
+/**
+ CANopen SDO request.
+*/
+
+typedef struct
+{
+ struct list_head queue; /**< list item */
+ ec_sdo_t *sdo;
+ ec_sdo_entry_t *entry;
+ uint8_t *data; /**< pointer to SDO data */
+ size_t size; /**< size of SDO data */
+ int return_code;
+}
+ec_sdo_request_t;
+
+/*****************************************************************************/
+
+int ec_sdo_init(ec_sdo_t *, uint16_t, ec_slave_t *);
+void ec_sdo_destroy(ec_sdo_t *);
+
+int ec_sdo_entry_init(ec_sdo_entry_t *, uint8_t, ec_sdo_t *);
+void ec_sdo_entry_destroy(ec_sdo_entry_t *);
+
+/*****************************************************************************/
+
+#endif
--- a/master/datagram.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/datagram.c Tue Nov 07 12:13:30 2006 +0000
@@ -69,6 +69,7 @@
void ec_datagram_init(ec_datagram_t *datagram /**< EtherCAT datagram */)
{
+ INIT_LIST_HEAD(&datagram->queue); // mark as unqueued
datagram->type = EC_DATAGRAM_NONE;
datagram->address.logical = 0x00000000;
datagram->data = NULL;
@@ -77,7 +78,11 @@
datagram->index = 0x00;
datagram->working_counter = 0x00;
datagram->state = EC_DATAGRAM_INIT;
+ datagram->cycles_queued = 0;
datagram->cycles_sent = 0;
+ datagram->jiffies_sent = 0;
+ datagram->cycles_received = 0;
+ datagram->jiffies_received = 0;
}
/*****************************************************************************/
--- a/master/datagram.h Fri Oct 13 10:07:10 2006 +0000
+++ b/master/datagram.h Tue Nov 07 12:13:30 2006 +0000
@@ -110,6 +110,7 @@
{
struct list_head list; /**< needed by domain datagram lists */
struct list_head queue; /**< master datagram queue item */
+ struct list_head sent; /**< master list item for sent datagrams */
ec_datagram_type_t type; /**< datagram type (APRD, BWR, etc) */
ec_address_t address; /**< receipient address */
uint8_t *data; /**< datagram data */
@@ -118,7 +119,11 @@
uint8_t index; /**< datagram index (set by master) */
uint16_t working_counter; /**< working counter */
ec_datagram_state_t state; /**< datagram state */
+ cycles_t cycles_queued; /**< time, the datagram was queued */
cycles_t cycles_sent; /**< time, the datagram was sent */
+ unsigned long jiffies_sent; /**< jiffies when datagram was sent */
+ cycles_t cycles_received; /**< time, the datagram was received */
+ unsigned long jiffies_received; /**< jiffies when datagram was received */
}
ec_datagram_t;
--- a/master/debug.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/debug.c Tue Nov 07 12:13:30 2006 +0000
@@ -49,6 +49,7 @@
// net_device functions
int ec_dbgdev_open(struct net_device *);
int ec_dbgdev_stop(struct net_device *);
+int ec_dbgdev_tx(struct sk_buff *, struct net_device *);
struct net_device_stats *ec_dbgdev_stats(struct net_device *);
/*****************************************************************************/
@@ -74,6 +75,7 @@
// initialize net_device
dbg->dev->open = ec_dbgdev_open;
dbg->dev->stop = ec_dbgdev_stop;
+ dbg->dev->hard_start_xmit = ec_dbgdev_tx;
dbg->dev->get_stats = ec_dbgdev_stats;
// initialize private data
@@ -156,7 +158,7 @@
{
ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev));
dbg->opened = 1;
- EC_INFO("debug interface %s opened.\n", dev->name);
+ EC_INFO("Debug interface %s opened.\n", dev->name);
return 0;
}
@@ -170,7 +172,24 @@
{
ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev));
dbg->opened = 0;
- EC_INFO("debug interface %s stopped.\n", dev->name);
+ EC_INFO("Debug interface %s stopped.\n", dev->name);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Transmits data via the virtual network device.
+*/
+
+int ec_dbgdev_tx(struct sk_buff *skb, /**< transmit socket buffer */
+ struct net_device *dev /**< EoE net_device */
+ )
+{
+ ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev));
+
+ dev_kfree_skb(skb);
+ dbg->stats.tx_dropped++;
return 0;
}
--- a/master/device.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/device.c Tue Nov 07 12:13:30 2006 +0000
@@ -230,6 +230,8 @@
void ec_device_call_isr(ec_device_t *device /**< EtherCAT device */)
{
+ device->cycles_isr = get_cycles();
+ device->jiffies_isr = jiffies;
if (likely(device->isr)) device->isr(0, device->dev, NULL);
}
--- a/master/device.h Fri Oct 13 10:07:10 2006 +0000
+++ b/master/device.h Tue Nov 07 12:13:30 2006 +0000
@@ -66,6 +66,8 @@
uint8_t open; /**< true, if the net_device has been opened */
struct sk_buff *tx_skb; /**< transmit socket buffer */
ec_isr_t isr; /**< pointer to the device's interrupt service routine */
+ cycles_t cycles_isr; /**< cycles of last ISR call */
+ unsigned long jiffies_isr; /**< jiffies of last ISR call */
struct module *module; /**< pointer to the device's owning module */
uint8_t link_state; /**< device link state */
#ifdef EC_DBG_IF
--- a/master/domain.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/domain.c Tue Nov 07 12:13:30 2006 +0000
@@ -62,6 +62,7 @@
/*****************************************************************************/
+void ec_domain_clear(struct kobject *);
void ec_domain_clear_data_regs(ec_domain_t *);
ssize_t ec_show_domain_attribute(struct kobject *, struct attribute *, char *);
@@ -119,6 +120,12 @@
domain->kobj.parent = &master->kobj;
if (kobject_set_name(&domain->kobj, "domain%i", index)) {
EC_ERR("Failed to set kobj name.\n");
+ kobject_put(&domain->kobj);
+ return -1;
+ }
+ if (kobject_add(&domain->kobj)) {
+ EC_ERR("Failed to add domain kobject.\n");
+ kobject_put(&domain->kobj);
return -1;
}
@@ -129,6 +136,30 @@
/**
Domain destructor.
+ Clears and frees a domain object.
+*/
+
+void ec_domain_destroy(ec_domain_t *domain /**< EtherCAT domain */)
+{
+ ec_datagram_t *datagram;
+
+ // dequeue datagrams
+ list_for_each_entry(datagram, &domain->datagrams, list) {
+ if (!list_empty(&datagram->queue)) // datagram queued?
+ list_del_init(&datagram->queue);
+ }
+
+ // destroy self
+ kobject_del(&domain->kobj);
+ kobject_put(&domain->kobj);
+}
+
+/*****************************************************************************/
+
+/**
+ Clear and free domain.
+ This method is called by the kobject,
+ once there are no more references to it.
*/
void ec_domain_clear(struct kobject *kobj /**< kobject of the domain */)
@@ -138,8 +169,6 @@
domain = container_of(kobj, ec_domain_t, kobj);
- EC_INFO("Clearing domain %i.\n", domain->index);
-
list_for_each_entry_safe(datagram, next, &domain->datagrams, list) {
ec_datagram_clear(datagram);
kfree(datagram);
@@ -187,7 +216,7 @@
return -1;
}
- // Calculate offset for process data pointer
+ // Calculate offset (in sync manager) for process data pointer
bit_offset = 0;
byte_offset = 0;
list_for_each_entry(other_pdo, &slave->sii_pdos, list) {
@@ -221,6 +250,87 @@
data_reg->data_ptr = data_ptr;
list_add_tail(&data_reg->list, &domain->data_regs);
+
+ ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Registeres a PDO range.
+ \return 0 in case of success, else < 0
+*/
+
+int ec_domain_reg_pdo_range(ec_domain_t *domain, /**< EtherCAT domain */
+ ec_slave_t *slave, /**< slave */
+ ec_direction_t dir, /**< data direction */
+ uint16_t offset, /**< offset */
+ uint16_t length, /**< length */
+ void **data_ptr /**< pointer to the process data
+ pointer */
+ )
+{
+ ec_data_reg_t *data_reg;
+ ec_sii_sync_t *sync;
+ unsigned int sync_found, sync_index;
+ uint16_t sync_length;
+
+ switch (dir) {
+ case EC_DIR_OUTPUT: sync_index = 2; break;
+ case EC_DIR_INPUT: sync_index = 3; break;
+ default:
+ EC_ERR("Invalid direction!\n");
+ return -1;
+ }
+
+ // Find sync manager
+ sync_found = 0;
+ list_for_each_entry(sync, &slave->sii_syncs, list) {
+ if (sync->index == sync_index) {
+ sync_found = 1;
+ break;
+ }
+ }
+
+ if (!sync_found) {
+ EC_ERR("No sync manager found for PDO range.\n");
+ return -1;
+ }
+
+ // Allocate memory for data registration object
+ if (!(data_reg =
+ (ec_data_reg_t *) kmalloc(sizeof(ec_data_reg_t), GFP_KERNEL))) {
+ EC_ERR("Failed to allocate data registration.\n");
+ return -1;
+ }
+
+ if (ec_slave_prepare_fmmu(slave, domain, sync)) {
+ EC_ERR("FMMU configuration failed.\n");
+ kfree(data_reg);
+ return -1;
+ }
+
+ data_reg->slave = slave;
+ data_reg->sync = sync;
+ data_reg->sync_offset = offset;
+ data_reg->data_ptr = data_ptr;
+
+ // estimate sync manager length
+ sync_length = offset + length;
+ if (sync->est_length < sync_length) {
+ sync->est_length = sync_length;
+ if (domain->master->debug_level) {
+ EC_DBG("Estimating length of sync manager %i of slave %i to %i.\n",
+ sync->index, slave->ring_position, sync_length);
+ }
+ }
+
+ list_add_tail(&data_reg->list, &domain->data_regs);
+
+ ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
+
return 0;
}
@@ -372,7 +482,7 @@
Places all process data datagrams in the masters datagram queue.
*/
-void ec_domain_queue(ec_domain_t *domain /**< EtherCAT domain */)
+void ec_domain_queue_datagrams(ec_domain_t *domain /**< EtherCAT domain */)
{
ec_datagram_t *datagram;
@@ -438,29 +548,19 @@
master = domain->master;
- // translate address
+ // translate address and validate slave
if (!(slave = ecrt_master_get_slave(master, address))) return NULL;
-
- if (vendor_id != slave->sii_vendor_id ||
- product_code != slave->sii_product_code) {
- EC_ERR("Invalid slave type at position %i - Requested: 0x%08X 0x%08X,"
- " found: 0x%08X 0x%08X\".\n", slave->ring_position, vendor_id,
- product_code, slave->sii_vendor_id, slave->sii_product_code);
- return NULL;
- }
-
- if (!data_ptr) {
- // data_ptr is NULL => mark slave as "registered" (do not warn)
- slave->registered = 1;
- }
+ if (ec_slave_validate(slave, vendor_id, product_code)) return NULL;
+
+ if (!data_ptr) return slave;
list_for_each_entry(pdo, &slave->sii_pdos, list) {
list_for_each_entry(entry, &pdo->entries, list) {
if (entry->index != pdo_index
|| entry->subindex != pdo_subindex) continue;
- if (data_ptr) {
- ec_domain_reg_pdo_entry(domain, slave, pdo, entry, data_ptr);
+ if (ec_domain_reg_pdo_entry(domain, slave, pdo, entry, data_ptr)) {
+ return NULL;
}
return slave;
@@ -469,7 +569,6 @@
EC_ERR("Slave %i does not provide PDO 0x%04X:%i.\n",
slave->ring_position, pdo_index, pdo_subindex);
- slave->registered = 0;
return NULL;
}
@@ -505,6 +604,54 @@
/*****************************************************************************/
/**
+ Registers a PDO range in a domain.
+ - If \a data_ptr is NULL, the slave is only validated.
+ \return pointer to the slave on success, else NULL
+ \ingroup RealtimeInterface
+*/
+
+ec_slave_t *ecrt_domain_register_pdo_range(ec_domain_t *domain,
+ /**< EtherCAT domain */
+ const char *address,
+ /**< ASCII address of the slave,
+ see ecrt_master_get_slave() */
+ uint32_t vendor_id,
+ /**< vendor ID */
+ uint32_t product_code,
+ /**< product code */
+ ec_direction_t direction,
+ /**< data direction */
+ uint16_t offset,
+ /**< offset in slave's PDO range */
+ uint16_t length,
+ /**< length of this range */
+ void **data_ptr
+ /**< address of the process data
+ pointer */
+ )
+{
+ ec_slave_t *slave;
+ ec_master_t *master;
+
+ master = domain->master;
+
+ // translate address and validate slave
+ if (!(slave = ecrt_master_get_slave(master, address))) return NULL;
+ if (ec_slave_validate(slave, vendor_id, product_code)) return NULL;
+
+ if (!data_ptr) return slave;
+
+ if (ec_domain_reg_pdo_range(domain, slave,
+ direction, offset, length, data_ptr)) {
+ return NULL;
+ }
+
+ return slave;
+}
+
+/*****************************************************************************/
+
+/**
Processes received process data and requeues the domain datagram(s).
\ingroup RealtimeInterface
*/
@@ -545,7 +692,7 @@
domain->working_counter_changes = 0;
}
- ec_domain_queue(domain);
+ ec_domain_queue_datagrams(domain);
}
/*****************************************************************************/
@@ -567,6 +714,7 @@
EXPORT_SYMBOL(ecrt_domain_register_pdo);
EXPORT_SYMBOL(ecrt_domain_register_pdo_list);
+EXPORT_SYMBOL(ecrt_domain_register_pdo_range);
EXPORT_SYMBOL(ecrt_domain_process);
EXPORT_SYMBOL(ecrt_domain_state);
--- a/master/domain.h Fri Oct 13 10:07:10 2006 +0000
+++ b/master/domain.h Tue Nov 07 12:13:30 2006 +0000
@@ -76,9 +76,10 @@
/*****************************************************************************/
int ec_domain_init(ec_domain_t *, ec_master_t *, unsigned int);
-void ec_domain_clear(struct kobject *);
+void ec_domain_destroy(ec_domain_t *);
+
int ec_domain_alloc(ec_domain_t *, uint32_t);
-void ec_domain_queue(ec_domain_t *);
+void ec_domain_queue_datagrams(ec_domain_t *);
/*****************************************************************************/
--- a/master/ethernet.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/ethernet.c Tue Nov 07 12:13:30 2006 +0000
@@ -627,10 +627,9 @@
eoe->tx_queue_active = 1;
EC_INFO("%s opened.\n", dev->name);
if (!eoe->slave)
- EC_WARN("device %s is not coupled to any EoE slave!\n", dev->name);
+ EC_WARN("Device %s is not coupled to any EoE slave!\n", dev->name);
else {
- eoe->slave->requested_state = EC_SLAVE_STATE_OP;
- eoe->slave->error_flag = 0;
+ ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
}
return 0;
}
@@ -650,10 +649,9 @@
ec_eoe_flush(eoe);
EC_INFO("%s stopped.\n", dev->name);
if (!eoe->slave)
- EC_WARN("device %s is not coupled to any EoE slave!\n", dev->name);
+ EC_WARN("Device %s is not coupled to any EoE slave!\n", dev->name);
else {
- eoe->slave->requested_state = EC_SLAVE_STATE_INIT;
- eoe->slave->error_flag = 0;
+ ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_PREOP);
}
return 0;
}
--- a/master/fsm.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/fsm.c Tue Nov 07 12:13:30 2006 +0000
@@ -48,19 +48,17 @@
void ec_fsm_master_start(ec_fsm_t *);
void ec_fsm_master_broadcast(ec_fsm_t *);
void ec_fsm_master_read_states(ec_fsm_t *);
+void ec_fsm_master_acknowledge(ec_fsm_t *);
void ec_fsm_master_validate_vendor(ec_fsm_t *);
void ec_fsm_master_validate_product(ec_fsm_t *);
void ec_fsm_master_rewrite_addresses(ec_fsm_t *);
void ec_fsm_master_configure_slave(ec_fsm_t *);
void ec_fsm_master_scan_slaves(ec_fsm_t *);
void ec_fsm_master_write_eeprom(ec_fsm_t *);
-
-void ec_fsm_startup_start(ec_fsm_t *);
-void ec_fsm_startup_broadcast(ec_fsm_t *);
-void ec_fsm_startup_scan(ec_fsm_t *);
-
-void ec_fsm_configuration_start(ec_fsm_t *);
-void ec_fsm_configuration_conf(ec_fsm_t *);
+void ec_fsm_master_sdodict(ec_fsm_t *);
+void ec_fsm_master_sdo_request(ec_fsm_t *);
+void ec_fsm_master_end(ec_fsm_t *);
+void ec_fsm_master_error(ec_fsm_t *);
void ec_fsm_slavescan_start(ec_fsm_t *);
void ec_fsm_slavescan_address(ec_fsm_t *);
@@ -70,37 +68,26 @@
void ec_fsm_slavescan_eeprom_size(ec_fsm_t *);
void ec_fsm_slavescan_eeprom_data(ec_fsm_t *);
-void ec_fsm_slaveconf_init(ec_fsm_t *);
-void ec_fsm_slaveconf_sync(ec_fsm_t *);
-void ec_fsm_slaveconf_preop(ec_fsm_t *);
-void ec_fsm_slaveconf_fmmu(ec_fsm_t *);
-void ec_fsm_slaveconf_sdoconf(ec_fsm_t *);
-void ec_fsm_slaveconf_saveop(ec_fsm_t *);
-void ec_fsm_slaveconf_op(ec_fsm_t *);
-
-void ec_fsm_sii_start_reading(ec_fsm_t *);
-void ec_fsm_sii_read_check(ec_fsm_t *);
-void ec_fsm_sii_read_fetch(ec_fsm_t *);
-void ec_fsm_sii_start_writing(ec_fsm_t *);
-void ec_fsm_sii_write_check(ec_fsm_t *);
-void ec_fsm_sii_write_check2(ec_fsm_t *);
-
-void ec_fsm_change_start(ec_fsm_t *);
-void ec_fsm_change_check(ec_fsm_t *);
-void ec_fsm_change_status(ec_fsm_t *);
-void ec_fsm_change_code(ec_fsm_t *);
-void ec_fsm_change_ack(ec_fsm_t *);
-void ec_fsm_change_check_ack(ec_fsm_t *);
-
-void ec_fsm_coe_down_start(ec_fsm_t *);
-void ec_fsm_coe_down_request(ec_fsm_t *);
-void ec_fsm_coe_down_check(ec_fsm_t *);
-void ec_fsm_coe_down_response(ec_fsm_t *);
-
-void ec_fsm_end(ec_fsm_t *);
-void ec_fsm_error(ec_fsm_t *);
-
-void ec_canopen_abort_msg(uint32_t);
+void ec_fsm_slaveconf_state_start(ec_fsm_t *);
+void ec_fsm_slaveconf_state_init(ec_fsm_t *);
+void ec_fsm_slaveconf_state_clear_fmmus(ec_fsm_t *);
+void ec_fsm_slaveconf_state_sync(ec_fsm_t *);
+void ec_fsm_slaveconf_state_preop(ec_fsm_t *);
+void ec_fsm_slaveconf_state_sync2(ec_fsm_t *);
+void ec_fsm_slaveconf_state_fmmu(ec_fsm_t *);
+void ec_fsm_slaveconf_state_sdoconf(ec_fsm_t *);
+void ec_fsm_slaveconf_state_saveop(ec_fsm_t *);
+void ec_fsm_slaveconf_state_op(ec_fsm_t *);
+
+void ec_fsm_slaveconf_enter_sync(ec_fsm_t *);
+void ec_fsm_slaveconf_enter_preop(ec_fsm_t *);
+void ec_fsm_slaveconf_enter_sync2(ec_fsm_t *);
+void ec_fsm_slaveconf_enter_fmmu(ec_fsm_t *);
+void ec_fsm_slaveconf_enter_sdoconf(ec_fsm_t *);
+void ec_fsm_slaveconf_enter_saveop(ec_fsm_t *);
+
+void ec_fsm_slave_state_end(ec_fsm_t *);
+void ec_fsm_slave_state_error(ec_fsm_t *);
/*****************************************************************************/
@@ -124,6 +111,11 @@
return -1;
}
+ // init sub-state-machines
+ ec_fsm_sii_init(&fsm->fsm_sii, &fsm->datagram);
+ ec_fsm_change_init(&fsm->fsm_change, &fsm->datagram);
+ ec_fsm_coe_init(&fsm->fsm_coe, &fsm->datagram);
+
return 0;
}
@@ -135,283 +127,53 @@
void ec_fsm_clear(ec_fsm_t *fsm /**< finite state machine */)
{
+ // clear sub-state machines
+ ec_fsm_sii_clear(&fsm->fsm_sii);
+ ec_fsm_change_clear(&fsm->fsm_change);
+ ec_fsm_coe_clear(&fsm->fsm_coe);
+
ec_datagram_clear(&fsm->datagram);
}
/*****************************************************************************/
/**
- Resets the state machine.
-*/
-
-void ec_fsm_reset(ec_fsm_t *fsm /**< finite state machine */)
-{
- fsm->master_state = ec_fsm_master_start;
- fsm->master_slaves_responding = 0;
- fsm->master_slave_states = EC_SLAVE_STATE_UNKNOWN;
-}
-
-/*****************************************************************************/
-
-/**
Executes the current state of the state machine.
-*/
-
-void ec_fsm_execute(ec_fsm_t *fsm /**< finite state machine */)
+ \return false, if state machine has terminated
+*/
+
+int ec_fsm_exec(ec_fsm_t *fsm /**< finite state machine */)
{
fsm->master_state(fsm);
-}
-
-/*****************************************************************************/
-
-/**
- Initializes the master startup state machine.
-*/
-
-void ec_fsm_startup(ec_fsm_t *fsm)
-{
- fsm->master_state = ec_fsm_startup_start;
-}
-
-/*****************************************************************************/
-
-/**
- Returns the running state of the master startup state machine.
- \return non-zero if not terminated yet.
-*/
-
-int ec_fsm_startup_running(ec_fsm_t *fsm /**< Finite state machine */)
-{
- return fsm->master_state != ec_fsm_end &&
- fsm->master_state != ec_fsm_error;
-}
-
-/*****************************************************************************/
-
-/**
- Returns, if the master startup state machine terminated with success.
- \return non-zero if successful.
-*/
-
-int ec_fsm_startup_success(ec_fsm_t *fsm /**< Finite state machine */)
-{
- return fsm->master_state == ec_fsm_end;
-}
-
-/*****************************************************************************/
-
-/**
- Initializes the master configuration state machine.
-*/
-
-void ec_fsm_configuration(ec_fsm_t *fsm)
-{
- fsm->master_state = ec_fsm_configuration_start;
-}
-
-/*****************************************************************************/
-
-/**
- Returns the running state of the master configuration state machine.
- \return non-zero if not terminated yet.
-*/
-
-int ec_fsm_configuration_running(ec_fsm_t *fsm /**< Finite state machine */)
-{
- return fsm->master_state != ec_fsm_end &&
- fsm->master_state != ec_fsm_error;
-}
-
-/*****************************************************************************/
-
-/**
- Returns, if the master confuguration state machine terminated with success.
- \return non-zero if successful.
-*/
-
-int ec_fsm_configuration_success(ec_fsm_t *fsm /**< Finite state machine */)
-{
- return fsm->master_state == ec_fsm_end;
+
+ return ec_fsm_running(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+ \return false, if state machine has terminated
+*/
+
+int ec_fsm_running(ec_fsm_t *fsm /**< finite state machine */)
+{
+ return fsm->master_state != ec_fsm_master_end
+ && fsm->master_state != ec_fsm_master_error;
+}
+
+/*****************************************************************************/
+
+/**
+ \return true, if the master state machine terminated gracefully
+*/
+
+int ec_fsm_success(ec_fsm_t *fsm /**< finite state machine */)
+{
+ return fsm->master_state == ec_fsm_master_end;
}
/******************************************************************************
- * master startup state machine
- *****************************************************************************/
-
-/**
- Master state: START.
- Starts with getting slave count and slave states.
-*/
-
-void ec_fsm_startup_start(ec_fsm_t *fsm)
-{
- ec_datagram_brd(&fsm->datagram, 0x0130, 2);
- ec_master_queue_datagram(fsm->master, &fsm->datagram);
- fsm->master_state = ec_fsm_startup_broadcast;
-}
-
-/*****************************************************************************/
-
-/**
- Master state: BROADCAST.
- Processes the broadcast read slave count and slaves states.
-*/
-
-void ec_fsm_startup_broadcast(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- unsigned int i;
- ec_slave_t *slave;
- ec_master_t *master = fsm->master;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED) {
- EC_ERR("Failed tor receive broadcast datagram.\n");
- fsm->master_state = ec_fsm_error;
- return;
- }
-
- EC_INFO("Scanning bus.\n");
-
- ec_master_clear_slaves(master);
-
- master->slave_count = datagram->working_counter;
-
- if (!master->slave_count) {
- // no slaves present -> finish state machine.
- fsm->master_state = ec_fsm_end;
- return;
- }
-
- // init slaves
- for (i = 0; i < master->slave_count; i++) {
- if (!(slave = (ec_slave_t *) kmalloc(sizeof(ec_slave_t),
- GFP_KERNEL))) {
- EC_ERR("Failed to allocate slave %i!\n", i);
- fsm->master_state = ec_fsm_error;
- return;
- }
-
- if (ec_slave_init(slave, master, i, i + 1)) {
- fsm->master_state = ec_fsm_error;
- return;
- }
-
- if (kobject_add(&slave->kobj)) {
- EC_ERR("Failed to add kobject.\n");
- kobject_put(&slave->kobj); // free
- fsm->master_state = ec_fsm_error;
- return;
- }
-
- list_add_tail(&slave->list, &master->slaves);
- }
-
- // begin scanning of slaves
- fsm->slave = list_entry(master->slaves.next, ec_slave_t, list);
- fsm->slave_state = ec_fsm_slavescan_start;
- fsm->master_state = ec_fsm_startup_scan;
- fsm->master_state(fsm); // execute immediately
- return;
-}
-
-/*****************************************************************************/
-
-/**
- Master state: SCAN.
- Executes the sub-statemachine for the scanning of a slave.
-*/
-
-void ec_fsm_startup_scan(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_master_t *master = fsm->master;
- ec_slave_t *slave = fsm->slave;
-
- fsm->slave_state(fsm); // execute slave state machine
-
- if (fsm->slave_state == ec_fsm_error) {
- EC_ERR("Slave scanning failed.\n");
- fsm->master_state = ec_fsm_error;
- return;
- }
-
- if (fsm->slave_state != ec_fsm_end) return;
-
- // another slave to scan?
- if (slave->list.next != &master->slaves) {
- fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list);
- fsm->slave_state = ec_fsm_slavescan_start;
- fsm->slave_state(fsm); // execute immediately
- return;
- }
-
- EC_INFO("Bus scanning completed.\n");
-
- ec_master_calc_addressing(master);
-
- fsm->master_state = ec_fsm_end;
-}
-
-/******************************************************************************
- * master configuration state machine
- *****************************************************************************/
-
-/**
- Master configuration state machine: START.
-*/
-
-void ec_fsm_configuration_start(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_master_t *master = fsm->master;
-
- if (list_empty(&master->slaves)) {
- fsm->master_state = ec_fsm_end;
- return;
- }
-
- // begin configuring slaves
- fsm->slave = list_entry(master->slaves.next, ec_slave_t, list);
- fsm->slave_state = ec_fsm_slaveconf_init;
- fsm->change_new = EC_SLAVE_STATE_INIT;
- fsm->change_state = ec_fsm_change_start;
- fsm->master_state = ec_fsm_configuration_conf;
- fsm->master_state(fsm); // execute immediately
-}
-
-/*****************************************************************************/
-
-/**
- Master state: CONF.
-*/
-
-void ec_fsm_configuration_conf(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_master_t *master = fsm->master;
- ec_slave_t *slave = fsm->slave;
-
- fsm->slave_state(fsm); // execute slave's state machine
-
- if (fsm->slave_state == ec_fsm_error) {
- fsm->master_state = ec_fsm_error;
- return;
- }
-
- if (fsm->slave_state != ec_fsm_end) return;
-
- // another slave to configure?
- if (slave->list.next != &master->slaves) {
- fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list);
- fsm->slave_state = ec_fsm_slaveconf_init;
- fsm->change_new = EC_SLAVE_STATE_INIT;
- fsm->change_state = ec_fsm_change_start;
- fsm->master_state(fsm); // execute immediately
- return;
- }
-
- fsm->master_state = ec_fsm_end;
-}
-
-/******************************************************************************
- * operation / idle state machine
+ * operation/idle state machine
*****************************************************************************/
/**
@@ -447,8 +209,10 @@
slave->online = 0;
}
}
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
+ else {
+ EC_ERR("Failed to receive broadcast datagram.\n");
+ }
+ fsm->master_state = ec_fsm_master_error;
return;
}
@@ -475,24 +239,22 @@
}
if (states_change) {
- char states[25];
+ char states[EC_STATE_STRING_SIZE];
ec_state_string(fsm->master_slave_states, states);
EC_INFO("Slave states: %s.\n", states);
}
// topology change in idle mode: clear all slaves and scan the bus
if (topology_change && master->mode == EC_MASTER_MODE_IDLE) {
- EC_INFO("Scanning bus.\n");
ec_master_eoe_stop(master);
- ec_master_clear_slaves(master);
+ ec_master_destroy_slaves(master);
master->slave_count = datagram->working_counter;
if (!master->slave_count) {
// no slaves present -> finish state machine.
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
+ fsm->master_state = ec_fsm_master_end;
return;
}
@@ -501,32 +263,23 @@
if (!(slave = (ec_slave_t *) kmalloc(sizeof(ec_slave_t),
GFP_ATOMIC))) {
EC_ERR("Failed to allocate slave %i!\n", i);
- ec_master_clear_slaves(master);
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
+ ec_master_destroy_slaves(master);
+ fsm->master_state = ec_fsm_master_error;
return;
}
if (ec_slave_init(slave, master, i, i + 1)) {
// freeing of "slave" already done
- ec_master_clear_slaves(master);
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
+ ec_master_destroy_slaves(master);
+ fsm->master_state = ec_fsm_master_error;
return;
}
- if (kobject_add(&slave->kobj)) {
- EC_ERR("Failed to add kobject.\n");
- kobject_put(&slave->kobj); // free
- ec_master_clear_slaves(master);
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
- return;
- }
-
list_add_tail(&slave->list, &master->slaves);
}
+ EC_INFO("Scanning bus.\n");
+
// begin scanning of slaves
fsm->slave = list_entry(master->slaves.next, ec_slave_t, list);
fsm->slave_state = ec_fsm_slavescan_start;
@@ -555,26 +308,33 @@
{
ec_master_t *master = fsm->master;
ec_slave_t *slave;
- char old_state[25], new_state[25];
+ char old_state[EC_STATE_STRING_SIZE], new_state[EC_STATE_STRING_SIZE];
// check if any slaves are not in the state, they're supposed to be
list_for_each_entry(slave, &master->slaves, list) {
- if (slave->error_flag ||
- !slave->online ||
- slave->requested_state == EC_SLAVE_STATE_UNKNOWN ||
- slave->current_state == slave->requested_state) continue;
-
- ec_state_string(slave->current_state, old_state);
- ec_state_string(slave->requested_state, new_state);
- EC_INFO("Changing state of slave %i from %s to %s.\n",
- slave->ring_position, old_state, new_state);
-
+ if (slave->error_flag
+ || !slave->online
+ || slave->requested_state == EC_SLAVE_STATE_UNKNOWN
+ || (slave->current_state == slave->requested_state
+ && slave->configured)) continue;
+
+ if (master->debug_level) {
+ ec_state_string(slave->current_state, old_state);
+ if (slave->current_state != slave->requested_state) {
+ ec_state_string(slave->requested_state, new_state);
+ EC_DBG("Changing state of slave %i (%s -> %s).\n",
+ slave->ring_position, old_state, new_state);
+ }
+ else if (!slave->configured) {
+ EC_DBG("Reconfiguring slave %i (%s).\n",
+ slave->ring_position, old_state);
+ }
+ }
+
+ fsm->master_state = ec_fsm_master_configure_slave;
fsm->slave = slave;
- fsm->slave_state = ec_fsm_slaveconf_init;
- fsm->change_new = EC_SLAVE_STATE_INIT;
- fsm->change_state = ec_fsm_change_start;
- fsm->master_state = ec_fsm_master_configure_slave;
- fsm->master_state(fsm); // execute immediately
+ fsm->slave_state = ec_fsm_slaveconf_state_start;
+ fsm->slave_state(fsm); // execute immediately
return;
}
@@ -582,7 +342,55 @@
ec_master_eoe_start(master);
if (master->mode == EC_MASTER_MODE_IDLE) {
- // nothing to configure. check for pending EEPROM write operations.
+
+ // Check for a pending SDO request
+ if (master->sdo_seq_master != master->sdo_seq_user) {
+ if (master->debug_level)
+ EC_DBG("Processing SDO request...\n");
+ slave = master->sdo_request->sdo->slave;
+ if (slave->current_state == EC_SLAVE_STATE_INIT
+ || !slave->online) {
+ EC_ERR("Failed to process SDO request, slave %i not ready.\n",
+ slave->ring_position);
+ master->sdo_request->return_code = -1;
+ master->sdo_seq_master++;
+ }
+ else {
+ // start uploading SDO
+ fsm->slave = slave;
+ fsm->master_state = ec_fsm_master_sdo_request;
+ fsm->sdo_request = master->sdo_request;
+ ec_fsm_coe_upload(&fsm->fsm_coe, slave, fsm->sdo_request);
+ ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
+ return;
+ }
+ }
+
+ // check, if slaves have an SDO dictionary to read out.
+ list_for_each_entry(slave, &master->slaves, list) {
+ if (!(slave->sii_mailbox_protocols & EC_MBOX_COE)
+ || slave->sdo_dictionary_fetched
+ || slave->current_state == EC_SLAVE_STATE_INIT
+ || jiffies - slave->jiffies_preop < EC_WAIT_SDO_DICT * HZ
+ || !slave->online
+ || slave->error_flag) continue;
+
+ if (master->debug_level) {
+ EC_DBG("Fetching SDO dictionary from slave %i.\n",
+ slave->ring_position);
+ }
+
+ slave->sdo_dictionary_fetched = 1;
+
+ // start fetching SDO dictionary
+ fsm->slave = slave;
+ fsm->master_state = ec_fsm_master_sdodict;
+ ec_fsm_coe_dictionary(&fsm->fsm_coe, slave);
+ ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
+ return;
+ }
+
+ // check for pending EEPROM write operations.
list_for_each_entry(slave, &master->slaves, list) {
if (!slave->new_eeprom_data) continue;
@@ -596,20 +404,17 @@
// found pending EEPROM write operation. execute it!
EC_INFO("Writing EEPROM of slave %i...\n", slave->ring_position);
+ fsm->slave = slave;
fsm->sii_offset = 0x0000;
- memcpy(fsm->sii_value, slave->new_eeprom_data, 2);
- fsm->sii_mode = 1;
- fsm->sii_state = ec_fsm_sii_start_writing;
- fsm->slave = slave;
+ ec_fsm_sii_write(&fsm->fsm_sii, slave, fsm->sii_offset,
+ slave->new_eeprom_data, EC_FSM_SII_NODE);
fsm->master_state = ec_fsm_master_write_eeprom;
fsm->master_state(fsm); // execute immediately
return;
}
}
- // nothing to do. restart master state machine.
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
+ fsm->master_state = ec_fsm_master_end;
}
/*****************************************************************************/
@@ -647,10 +452,8 @@
EC_INFO("Validating bus.\n");
fsm->slave = list_entry(master->slaves.next, ec_slave_t, list);
fsm->master_state = ec_fsm_master_validate_vendor;
- fsm->sii_offset = 0x0008; // vendor ID
- fsm->sii_mode = 0;
- fsm->sii_state = ec_fsm_sii_start_reading;
- fsm->sii_state(fsm); // execute immediately
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, 0x0008, EC_FSM_SII_POSITION);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately
return;
}
}
@@ -661,7 +464,7 @@
/*****************************************************************************/
/**
- Master state: STATES.
+ Master state: READ STATES.
Fetches the AL- and online state of a slave.
*/
@@ -672,8 +475,9 @@
uint8_t new_state;
if (datagram->state != EC_DATAGRAM_RECEIVED) {
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
+ EC_ERR("Failed to receive AL state datagram for slave %i!\n",
+ slave->ring_position);
+ fsm->master_state = ec_fsm_master_error;
return;
}
@@ -681,7 +485,8 @@
if (datagram->working_counter != 1) {
if (slave->online) {
slave->online = 0;
- EC_INFO("Slave %i: offline.\n", slave->ring_position);
+ if (slave->master->debug_level)
+ EC_DBG("Slave %i: offline.\n", slave->ring_position);
}
ec_fsm_master_action_next_slave_state(fsm);
return;
@@ -690,22 +495,59 @@
// slave responded
new_state = EC_READ_U8(datagram->data);
if (!slave->online) { // slave was offline before
- char cur_state[25];
slave->online = 1;
slave->error_flag = 0; // clear error flag
slave->current_state = new_state;
- ec_state_string(slave->current_state, cur_state);
- EC_INFO("Slave %i: online (%s).\n", slave->ring_position, cur_state);
+ if (slave->master->debug_level) {
+ char cur_state[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, cur_state);
+ EC_DBG("Slave %i: online (%s).\n",
+ slave->ring_position, cur_state);
+ }
}
else if (new_state != slave->current_state) {
- char old_state[25], cur_state[25];
- ec_state_string(slave->current_state, old_state);
- ec_state_string(new_state, cur_state);
- EC_INFO("Slave %i: %s -> %s.\n",
- slave->ring_position, old_state, cur_state);
+ if (slave->master->debug_level) {
+ char old_state[EC_STATE_STRING_SIZE],
+ cur_state[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, old_state);
+ ec_state_string(new_state, cur_state);
+ EC_DBG("Slave %i: %s -> %s.\n",
+ slave->ring_position, old_state, cur_state);
+ }
slave->current_state = new_state;
}
+ // check, if new slave state has to be acknowledged
+ if (slave->current_state & EC_SLAVE_STATE_ACK_ERR && !slave->error_flag) {
+ ec_fsm_change_ack(&fsm->fsm_change, slave);
+ ec_fsm_change_exec(&fsm->fsm_change);
+ fsm->master_state = ec_fsm_master_acknowledge;
+ return;
+ }
+
+ ec_fsm_master_action_next_slave_state(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+ Master state: ACKNOWLEDGE
+*/
+
+void ec_fsm_master_acknowledge(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (ec_fsm_change_exec(&fsm->fsm_change)) return;
+
+ if (!ec_fsm_change_success(&fsm->fsm_change)) {
+ fsm->slave->error_flag = 1;
+ EC_ERR("Failed to acknowledge state change on slave %i.\n",
+ slave->ring_position);
+ fsm->master_state = ec_fsm_master_error;
+ return;
+ }
+
ec_fsm_master_action_next_slave_state(fsm);
}
@@ -720,32 +562,26 @@
{
ec_slave_t *slave = fsm->slave;
- fsm->sii_state(fsm); // execute SII state machine
-
- if (fsm->sii_state == ec_fsm_error) {
+ if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
+
+ if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
fsm->slave->error_flag = 1;
EC_ERR("Failed to validate vendor ID of slave %i.\n",
slave->ring_position);
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
- return;
- }
-
- if (fsm->sii_state != ec_fsm_end) return;
-
- if (EC_READ_U32(fsm->sii_value) != slave->sii_vendor_id) {
- EC_ERR("Slave %i: invalid vendor ID!\n", slave->ring_position);
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
+ fsm->master_state = ec_fsm_master_error;
+ return;
+ }
+
+ if (EC_READ_U32(fsm->fsm_sii.value) != slave->sii_vendor_id) {
+ EC_ERR("Slave %i has an invalid vendor ID!\n", slave->ring_position);
+ fsm->master_state = ec_fsm_master_error;
return;
}
// vendor ID is ok. check product code.
fsm->master_state = ec_fsm_master_validate_product;
- fsm->sii_offset = 0x000A; // product code
- fsm->sii_mode = 0;
- fsm->sii_state = ec_fsm_sii_start_reading;
- fsm->sii_state(fsm); // execute immediately
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, 0x000A, EC_FSM_SII_POSITION);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately
}
/*****************************************************************************/
@@ -770,7 +606,8 @@
fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list);
}
- EC_INFO("Reinitializing slave %i.\n", fsm->slave->ring_position);
+ if (fsm->master->debug_level)
+ EC_DBG("Reinitializing slave %i.\n", fsm->slave->ring_position);
// write station address
ec_datagram_apwr(datagram, fsm->slave->ring_position, 0x0010, 2);
@@ -790,25 +627,21 @@
{
ec_slave_t *slave = fsm->slave;
- fsm->sii_state(fsm); // execute SII state machine
-
- if (fsm->sii_state == ec_fsm_error) {
+ if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
+
+ if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
fsm->slave->error_flag = 1;
EC_ERR("Failed to validate product code of slave %i.\n",
slave->ring_position);
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
- return;
- }
-
- if (fsm->sii_state != ec_fsm_end) return;
-
- if (EC_READ_U32(fsm->sii_value) != slave->sii_product_code) {
+ fsm->master_state = ec_fsm_master_error;
+ return;
+ }
+
+ if (EC_READ_U32(fsm->fsm_sii.value) != slave->sii_product_code) {
EC_ERR("Slave %i: invalid product code!\n", slave->ring_position);
EC_ERR("expected 0x%08X, got 0x%08X.\n", slave->sii_product_code,
- EC_READ_U32(fsm->sii_value));
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
+ EC_READ_U32(fsm->fsm_sii.value));
+ fsm->master_state = ec_fsm_master_error;
return;
}
@@ -823,16 +656,14 @@
// validate next slave
fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list);
fsm->master_state = ec_fsm_master_validate_vendor;
- fsm->sii_offset = 0x0008; // vendor ID
- fsm->sii_mode = 0;
- fsm->sii_state = ec_fsm_sii_start_reading;
- fsm->sii_state(fsm); // execute immediately
-}
-
-/*****************************************************************************/
-
-/**
- Master state: ADDRESS.
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, 0x0008, EC_FSM_SII_POSITION);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately
+}
+
+/*****************************************************************************/
+
+/**
+ Master state: REWRITE ADDRESS.
Checks, if the new station address has been written to the slave.
*/
@@ -864,7 +695,7 @@
/*****************************************************************************/
/**
- Master state: SCAN.
+ Master state: SCAN SLAVES.
Executes the sub-statemachine for the scanning of a slave.
*/
@@ -873,11 +704,10 @@
ec_master_t *master = fsm->master;
ec_slave_t *slave = fsm->slave;
-
fsm->slave_state(fsm); // execute slave state machine
- if (fsm->slave_state != ec_fsm_end
- && fsm->slave_state != ec_fsm_error) return;
+ if (fsm->slave_state != ec_fsm_slave_state_end
+ && fsm->slave_state != ec_fsm_slave_state_error) return;
// another slave to fetch?
if (slave->list.next != &master->slaves) {
@@ -891,27 +721,19 @@
ec_master_calc_addressing(master);
- // determine initial states.
+ // set initial states of all slaves to PREOP to make mailbox
+ // communication possible
list_for_each_entry(slave, &master->slaves, list) {
- if (ec_slave_is_coupler(slave)) {
- slave->requested_state = EC_SLAVE_STATE_OP;
- }
- else {
- if (master->mode == EC_MASTER_MODE_OPERATION)
- slave->requested_state = EC_SLAVE_STATE_PREOP;
- else
- slave->requested_state = EC_SLAVE_STATE_INIT;
- }
- }
-
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
-}
-
-/*****************************************************************************/
-
-/**
- Master state: CONF.
+ ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
+ }
+
+ fsm->master_state = ec_fsm_master_end;
+}
+
+/*****************************************************************************/
+
+/**
+ Master state: CONFIGURE SLAVES.
Starts configuring a slave.
*/
@@ -921,8 +743,8 @@
{
fsm->slave_state(fsm); // execute slave's state machine
- if (fsm->slave_state != ec_fsm_end
- && fsm->slave_state != ec_fsm_error) return;
+ if (fsm->slave_state != ec_fsm_slave_state_end
+ && fsm->slave_state != ec_fsm_slave_state_error) return;
ec_fsm_master_action_process_states(fsm);
}
@@ -930,33 +752,31 @@
/*****************************************************************************/
/**
- Master state: EEPROM.
+ Master state: WRITE EEPROM.
*/
void ec_fsm_master_write_eeprom(ec_fsm_t *fsm /**< finite state machine */)
{
ec_slave_t *slave = fsm->slave;
- fsm->sii_state(fsm); // execute SII state machine
-
- if (fsm->sii_state == ec_fsm_error) {
+ if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
+
+ if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
fsm->slave->error_flag = 1;
EC_ERR("Failed to write EEPROM contents to slave %i.\n",
slave->ring_position);
kfree(slave->new_eeprom_data);
slave->new_eeprom_data = NULL;
- fsm->master_state = ec_fsm_master_start;
- fsm->master_state(fsm); // execute immediately
- return;
- }
-
- if (fsm->sii_state != ec_fsm_end) return;
+ fsm->master_state = ec_fsm_master_error;
+ return;
+ }
fsm->sii_offset++;
if (fsm->sii_offset < slave->new_eeprom_size) {
- memcpy(fsm->sii_value, slave->new_eeprom_data + fsm->sii_offset, 2);
- fsm->sii_state = ec_fsm_sii_start_writing;
- fsm->sii_state(fsm); // execute immediately
+ ec_fsm_sii_write(&fsm->fsm_sii, slave, fsm->sii_offset,
+ slave->new_eeprom_data + fsm->sii_offset,
+ EC_FSM_SII_NODE);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately
return;
}
@@ -970,7 +790,90 @@
// restart master state machine.
fsm->master_state = ec_fsm_master_start;
fsm->master_state(fsm); // execute immediately
- return;
+}
+
+/*****************************************************************************/
+
+/**
+ Master state: SDODICT.
+*/
+
+void ec_fsm_master_sdodict(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = fsm->master;
+
+ if (ec_fsm_coe_exec(&fsm->fsm_coe)) return;
+
+ if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
+ fsm->master_state = ec_fsm_master_error;
+ return;
+ }
+
+ // SDO dictionary fetching finished
+
+ if (master->debug_level) {
+ unsigned int sdo_count, entry_count;
+ ec_slave_sdo_dict_info(slave, &sdo_count, &entry_count);
+ EC_DBG("Fetched %i SDOs and %i entries from slave %i.\n",
+ sdo_count, entry_count, slave->ring_position);
+ }
+
+ // restart master state machine.
+ fsm->master_state = ec_fsm_master_start;
+ fsm->master_state(fsm); // execute immediately
+}
+
+/*****************************************************************************/
+
+/**
+ Master state: SDO REQUEST.
+*/
+
+void ec_fsm_master_sdo_request(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_master_t *master = fsm->master;
+ ec_sdo_request_t *request = fsm->sdo_request;
+
+ if (ec_fsm_coe_exec(&fsm->fsm_coe)) return;
+
+ if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
+ request->return_code = -1;
+ master->sdo_seq_master++;
+ fsm->master_state = ec_fsm_master_error;
+ return;
+ }
+
+ // SDO dictionary fetching finished
+
+ request->return_code = 1;
+ master->sdo_seq_master++;
+
+ // restart master state machine.
+ fsm->master_state = ec_fsm_master_start;
+ fsm->master_state(fsm); // execute immediately
+}
+
+/*****************************************************************************/
+
+/**
+ State: ERROR.
+*/
+
+void ec_fsm_master_error(ec_fsm_t *fsm /**< finite state machine */)
+{
+ fsm->master_state = ec_fsm_master_start;
+}
+
+/*****************************************************************************/
+
+/**
+ State: END.
+*/
+
+void ec_fsm_master_end(ec_fsm_t *fsm /**< finite state machine */)
+{
+ fsm->master_state = ec_fsm_master_start;
}
/******************************************************************************
@@ -978,7 +881,7 @@
*****************************************************************************/
/**
- Slave state: START_READING.
+ Slave scan state: START.
First state of the slave state machine. Writes the station address to the
slave, according to its ring position.
*/
@@ -997,7 +900,7 @@
/*****************************************************************************/
/**
- Slave state: ADDRESS.
+ Slave scan state: ADDRESS.
*/
void ec_fsm_slavescan_address(ec_fsm_t *fsm /**< finite state machine */)
@@ -1007,7 +910,7 @@
if (datagram->state != EC_DATAGRAM_RECEIVED
|| datagram->working_counter != 1) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
EC_ERR("Failed to write station address of slave %i.\n",
fsm->slave->ring_position);
return;
@@ -1022,7 +925,7 @@
/*****************************************************************************/
/**
- Slave state: STATE.
+ Slave scan state: STATE.
*/
void ec_fsm_slavescan_state(ec_fsm_t *fsm /**< finite state machine */)
@@ -1033,17 +936,18 @@
if (datagram->state != EC_DATAGRAM_RECEIVED
|| datagram->working_counter != 1) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
EC_ERR("Failed to read AL state of slave %i.\n",
fsm->slave->ring_position);
return;
}
slave->current_state = EC_READ_U8(datagram->data);
- if (slave->current_state & EC_ACK) {
- EC_WARN("Slave %i has state error bit set (0x%02X)!\n",
- slave->ring_position, slave->current_state);
- slave->current_state &= 0x0F;
+ if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, state_str);
+ EC_WARN("Slave %i has state error bit set (%s)!\n",
+ slave->ring_position, state_str);
}
// read base data
@@ -1055,7 +959,7 @@
/*****************************************************************************/
/**
- Slave state: BASE.
+ Slave scan state: BASE.
*/
void ec_fsm_slavescan_base(ec_fsm_t *fsm /**< finite state machine */)
@@ -1066,7 +970,7 @@
if (datagram->state != EC_DATAGRAM_RECEIVED
|| datagram->working_counter != 1) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
EC_ERR("Failed to read base data of slave %i.\n",
slave->ring_position);
return;
@@ -1090,7 +994,7 @@
/*****************************************************************************/
/**
- Slave state: DATALINK.
+ Slave scan state: DATALINK.
*/
void ec_fsm_slavescan_datalink(ec_fsm_t *fsm /**< finite state machine */)
@@ -1103,7 +1007,7 @@
if (datagram->state != EC_DATAGRAM_RECEIVED
|| datagram->working_counter != 1) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
EC_ERR("Failed to read DL status of slave %i.\n",
slave->ring_position);
return;
@@ -1119,8 +1023,7 @@
// Start fetching EEPROM size
fsm->sii_offset = 0x0040; // first category header
- fsm->sii_mode = 1;
- fsm->sii_state = ec_fsm_sii_start_reading;
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, fsm->sii_offset, EC_FSM_SII_NODE);
fsm->slave_state = ec_fsm_slavescan_eeprom_size;
fsm->slave_state(fsm); // execute state immediately
}
@@ -1128,7 +1031,7 @@
/*****************************************************************************/
/**
- Slave state: EEPROM_SIZE.
+ Slave scan state: EEPROM SIZE.
*/
void ec_fsm_slavescan_eeprom_size(ec_fsm_t *fsm /**< finite state machine */)
@@ -1136,26 +1039,24 @@
ec_slave_t *slave = fsm->slave;
uint16_t cat_type, cat_size;
- // execute SII state machine
- fsm->sii_state(fsm);
-
- if (fsm->sii_state == ec_fsm_error) {
+ if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
+
+ if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
EC_ERR("Failed to read EEPROM size of slave %i.\n",
slave->ring_position);
return;
}
- if (fsm->sii_state != ec_fsm_end) return;
-
- cat_type = EC_READ_U16(fsm->sii_value);
- cat_size = EC_READ_U16(fsm->sii_value + 2);
+ cat_type = EC_READ_U16(fsm->fsm_sii.value);
+ cat_size = EC_READ_U16(fsm->fsm_sii.value + 2);
if (cat_type != 0xFFFF) { // not the last category
fsm->sii_offset += cat_size + 2;
- fsm->sii_state = ec_fsm_sii_start_reading;
- fsm->sii_state(fsm); // execute state immediately
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, fsm->sii_offset,
+ EC_FSM_SII_NODE);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute state immediately
return;
}
@@ -1170,7 +1071,7 @@
if (!(slave->eeprom_data =
(uint8_t *) kmalloc(slave->eeprom_size, GFP_ATOMIC))) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
EC_ERR("Failed to allocate EEPROM data on slave %i.\n",
slave->ring_position);
return;
@@ -1178,17 +1079,16 @@
// Start fetching EEPROM contents
+ fsm->slave_state = ec_fsm_slavescan_eeprom_data;
fsm->sii_offset = 0x0000;
- fsm->sii_mode = 1;
- fsm->sii_state = ec_fsm_sii_start_reading;
- fsm->slave_state = ec_fsm_slavescan_eeprom_data;
- fsm->slave_state(fsm); // execute state immediately
-}
-
-/*****************************************************************************/
-
-/**
- Slave state: EEPROM_DATA.
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, fsm->sii_offset, EC_FSM_SII_NODE);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute state immediately
+}
+
+/*****************************************************************************/
+
+/**
+ Slave scan state: EEPROM DATA.
*/
void ec_fsm_slavescan_eeprom_data(ec_fsm_t *fsm /**< finite state machine */)
@@ -1196,33 +1096,33 @@
ec_slave_t *slave = fsm->slave;
uint16_t *cat_word, cat_type, cat_size;
- // execute SII state machine
- fsm->sii_state(fsm);
-
- if (fsm->sii_state == ec_fsm_error) {
+ if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
+
+ if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
EC_ERR("Failed to fetch EEPROM contents of slave %i.\n",
slave->ring_position);
return;
}
- if (fsm->sii_state != ec_fsm_end) return;
-
// 2 words fetched
if (fsm->sii_offset + 2 <= slave->eeprom_size / 2) { // 2 words fit
- memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_value, 4);
+ memcpy(slave->eeprom_data + fsm->sii_offset * 2,
+ fsm->fsm_sii.value, 4);
}
else { // copy the last word
- memcpy(slave->eeprom_data + fsm->sii_offset * 2, fsm->sii_value, 2);
+ memcpy(slave->eeprom_data + fsm->sii_offset * 2,
+ fsm->fsm_sii.value, 2);
}
if (fsm->sii_offset + 2 < slave->eeprom_size / 2) {
// fetch the next 2 words
fsm->sii_offset += 2;
- fsm->sii_state = ec_fsm_sii_start_reading;
- fsm->sii_state(fsm); // execute state immediately
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, fsm->sii_offset,
+ EC_FSM_SII_NODE);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute state immediately
return;
}
@@ -1281,20 +1181,21 @@
goto end;
break;
default:
- EC_WARN("Unknown category type 0x%04X in slave %i.\n",
- cat_type, slave->ring_position);
+ if (fsm->master->debug_level)
+ EC_WARN("Unknown category type 0x%04X in slave %i.\n",
+ cat_type, slave->ring_position);
}
cat_word += cat_size + 2;
}
- fsm->slave_state = ec_fsm_end;
+ fsm->slave_state = ec_fsm_slave_state_end;
return;
end:
EC_ERR("Failed to analyze category data.\n");
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
}
/******************************************************************************
@@ -1302,41 +1203,121 @@
*****************************************************************************/
/**
- Slave state: INIT.
-*/
-
-void ec_fsm_slaveconf_init(ec_fsm_t *fsm /**< finite state machine */)
-{
+ Slave configuration state: START.
+*/
+
+void ec_fsm_slaveconf_state_start(ec_fsm_t *fsm /**< finite state machine */)
+{
+ if (fsm->master->debug_level) {
+ EC_DBG("Configuring slave %i...\n", fsm->slave->ring_position);
+ }
+
+ ec_fsm_change_start(&fsm->fsm_change, fsm->slave, EC_SLAVE_STATE_INIT);
+ ec_fsm_change_exec(&fsm->fsm_change);
+ fsm->slave_state = ec_fsm_slaveconf_state_init;
+}
+
+/*****************************************************************************/
+
+/**
+ Slave configuration state: INIT.
+*/
+
+void ec_fsm_slaveconf_state_init(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_master_t *master = fsm->master;
ec_slave_t *slave = fsm->slave;
ec_datagram_t *datagram = &fsm->datagram;
- const ec_sii_sync_t *sync;
-
- fsm->change_state(fsm); // execute state change state machine
-
- if (fsm->change_state == ec_fsm_error) {
+
+ if (ec_fsm_change_exec(&fsm->fsm_change)) return;
+
+ if (!ec_fsm_change_success(&fsm->fsm_change)) {
slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
- return;
- }
-
- if (fsm->change_state != ec_fsm_end) return;
-
- // slave is now in INIT
- if (slave->current_state == slave->requested_state) {
- fsm->slave_state = ec_fsm_end; // successful
- return;
+ fsm->slave_state = ec_fsm_slave_state_error;
+ return;
+ }
+
+ slave->configured = 1;
+
+ if (master->debug_level) {
+ EC_DBG("Slave %i is now in INIT.\n", slave->ring_position);
}
// check and reset CRC fault counters
//ec_slave_check_crc(slave);
// TODO: Implement state machine for CRC checking.
+ if (!slave->base_fmmu_count) { // skip FMMU configuration
+ ec_fsm_slaveconf_enter_sync(fsm);
+ return;
+ }
+
+ if (master->debug_level)
+ EC_DBG("Clearing FMMU configurations of slave %i...\n",
+ slave->ring_position);
+
+ // clear FMMU configurations
+ ec_datagram_npwr(datagram, slave->station_address,
+ 0x0600, EC_FMMU_SIZE * slave->base_fmmu_count);
+ memset(datagram->data, 0x00, EC_FMMU_SIZE * slave->base_fmmu_count);
+ ec_master_queue_datagram(master, datagram);
+ fsm->slave_state = ec_fsm_slaveconf_state_clear_fmmus;
+}
+
+/*****************************************************************************/
+
+/**
+ Slave configuration state: CLEAR FMMU.
+*/
+
+void ec_fsm_slaveconf_state_clear_fmmus(ec_fsm_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = &fsm->datagram;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->slave->error_flag = 1;
+ fsm->slave_state = ec_fsm_slave_state_error;
+ EC_ERR("Failed to clear FMMUs on slave %i.\n",
+ fsm->slave->ring_position);
+ return;
+ }
+
+ ec_fsm_slaveconf_enter_sync(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+*/
+
+void ec_fsm_slaveconf_enter_sync(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_master_t *master = fsm->master;
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = &fsm->datagram;
+ const ec_sii_sync_t *sync;
+ ec_sii_sync_t mbox_sync;
+
+ // slave is now in INIT
+ if (slave->current_state == slave->requested_state) {
+ fsm->slave_state = ec_fsm_slave_state_end; // successful
+ if (master->debug_level) {
+ EC_DBG("Finished configuration of slave %i.\n",
+ slave->ring_position);
+ }
+ return;
+ }
+
if (!slave->base_sync_count) { // no sync managers
- fsm->slave_state = ec_fsm_slaveconf_preop;
- fsm->change_new = EC_SLAVE_STATE_PREOP;
- fsm->change_state = ec_fsm_change_start;
- fsm->change_state(fsm); // execute immediately
- return;
+ ec_fsm_slaveconf_enter_preop(fsm);
+ return;
+ }
+
+ if (master->debug_level) {
+ EC_DBG("Configuring sync managers of slave %i.\n",
+ slave->ring_position);
}
// configure sync managers
@@ -1344,28 +1325,49 @@
EC_SYNC_SIZE * slave->base_sync_count);
memset(datagram->data, 0x00, EC_SYNC_SIZE * slave->base_sync_count);
- list_for_each_entry(sync, &slave->sii_syncs, list) {
- if (sync->index >= slave->base_sync_count) {
- EC_ERR("Invalid sync manager configuration found!");
- fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
- return;
- }
- ec_sync_config(sync, slave,
- datagram->data + EC_SYNC_SIZE * sync->index);
+ if (list_empty(&slave->sii_syncs)) {
+ if (slave->sii_rx_mailbox_offset && slave->sii_tx_mailbox_offset) {
+ if (slave->master->debug_level)
+ EC_DBG("Guessing sync manager settings for slave %i.\n",
+ slave->ring_position);
+ mbox_sync.index = 0;
+ mbox_sync.physical_start_address = slave->sii_tx_mailbox_offset;
+ mbox_sync.length = slave->sii_tx_mailbox_size;
+ mbox_sync.control_register = 0x26;
+ mbox_sync.enable = 0x01;
+ mbox_sync.est_length = 0;
+ ec_sync_config(&mbox_sync, slave,
+ datagram->data + EC_SYNC_SIZE * mbox_sync.index);
+ mbox_sync.index = 1;
+ mbox_sync.physical_start_address = slave->sii_rx_mailbox_offset;
+ mbox_sync.length = slave->sii_rx_mailbox_size;
+ mbox_sync.control_register = 0x22;
+ mbox_sync.enable = 0x01;
+ mbox_sync.est_length = 0;
+ ec_sync_config(&mbox_sync, slave,
+ datagram->data + EC_SYNC_SIZE * mbox_sync.index);
+ }
+ }
+ else if (slave->sii_mailbox_protocols) { // mailboxes present
+ list_for_each_entry(sync, &slave->sii_syncs, list) {
+ // only configure mailbox sync-managers
+ if (sync->index != 0 && sync->index != 1) continue;
+ ec_sync_config(sync, slave,
+ datagram->data + EC_SYNC_SIZE * sync->index);
+ }
}
ec_master_queue_datagram(fsm->master, datagram);
- fsm->slave_state = ec_fsm_slaveconf_sync;
-}
-
-/*****************************************************************************/
-
-/**
- Slave state: SYNC.
-*/
-
-void ec_fsm_slaveconf_sync(ec_fsm_t *fsm /**< finite state machine */)
+ fsm->slave_state = ec_fsm_slaveconf_state_sync;
+}
+
+/*****************************************************************************/
+
+/**
+ Slave configuration state: SYNC.
+*/
+
+void ec_fsm_slaveconf_state_sync(ec_fsm_t *fsm /**< finite state machine */)
{
ec_datagram_t *datagram = &fsm->datagram;
ec_slave_t *slave = fsm->slave;
@@ -1373,59 +1375,132 @@
if (datagram->state != EC_DATAGRAM_RECEIVED
|| datagram->working_counter != 1) {
slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
EC_ERR("Failed to set sync managers on slave %i.\n",
slave->ring_position);
return;
}
- fsm->slave_state = ec_fsm_slaveconf_preop;
- fsm->change_new = EC_SLAVE_STATE_PREOP;
- fsm->change_state = ec_fsm_change_start;
- fsm->change_state(fsm); // execute immediately
-}
-
-/*****************************************************************************/
-
-/**
- Slave state: PREOP.
-*/
-
-void ec_fsm_slaveconf_preop(ec_fsm_t *fsm /**< finite state machine */)
+ ec_fsm_slaveconf_enter_preop(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+ */
+
+void ec_fsm_slaveconf_enter_preop(ec_fsm_t *fsm /**< finite state machine */)
+{
+ fsm->slave_state = ec_fsm_slaveconf_state_preop;
+ ec_fsm_change_start(&fsm->fsm_change, fsm->slave, EC_SLAVE_STATE_PREOP);
+ ec_fsm_change_exec(&fsm->fsm_change); // execute immediately
+}
+
+/*****************************************************************************/
+
+/**
+ Slave configuration state: PREOP.
+*/
+
+void ec_fsm_slaveconf_state_preop(ec_fsm_t *fsm /**< finite state machine */)
{
ec_slave_t *slave = fsm->slave;
ec_master_t *master = fsm->master;
+
+ if (ec_fsm_change_exec(&fsm->fsm_change)) return;
+
+ if (!ec_fsm_change_success(&fsm->fsm_change)) {
+ slave->error_flag = 1;
+ fsm->slave_state = ec_fsm_slave_state_error;
+ return;
+ }
+
+ // slave is now in PREOP
+ slave->jiffies_preop = fsm->datagram.jiffies_received;
+
+ if (master->debug_level) {
+ EC_DBG("Slave %i is now in PREOP.\n", slave->ring_position);
+ }
+
+ if (slave->current_state == slave->requested_state) {
+ fsm->slave_state = ec_fsm_slave_state_end; // successful
+ if (master->debug_level) {
+ EC_DBG("Finished configuration of slave %i.\n",
+ slave->ring_position);
+ }
+ return;
+ }
+
+ ec_fsm_slaveconf_enter_sync2(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+*/
+
+void ec_fsm_slaveconf_enter_sync2(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = &fsm->datagram;
+ ec_sii_sync_t *sync;
+
+ if (list_empty(&slave->sii_syncs)) {
+ ec_fsm_slaveconf_enter_fmmu(fsm);
+ return;
+ }
+
+ // configure sync managers for process data
+ ec_datagram_npwr(datagram, slave->station_address, 0x0800,
+ EC_SYNC_SIZE * slave->base_sync_count);
+ memset(datagram->data, 0x00, EC_SYNC_SIZE * slave->base_sync_count);
+
+ list_for_each_entry(sync, &slave->sii_syncs, list) {
+ ec_sync_config(sync, slave,
+ datagram->data + EC_SYNC_SIZE * sync->index);
+ }
+
+ ec_master_queue_datagram(fsm->master, datagram);
+ fsm->slave_state = ec_fsm_slaveconf_state_sync2;
+}
+
+/*****************************************************************************/
+
+/**
+ Slave configuration state: SYNC2.
+*/
+
+void ec_fsm_slaveconf_state_sync2(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = &fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ slave->error_flag = 1;
+ fsm->slave_state = ec_fsm_slave_state_error;
+ EC_ERR("Failed to set process data sync managers on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ ec_fsm_slaveconf_enter_fmmu(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+*/
+
+void ec_fsm_slaveconf_enter_fmmu(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = slave->master;
ec_datagram_t *datagram = &fsm->datagram;
unsigned int j;
- fsm->change_state(fsm); // execute state change state machine
-
- if (fsm->change_state == ec_fsm_error) {
- slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
- return;
- }
-
- if (fsm->change_state != ec_fsm_end) return;
-
- // slave is now in PREOP
- if (slave->current_state == slave->requested_state) {
- fsm->slave_state = ec_fsm_end; // successful
- return;
- }
-
if (!slave->base_fmmu_count) { // skip FMMU configuration
- if (list_empty(&slave->sdo_confs)) { // skip SDO configuration
- fsm->slave_state = ec_fsm_slaveconf_saveop;
- fsm->change_new = EC_SLAVE_STATE_SAVEOP;
- fsm->change_state = ec_fsm_change_start;
- fsm->change_state(fsm); // execute immediately
- return;
- }
- fsm->slave_state = ec_fsm_slaveconf_sdoconf;
- fsm->sdodata = list_entry(slave->sdo_confs.next, ec_sdo_data_t, list);
- fsm->coe_state = ec_fsm_coe_down_start;
- fsm->coe_state(fsm); // execute immediately
+ ec_fsm_slaveconf_enter_sdoconf(fsm);
return;
}
@@ -1439,16 +1514,16 @@
}
ec_master_queue_datagram(master, datagram);
- fsm->slave_state = ec_fsm_slaveconf_fmmu;
-}
-
-/*****************************************************************************/
-
-/**
- Slave state: FMMU.
-*/
-
-void ec_fsm_slaveconf_fmmu(ec_fsm_t *fsm /**< finite state machine */)
+ fsm->slave_state = ec_fsm_slaveconf_state_fmmu;
+}
+
+/*****************************************************************************/
+
+/**
+ Slave configuration state: FMMU.
+*/
+
+void ec_fsm_slaveconf_state_fmmu(ec_fsm_t *fsm /**< finite state machine */)
{
ec_datagram_t *datagram = &fsm->datagram;
ec_slave_t *slave = fsm->slave;
@@ -1456,7 +1531,7 @@
if (datagram->state != EC_DATAGRAM_RECEIVED
|| datagram->working_counter != 1) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
+ fsm->slave_state = ec_fsm_slave_state_error;
EC_ERR("Failed to set FMMUs on slave %i.\n",
fsm->slave->ring_position);
return;
@@ -1464,810 +1539,144 @@
// No CoE configuration to be applied? Jump to SAVEOP state.
if (list_empty(&slave->sdo_confs)) { // skip SDO configuration
- // set state to SAVEOP
- fsm->slave_state = ec_fsm_slaveconf_saveop;
- fsm->change_new = EC_SLAVE_STATE_SAVEOP;
- fsm->change_state = ec_fsm_change_start;
- fsm->change_state(fsm); // execute immediately
- return;
- }
-
- fsm->slave_state = ec_fsm_slaveconf_sdoconf;
- fsm->sdodata = list_entry(slave->sdo_confs.next, ec_sdo_data_t, list);
- fsm->coe_state = ec_fsm_coe_down_start;
- fsm->coe_state(fsm); // execute immediately
-}
-
-/*****************************************************************************/
-
-/**
- Slave state: SDOCONF.
-*/
-
-void ec_fsm_slaveconf_sdoconf(ec_fsm_t *fsm /**< finite state machine */)
-{
- fsm->coe_state(fsm); // execute CoE state machine
-
- if (fsm->coe_state == ec_fsm_error) {
+ ec_fsm_slaveconf_enter_saveop(fsm);
+ return;
+ }
+
+ ec_fsm_slaveconf_enter_sdoconf(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+ */
+
+void ec_fsm_slaveconf_enter_sdoconf(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (list_empty(&slave->sdo_confs)) { // skip SDO configuration
+ ec_fsm_slaveconf_enter_saveop(fsm);
+ return;
+ }
+
+ // start SDO configuration
+ fsm->slave_state = ec_fsm_slaveconf_state_sdoconf;
+ fsm->sdodata = list_entry(fsm->slave->sdo_confs.next, ec_sdo_data_t, list);
+ ec_fsm_coe_download(&fsm->fsm_coe, fsm->slave, fsm->sdodata);
+ ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
+}
+
+/*****************************************************************************/
+
+/**
+ Slave configuration state: SDOCONF.
+*/
+
+void ec_fsm_slaveconf_state_sdoconf(ec_fsm_t *fsm /**< finite state machine */)
+{
+ if (ec_fsm_coe_exec(&fsm->fsm_coe)) return;
+
+ if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
- return;
- }
-
- if (fsm->coe_state != ec_fsm_end) return;
+ fsm->slave_state = ec_fsm_slave_state_error;
+ return;
+ }
// Another SDO to configure?
if (fsm->sdodata->list.next != &fsm->slave->sdo_confs) {
fsm->sdodata = list_entry(fsm->sdodata->list.next,
ec_sdo_data_t, list);
- fsm->coe_state = ec_fsm_coe_down_start;
- fsm->coe_state(fsm); // execute immediately
+ ec_fsm_coe_download(&fsm->fsm_coe, fsm->slave, fsm->sdodata);
+ ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
return;
}
// All SDOs are now configured.
// set state to SAVEOP
- fsm->slave_state = ec_fsm_slaveconf_saveop;
- fsm->change_new = EC_SLAVE_STATE_SAVEOP;
- fsm->change_state = ec_fsm_change_start;
- fsm->change_state(fsm); // execute immediately
-}
-
-/*****************************************************************************/
-
-/**
- Slave state: SAVEOP.
-*/
-
-void ec_fsm_slaveconf_saveop(ec_fsm_t *fsm /**< finite state machine */)
-{
- fsm->change_state(fsm); // execute state change state machine
-
- if (fsm->change_state == ec_fsm_error) {
+ ec_fsm_slaveconf_enter_saveop(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+ */
+
+void ec_fsm_slaveconf_enter_saveop(ec_fsm_t *fsm /**< finite state machine */)
+{
+ fsm->slave_state = ec_fsm_slaveconf_state_saveop;
+ ec_fsm_change_start(&fsm->fsm_change, fsm->slave, EC_SLAVE_STATE_SAVEOP);
+ ec_fsm_change_exec(&fsm->fsm_change); // execute immediately
+}
+
+/*****************************************************************************/
+
+/**
+ Slave configuration state: SAVEOP.
+*/
+
+void ec_fsm_slaveconf_state_saveop(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_master_t *master = fsm->master;
+ ec_slave_t *slave = fsm->slave;
+
+ if (ec_fsm_change_exec(&fsm->fsm_change)) return;
+
+ if (!ec_fsm_change_success(&fsm->fsm_change)) {
fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
- return;
- }
-
- if (fsm->change_state != ec_fsm_end) return;
+ fsm->slave_state = ec_fsm_slave_state_error;
+ return;
+ }
// slave is now in SAVEOP
+
+ if (master->debug_level) {
+ EC_DBG("Slave %i is now in SAVEOP.\n", slave->ring_position);
+ }
+
if (fsm->slave->current_state == fsm->slave->requested_state) {
- fsm->slave_state = ec_fsm_end; // successful
+ fsm->slave_state = ec_fsm_slave_state_end; // successful
+ if (master->debug_level) {
+ EC_DBG("Finished configuration of slave %i.\n",
+ slave->ring_position);
+ }
return;
}
// set state to OP
- fsm->slave_state = ec_fsm_slaveconf_op;
- fsm->change_new = EC_SLAVE_STATE_OP;
- fsm->change_state = ec_fsm_change_start;
- fsm->change_state(fsm); // execute immediately
-}
-
-/*****************************************************************************/
-
-/**
- Slave state: OP
-*/
-
-void ec_fsm_slaveconf_op(ec_fsm_t *fsm /**< finite state machine */)
-{
- fsm->change_state(fsm); // execute state change state machine
-
- if (fsm->change_state == ec_fsm_error) {
- fsm->slave->error_flag = 1;
- fsm->slave_state = ec_fsm_error;
- return;
- }
-
- if (fsm->change_state != ec_fsm_end) return;
+ fsm->slave_state = ec_fsm_slaveconf_state_op;
+ ec_fsm_change_start(&fsm->fsm_change, slave, EC_SLAVE_STATE_OP);
+ ec_fsm_change_exec(&fsm->fsm_change); // execute immediately
+}
+
+/*****************************************************************************/
+
+/**
+ Slave configuration state: OP
+*/
+
+void ec_fsm_slaveconf_state_op(ec_fsm_t *fsm /**< finite state machine */)
+{
+ ec_master_t *master = fsm->master;
+ ec_slave_t *slave = fsm->slave;
+
+ if (ec_fsm_change_exec(&fsm->fsm_change)) return;
+
+ if (!ec_fsm_change_success(&fsm->fsm_change)) {
+ slave->error_flag = 1;
+ fsm->slave_state = ec_fsm_slave_state_error;
+ return;
+ }
// slave is now in OP
- fsm->slave_state = ec_fsm_end; // successful
-}
-
-/******************************************************************************
- * SII state machine
- *****************************************************************************/
-
-/**
- SII state: START_READING.
- Starts reading the slave information interface.
-*/
-
-void ec_fsm_sii_start_reading(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
-
- // initiate read operation
- if (fsm->sii_mode) {
- ec_datagram_npwr(datagram, fsm->slave->station_address, 0x502, 4);
- }
- else {
- ec_datagram_apwr(datagram, fsm->slave->ring_position, 0x502, 4);
- }
-
- EC_WRITE_U8 (datagram->data, 0x00); // read-only access
- EC_WRITE_U8 (datagram->data + 1, 0x01); // request read operation
- EC_WRITE_U16(datagram->data + 2, fsm->sii_offset);
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->sii_state = ec_fsm_sii_read_check;
-}
-
-/*****************************************************************************/
-
-/**
- SII state: READ_CHECK.
- Checks, if the SII-read-datagram has been sent and issues a fetch datagram.
-*/
-
-void ec_fsm_sii_read_check(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- EC_ERR("SII: Reception of read datagram failed.\n");
- fsm->sii_state = ec_fsm_error;
- return;
- }
-
- fsm->sii_start = get_cycles();
-
- // issue check/fetch datagram
- if (fsm->sii_mode) {
- ec_datagram_nprd(datagram, fsm->slave->station_address, 0x502, 10);
- }
- else {
- ec_datagram_aprd(datagram, fsm->slave->ring_position, 0x502, 10);
- }
-
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->sii_state = ec_fsm_sii_read_fetch;
-}
-
-/*****************************************************************************/
-
-/**
- SII state: READ_FETCH.
- Fetches the result of an SII-read datagram.
-*/
-
-void ec_fsm_sii_read_fetch(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- EC_ERR("SII: Reception of check/fetch datagram failed.\n");
- fsm->sii_state = ec_fsm_error;
- return;
- }
-
- // check "busy bit"
- if (EC_READ_U8(datagram->data + 1) & 0x81) {
- // still busy... timeout?
- if (get_cycles() - fsm->sii_start >= (cycles_t) 10 * cpu_khz) {
- EC_ERR("SII: Timeout.\n");
- fsm->sii_state = ec_fsm_error;
-#if 0
- EC_DBG("SII busy: %02X %02X %02X %02X\n",
- EC_READ_U8(datagram->data + 0),
- EC_READ_U8(datagram->data + 1),
- EC_READ_U8(datagram->data + 2),
- EC_READ_U8(datagram->data + 3));
-#endif
- }
-
- // issue check/fetch datagram again
- if (fsm->sii_mode) {
- ec_datagram_nprd(datagram, fsm->slave->station_address, 0x502, 10);
- }
- else {
- ec_datagram_aprd(datagram, fsm->slave->ring_position, 0x502, 10);
- }
- ec_master_queue_datagram(fsm->master, datagram);
- return;
- }
-
-#if 0
- EC_DBG("SII rec: %02X %02X %02X %02X - %02X %02X %02X %02X\n",
- EC_READ_U8(datagram->data + 0), EC_READ_U8(datagram->data + 1),
- EC_READ_U8(datagram->data + 2), EC_READ_U8(datagram->data + 3),
- EC_READ_U8(datagram->data + 6), EC_READ_U8(datagram->data + 7),
- EC_READ_U8(datagram->data + 8), EC_READ_U8(datagram->data + 9));
-#endif
-
- // SII value received.
- memcpy(fsm->sii_value, datagram->data + 6, 4);
- fsm->sii_state = ec_fsm_end;
-}
-
-/*****************************************************************************/
-
-/**
- SII state: START_WRITING.
- Starts reading the slave information interface.
-*/
-
-void ec_fsm_sii_start_writing(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
-
- // initiate write operation
- ec_datagram_npwr(datagram, fsm->slave->station_address, 0x502, 8);
- EC_WRITE_U8 (datagram->data, 0x01); // enable write access
- EC_WRITE_U8 (datagram->data + 1, 0x02); // request write operation
- EC_WRITE_U32(datagram->data + 2, fsm->sii_offset);
- memcpy(datagram->data + 6, fsm->sii_value, 2);
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->sii_state = ec_fsm_sii_write_check;
-}
-
-/*****************************************************************************/
-
-/**
- SII state: WRITE_CHECK.
-*/
-
-void ec_fsm_sii_write_check(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- EC_ERR("SII: Reception of write datagram failed.\n");
- fsm->sii_state = ec_fsm_error;
- return;
- }
-
- fsm->sii_start = get_cycles();
-
- // issue check/fetch datagram
- ec_datagram_nprd(datagram, fsm->slave->station_address, 0x502, 2);
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->sii_state = ec_fsm_sii_write_check2;
-}
-
-/*****************************************************************************/
-
-/**
- SII state: WRITE_CHECK2.
-*/
-
-void ec_fsm_sii_write_check2(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- EC_ERR("SII: Reception of write check datagram failed.\n");
- fsm->sii_state = ec_fsm_error;
- return;
- }
-
- if (EC_READ_U8(datagram->data + 1) & 0x82) {
- // still busy... timeout?
- if (get_cycles() - fsm->sii_start >= (cycles_t) 10 * cpu_khz) {
- EC_ERR("SII: Write timeout.\n");
- fsm->sii_state = ec_fsm_error;
- }
-
- // issue check/fetch datagram again
- ec_master_queue_datagram(fsm->master, datagram);
- }
- else if (EC_READ_U8(datagram->data + 1) & 0x40) {
- EC_ERR("SII: Write operation failed!\n");
- fsm->sii_state = ec_fsm_error;
- }
- else { // success
- fsm->sii_state = ec_fsm_end;
- }
-}
-
-/******************************************************************************
- * state change state machine
- *****************************************************************************/
-
-/**
- Change state: START.
-*/
-
-void ec_fsm_change_start(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
-
- fsm->change_jiffies = jiffies;
-
- // write new state to slave
- ec_datagram_npwr(datagram, slave->station_address, 0x0120, 2);
- EC_WRITE_U16(datagram->data, fsm->change_new);
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->change_state = ec_fsm_change_check;
-}
-
-/*****************************************************************************/
-
-/**
- Change state: CHECK.
-*/
-
-void ec_fsm_change_check(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED) {
- fsm->change_state = ec_fsm_error;
- EC_ERR("Failed to send state datagram to slave %i!\n",
- fsm->slave->ring_position);
- return;
- }
-
- if (datagram->working_counter != 1) {
- if (jiffies - fsm->change_jiffies >= 3 * HZ) {
- fsm->change_state = ec_fsm_error;
- EC_ERR("Failed to set state 0x%02X on slave %i: Slave did not"
- " respond.\n", fsm->change_new, fsm->slave->ring_position);
- return;
- }
-
- // repeat writing new state to slave
- ec_datagram_npwr(datagram, slave->station_address, 0x0120, 2);
- EC_WRITE_U16(datagram->data, fsm->change_new);
- ec_master_queue_datagram(fsm->master, datagram);
- return;
- }
-
- fsm->change_jiffies = jiffies;
-
- // read AL status from slave
- ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2);
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->change_state = ec_fsm_change_status;
-}
-
-/*****************************************************************************/
-
-/**
- Change state: STATUS.
-*/
-
-void ec_fsm_change_status(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- fsm->change_state = ec_fsm_error;
- EC_ERR("Failed to check state 0x%02X on slave %i.\n",
- fsm->change_new, slave->ring_position);
- return;
- }
-
- slave->current_state = EC_READ_U8(datagram->data);
-
- if (slave->current_state == fsm->change_new) {
- // state has been set successfully
- fsm->change_state = ec_fsm_end;
- return;
- }
-
- if (slave->current_state & 0x10) {
- // state change error
- fsm->change_new = slave->current_state & 0x0F;
- EC_ERR("Failed to set state 0x%02X - Slave %i refused state change"
- " (code 0x%02X)!\n", fsm->change_new, slave->ring_position,
- slave->current_state);
- // fetch AL status error code
- ec_datagram_nprd(datagram, slave->station_address, 0x0134, 2);
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->change_state = ec_fsm_change_code;
- return;
- }
-
- if (jiffies - fsm->change_jiffies >= 100 * HZ / 1000) { // 100ms
- // timeout while checking
- fsm->change_state = ec_fsm_error;
- EC_ERR("Timeout while setting state 0x%02X on slave %i.\n",
- fsm->change_new, slave->ring_position);
- return;
- }
-
- // still old state: check again
- ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2);
- ec_master_queue_datagram(fsm->master, datagram);
-}
-
-/*****************************************************************************/
-
-/**
- Application layer status messages.
-*/
-
-const ec_code_msg_t al_status_messages[] = {
- {0x0001, "Unspecified error"},
- {0x0011, "Invalud requested state change"},
- {0x0012, "Unknown requested state"},
- {0x0013, "Bootstrap not supported"},
- {0x0014, "No valid firmware"},
- {0x0015, "Invalid mailbox configuration"},
- {0x0016, "Invalid mailbox configuration"},
- {0x0017, "Invalid sync manager configuration"},
- {0x0018, "No valid inputs available"},
- {0x0019, "No valid outputs"},
- {0x001A, "Synchronisation error"},
- {0x001B, "Sync manager watchdog"},
- {0x001C, "Invalid sync manager types"},
- {0x001D, "Invalid output configuration"},
- {0x001E, "Invalid input configuration"},
- {0x001F, "Invalid watchdog configuration"},
- {0x0020, "Slave needs cold start"},
- {0x0021, "Slave needs INIT"},
- {0x0022, "Slave needs PREOP"},
- {0x0023, "Slave needs SAVEOP"},
- {0x0030, "Invalid DC SYNCH configuration"},
- {0x0031, "Invalid DC latch configuration"},
- {0x0032, "PLL error"},
- {0x0033, "Invalid DC IO error"},
- {0x0034, "Invalid DC timeout error"},
- {0x0042, "MBOX EOE"},
- {0x0043, "MBOX COE"},
- {0x0044, "MBOX FOE"},
- {0x0045, "MBOX SOE"},
- {0x004F, "MBOX VOE"},
- {}
-};
-
-/*****************************************************************************/
-
-/**
- Change state: CODE.
-*/
-
-void ec_fsm_change_code(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
- uint32_t code;
- const ec_code_msg_t *al_msg;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- fsm->change_state = ec_fsm_error;
- EC_ERR("Reception of AL status code datagram failed.\n");
- return;
- }
-
- if ((code = EC_READ_U16(datagram->data))) {
- for (al_msg = al_status_messages; al_msg->code; al_msg++) {
- if (al_msg->code != code) continue;
- EC_ERR("AL status message 0x%04X: \"%s\".\n",
- al_msg->code, al_msg->message);
- break;
- }
- if (!al_msg->code)
- EC_ERR("Unknown AL status code 0x%04X.\n", code);
- }
-
- // acknowledge "old" slave state
- ec_datagram_npwr(datagram, slave->station_address, 0x0120, 2);
- EC_WRITE_U16(datagram->data, slave->current_state);
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->change_state = ec_fsm_change_ack;
-}
-
-/*****************************************************************************/
-
-/**
- Change state: ACK.
-*/
-
-void ec_fsm_change_ack(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- fsm->change_state = ec_fsm_error;
- EC_ERR("Reception of state ack datagram failed.\n");
- return;
- }
-
- fsm->change_jiffies = jiffies;
-
- // read new AL status
- ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2);
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->change_state = ec_fsm_change_check_ack;
-}
-
-/*****************************************************************************/
-
-/**
- Change state: CHECK ACK.
-*/
-
-void ec_fsm_change_check_ack(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
- ec_slave_state_t ack_state;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- fsm->change_state = ec_fsm_error;
- EC_ERR("Reception of state ack check datagram failed.\n");
- return;
- }
-
- ack_state = EC_READ_U8(datagram->data);
-
- if (ack_state == slave->current_state) {
- fsm->change_state = ec_fsm_error;
- EC_INFO("Acknowleged state 0x%02X on slave %i.\n",
- slave->current_state, slave->ring_position);
- return;
- }
-
- if (jiffies - fsm->change_jiffies >= 100 * HZ / 1000) { // 100ms
- // timeout while checking
- slave->current_state = EC_SLAVE_STATE_UNKNOWN;
- fsm->change_state = ec_fsm_error;
- EC_ERR("Timeout while acknowleging state 0x%02X on slave %i.\n",
- fsm->change_new, slave->ring_position);
- return;
- }
-
- // reread new AL status
- ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2);
- ec_master_queue_datagram(fsm->master, datagram);
-}
-
-/******************************************************************************
- * CoE state machine
- *****************************************************************************/
-
-/**
- CoE state: DOWN_START.
-*/
-
-void ec_fsm_coe_down_start(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
- ec_sdo_data_t *sdodata = fsm->sdodata;
- uint8_t *data;
-
- EC_INFO("Downloading SDO 0x%04X:%i to slave %i.\n",
- sdodata->index, sdodata->subindex, slave->ring_position);
-
- if (slave->sii_rx_mailbox_size < 6 + 10 + sdodata->size) {
- EC_ERR("SDO fragmenting not supported yet!\n");
- fsm->coe_state = ec_fsm_error;
- return;
- }
-
- if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03,
- sdodata->size + 10))) {
- fsm->coe_state = ec_fsm_error;
- return;
- }
-
- EC_WRITE_U16(data, 0x2 << 12); // SDO request
- EC_WRITE_U8 (data + 2, (0x1 // size specified
- | 0x1 << 5)); // Download request
- EC_WRITE_U16(data + 3, sdodata->index);
- EC_WRITE_U8 (data + 5, sdodata->subindex);
- EC_WRITE_U32(data + 6, sdodata->size);
- memcpy(data + 10, sdodata->data, sdodata->size);
-
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->coe_state = ec_fsm_coe_down_request;
-}
-
-/*****************************************************************************/
-
-/**
- CoE state: DOWN_REQUEST.
-*/
-
-void ec_fsm_coe_down_request(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- fsm->coe_state = ec_fsm_error;
- EC_ERR("Reception of CoE download request failed.\n");
- return;
- }
-
- fsm->coe_start = get_cycles();
-
- ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->coe_state = ec_fsm_coe_down_check;
-}
-
-/*****************************************************************************/
-
-/**
- CoE state: DOWN_CHECK.
-*/
-
-void ec_fsm_coe_down_check(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- fsm->coe_state = ec_fsm_error;
- EC_ERR("Reception of CoE mailbox check datagram failed.\n");
- return;
- }
-
- if (!ec_slave_mbox_check(datagram)) {
- if (get_cycles() - fsm->coe_start >= (cycles_t) 100 * cpu_khz) {
- fsm->coe_state = ec_fsm_error;
- EC_ERR("Timeout while checking SDO configuration on slave %i.\n",
- slave->ring_position);
- return;
- }
-
- ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
- ec_master_queue_datagram(fsm->master, datagram);
- return;
- }
-
- // Fetch response
- ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
- ec_master_queue_datagram(fsm->master, datagram);
- fsm->coe_state = ec_fsm_coe_down_response;
-}
-
-/*****************************************************************************/
-
-/**
- CoE state: DOWN_RESPONSE.
-*/
-
-void ec_fsm_coe_down_response(ec_fsm_t *fsm /**< finite state machine */)
-{
- ec_datagram_t *datagram = &fsm->datagram;
- ec_slave_t *slave = fsm->slave;
- uint8_t *data, mbox_prot;
- size_t rec_size;
- ec_sdo_data_t *sdodata = fsm->sdodata;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED
- || datagram->working_counter != 1) {
- fsm->coe_state = ec_fsm_error;
- EC_ERR("Reception of CoE download response failed.\n");
- return;
- }
-
- if (!(data = ec_slave_mbox_fetch(slave, datagram,
- &mbox_prot, &rec_size))) {
- fsm->coe_state = ec_fsm_error;
- return;
- }
-
- if (mbox_prot != 0x03) { // CoE
- EC_WARN("Received mailbox protocol 0x%02X as response.\n", mbox_prot);
- fsm->coe_state = ec_fsm_error;
- return;
- }
-
- if (rec_size < 6) {
- fsm->coe_state = ec_fsm_error;
- EC_ERR("Received data is too small (%i bytes):\n", rec_size);
- ec_print_data(data, rec_size);
- return;
- }
-
- if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
- EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
- fsm->coe_state = ec_fsm_error;
- EC_ERR("SDO download 0x%04X:%X (%i bytes) aborted on slave %i.\n",
- sdodata->index, sdodata->subindex, sdodata->size,
- slave->ring_position);
- if (rec_size < 10) {
- EC_ERR("Incomplete Abort command:\n");
- ec_print_data(data, rec_size);
- }
- else
- ec_canopen_abort_msg(EC_READ_U32(data + 6));
- return;
- }
-
- if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
- EC_READ_U8 (data + 2) >> 5 != 0x3 || // Download response
- EC_READ_U16(data + 3) != sdodata->index || // index
- EC_READ_U8 (data + 5) != sdodata->subindex) { // subindex
- fsm->coe_state = ec_fsm_error;
- EC_ERR("SDO download 0x%04X:%X (%i bytes) failed:\n",
- sdodata->index, sdodata->subindex, sdodata->size);
- EC_ERR("Invalid SDO download response at slave %i!\n",
- slave->ring_position);
- ec_print_data(data, rec_size);
- return;
- }
-
- fsm->coe_state = ec_fsm_end; // success
-}
-
-/*****************************************************************************/
-
-/**
- SDO abort messages.
- The "abort SDO transfer request" supplies an abort code,
- which can be translated to clear text. This table does
- the mapping of the codes and messages.
-*/
-
-const ec_code_msg_t sdo_abort_messages[] = {
- {0x05030000, "Toggle bit not changed"},
- {0x05040000, "SDO protocol timeout"},
- {0x05040001, "Client/Server command specifier not valid or unknown"},
- {0x05040005, "Out of memory"},
- {0x06010000, "Unsupported access to an object"},
- {0x06010001, "Attempt to read a write-only object"},
- {0x06010002, "Attempt to write a read-only object"},
- {0x06020000, "This object does not exist in the object directory"},
- {0x06040041, "The object cannot be mapped into the PDO"},
- {0x06040042, "The number and length of the objects to be mapped would"
- " exceed the PDO length"},
- {0x06040043, "General parameter incompatibility reason"},
- {0x06040047, "Gerneral internal incompatibility in device"},
- {0x06060000, "Access failure due to a hardware error"},
- {0x06070010, "Data type does not match, length of service parameter does"
- " not match"},
- {0x06070012, "Data type does not match, length of service parameter too"
- " high"},
- {0x06070013, "Data type does not match, length of service parameter too"
- " low"},
- {0x06090011, "Subindex does not exist"},
- {0x06090030, "Value range of parameter exceeded"},
- {0x06090031, "Value of parameter written too high"},
- {0x06090032, "Value of parameter written too low"},
- {0x06090036, "Maximum value is less than minimum value"},
- {0x08000000, "General error"},
- {0x08000020, "Data cannot be transferred or stored to the application"},
- {0x08000021, "Data cannot be transferred or stored to the application"
- " because of local control"},
- {0x08000022, "Data cannot be transferred or stored to the application"
- " because of the present device state"},
- {0x08000023, "Object dictionary dynamic generation fails or no object"
- " dictionary is present"},
- {}
-};
-
-/*****************************************************************************/
-
-/**
- Outputs an SDO abort message.
-*/
-
-void ec_canopen_abort_msg(uint32_t abort_code)
-{
- const ec_code_msg_t *abort_msg;
-
- for (abort_msg = sdo_abort_messages; abort_msg->code; abort_msg++) {
- if (abort_msg->code == abort_code) {
- EC_ERR("SDO abort message 0x%08X: \"%s\".\n",
- abort_msg->code, abort_msg->message);
- return;
- }
- }
-
- EC_ERR("Unknown SDO abort code 0x%08X.\n", abort_code);
+
+ if (master->debug_level) {
+ EC_DBG("Slave %i is now in OP.\n", slave->ring_position);
+ EC_DBG("Finished configuration of slave %i.\n", slave->ring_position);
+ }
+
+ fsm->slave_state = ec_fsm_slave_state_end; // successful
}
/******************************************************************************
@@ -2278,7 +1687,7 @@
State: ERROR.
*/
-void ec_fsm_error(ec_fsm_t *fsm /**< finite state machine */)
+void ec_fsm_slave_state_error(ec_fsm_t *fsm /**< finite state machine */)
{
}
@@ -2288,8 +1697,8 @@
State: END.
*/
-void ec_fsm_end(ec_fsm_t *fsm /**< finite state machine */)
-{
-}
-
-/*****************************************************************************/
+void ec_fsm_slave_state_end(ec_fsm_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
--- a/master/fsm.h Fri Oct 13 10:07:10 2006 +0000
+++ b/master/fsm.h Tue Nov 07 12:13:30 2006 +0000
@@ -45,6 +45,11 @@
#include "../include/ecrt.h"
#include "datagram.h"
#include "slave.h"
+#include "canopen.h"
+
+#include "fsm_sii.h"
+#include "fsm_change.h"
+#include "fsm_coe.h"
/*****************************************************************************/
@@ -64,38 +69,30 @@
unsigned int master_slaves_responding; /**< number of responding slaves */
ec_slave_state_t master_slave_states; /**< states of responding slaves */
unsigned int master_validation; /**< non-zero, if validation to do */
+ uint16_t sii_offset; /**< current offset for SII access */
+ ec_sdo_request_t *sdo_request;
void (*slave_state)(ec_fsm_t *); /**< slave state function */
+ ec_sdo_data_t *sdodata; /**< SDO configuration data */
- void (*sii_state)(ec_fsm_t *); /**< SII state function */
- uint16_t sii_offset; /**< input: offset in SII */
- unsigned int sii_mode; /**< SII reading done by APRD (0) or NPRD (1) */
- uint8_t sii_value[4]; /**< raw SII value (32bit) */
- cycles_t sii_start; /**< sii start */
-
- void (*change_state)(ec_fsm_t *); /**< slave state change state function */
- ec_slave_state_t change_new; /**< input: new state */
- unsigned long change_jiffies; /**< change timer */
-
- void (*coe_state)(ec_fsm_t *); /**< CoE state function */
- ec_sdo_data_t *sdodata; /**< input/output: SDO data object */
- cycles_t coe_start; /**< CoE timestamp */
+ ec_fsm_sii_t fsm_sii; /**< SII state machine */
+ ec_fsm_change_t fsm_change; /**< State change state machine */
+ ec_fsm_coe_t fsm_coe; /**< CoE state machine */
};
/*****************************************************************************/
int ec_fsm_init(ec_fsm_t *, ec_master_t *);
void ec_fsm_clear(ec_fsm_t *);
-void ec_fsm_reset(ec_fsm_t *);
-void ec_fsm_execute(ec_fsm_t *);
-void ec_fsm_startup(ec_fsm_t *);
-int ec_fsm_startup_running(ec_fsm_t *);
-int ec_fsm_startup_success(ec_fsm_t *);
+int ec_fsm_exec(ec_fsm_t *);
+int ec_fsm_running(ec_fsm_t *);
+int ec_fsm_success(ec_fsm_t *);
-void ec_fsm_configuration(ec_fsm_t *);
-int ec_fsm_configuration_running(ec_fsm_t *);
-int ec_fsm_configuration_success(ec_fsm_t *);
+// TODO: layout slave state machines
+void ec_fsm_slaveconf_state_start(ec_fsm_t *);
+void ec_fsm_slave_state_end(ec_fsm_t *);
+void ec_fsm_slave_state_error(ec_fsm_t *);
/*****************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/fsm_change.c Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,491 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT state change FSM.
+*/
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "fsm_change.h"
+
+/*****************************************************************************/
+
+void ec_fsm_change_state_start(ec_fsm_change_t *);
+void ec_fsm_change_state_check(ec_fsm_change_t *);
+void ec_fsm_change_state_status(ec_fsm_change_t *);
+void ec_fsm_change_state_code(ec_fsm_change_t *);
+void ec_fsm_change_state_start_ack(ec_fsm_change_t *);
+void ec_fsm_change_state_ack(ec_fsm_change_t *);
+void ec_fsm_change_state_check_ack(ec_fsm_change_t *);
+void ec_fsm_change_state_end(ec_fsm_change_t *);
+void ec_fsm_change_state_error(ec_fsm_change_t *);
+
+/*****************************************************************************/
+
+/**
+ Constructor.
+*/
+
+void ec_fsm_change_init(ec_fsm_change_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< datagram */
+ )
+{
+ fsm->state = NULL;
+ fsm->datagram = datagram;
+}
+
+/*****************************************************************************/
+
+/**
+ Destructor.
+*/
+
+void ec_fsm_change_clear(ec_fsm_change_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ Starts the change state machine.
+*/
+
+void ec_fsm_change_start(ec_fsm_change_t *fsm, /**< finite state machine */
+ ec_slave_t *slave, /**< EtherCAT slave */
+ ec_slave_state_t state /**< requested state */
+ )
+{
+ fsm->mode = EC_FSM_CHANGE_MODE_FULL;
+ fsm->slave = slave;
+ fsm->requested_state = state;
+ fsm->state = ec_fsm_change_state_start;
+}
+
+/*****************************************************************************/
+
+/**
+ Starts the change state machine to only acknowlegde a slave's state.
+*/
+
+void ec_fsm_change_ack(ec_fsm_change_t *fsm, /**< finite state machine */
+ ec_slave_t *slave /**< EtherCAT slave */
+ )
+{
+ fsm->mode = EC_FSM_CHANGE_MODE_ACK_ONLY;
+ fsm->slave = slave;
+ fsm->requested_state = EC_SLAVE_STATE_UNKNOWN;
+ fsm->state = ec_fsm_change_state_start_ack;
+}
+
+/*****************************************************************************/
+
+/**
+ Executes the current state of the state machine.
+ \return false, if the state machine has terminated
+*/
+
+int ec_fsm_change_exec(ec_fsm_change_t *fsm /**< finite state machine */)
+{
+ fsm->state(fsm);
+
+ return fsm->state != ec_fsm_change_state_end
+ && fsm->state != ec_fsm_change_state_error;
+}
+
+/*****************************************************************************/
+
+/**
+ Returns, if the state machine terminated with success.
+ \return non-zero if successful.
+*/
+
+int ec_fsm_change_success(ec_fsm_change_t *fsm /**< Finite state machine */)
+{
+ return fsm->state == ec_fsm_change_state_end;
+}
+
+/******************************************************************************
+ * state change state machine
+ *****************************************************************************/
+
+/**
+ Change state: START.
+*/
+
+void ec_fsm_change_state_start(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ fsm->take_time = 1;
+ fsm->old_state = fsm->slave->current_state;
+
+ // write new state to slave
+ ec_datagram_npwr(datagram, slave->station_address, 0x0120, 2);
+ EC_WRITE_U16(datagram->data, fsm->requested_state);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_change_state_check;
+}
+
+/*****************************************************************************/
+
+/**
+ Change state: CHECK.
+*/
+
+void ec_fsm_change_state_check(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_ERR("Failed to send state datagram to slave %i!\n",
+ fsm->slave->ring_position);
+ return;
+ }
+
+ if (fsm->take_time) {
+ fsm->take_time = 0;
+ fsm->jiffies_start = datagram->jiffies_sent;
+ }
+
+ if (datagram->working_counter != 1) {
+ if (datagram->jiffies_received - fsm->jiffies_start >= 3 * HZ) {
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(fsm->requested_state, state_str);
+ fsm->state = ec_fsm_change_state_error;
+ EC_ERR("Failed to set state %s on slave %i: Slave did not"
+ " respond.\n", state_str, fsm->slave->ring_position);
+ return;
+ }
+
+ // repeat writing new state to slave
+ ec_datagram_npwr(datagram, slave->station_address, 0x0120, 2);
+ EC_WRITE_U16(datagram->data, fsm->requested_state);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ return;
+ }
+
+ fsm->take_time = 1;
+
+ // read AL status from slave
+ ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_change_state_status;
+}
+
+/*****************************************************************************/
+
+/**
+ Change state: STATUS.
+*/
+
+void ec_fsm_change_state_status(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ char req_state[EC_STATE_STRING_SIZE];
+ ec_state_string(fsm->requested_state, req_state);
+ fsm->state = ec_fsm_change_state_error;
+ EC_ERR("Failed to check state %s on slave %i.\n",
+ req_state, slave->ring_position);
+ return;
+ }
+
+ if (fsm->take_time) {
+ fsm->take_time = 0;
+ fsm->jiffies_start = datagram->jiffies_sent;
+ }
+
+ slave->current_state = EC_READ_U8(datagram->data);
+
+ if (slave->current_state == fsm->requested_state) {
+ // state has been set successfully
+ fsm->state = ec_fsm_change_state_end;
+ return;
+ }
+
+ if (slave->current_state != fsm->old_state) { // state changed
+ char req_state[EC_STATE_STRING_SIZE], cur_state[EC_STATE_STRING_SIZE];
+
+ ec_state_string(slave->current_state, cur_state);
+
+ if ((slave->current_state & 0x0F) != (fsm->old_state & 0x0F)) {
+ // Slave spontaneously changed its state just before the new state
+ // was written. Accept current state as old state and wait for
+ // state change
+ fsm->old_state = slave->current_state;
+ EC_WARN("Slave %i changed to %s in the meantime.\n",
+ slave->ring_position, cur_state);
+ goto again;
+ }
+
+ // state change error
+
+ slave->error_flag = 1;
+ ec_state_string(fsm->requested_state, req_state);
+
+ EC_ERR("Failed to set %s state, slave %i refused state change (%s).\n",
+ req_state, slave->ring_position, cur_state);
+ // fetch AL status error code
+ ec_datagram_nprd(datagram, slave->station_address, 0x0134, 2);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_change_state_code;
+ return;
+ }
+
+ // still old state
+
+ if (datagram->jiffies_received - fsm->jiffies_start >= HZ) { // 1s
+ // timeout while checking
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(fsm->requested_state, state_str);
+ fsm->state = ec_fsm_change_state_error;
+ EC_ERR("Timeout while setting state %s on slave %i.\n",
+ state_str, slave->ring_position);
+ return;
+ }
+
+ again:
+ // no timeout yet. check again
+ ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+}
+
+/*****************************************************************************/
+
+/**
+ Application layer status messages.
+*/
+
+const ec_code_msg_t al_status_messages[] = {
+ {0x0001, "Unspecified error"},
+ {0x0011, "Invalud requested state change"},
+ {0x0012, "Unknown requested state"},
+ {0x0013, "Bootstrap not supported"},
+ {0x0014, "No valid firmware"},
+ {0x0015, "Invalid mailbox configuration"},
+ {0x0016, "Invalid mailbox configuration"},
+ {0x0017, "Invalid sync manager configuration"},
+ {0x0018, "No valid inputs available"},
+ {0x0019, "No valid outputs"},
+ {0x001A, "Synchronisation error"},
+ {0x001B, "Sync manager watchdog"},
+ {0x001C, "Invalid sync manager types"},
+ {0x001D, "Invalid output configuration"},
+ {0x001E, "Invalid input configuration"},
+ {0x001F, "Invalid watchdog configuration"},
+ {0x0020, "Slave needs cold start"},
+ {0x0021, "Slave needs INIT"},
+ {0x0022, "Slave needs PREOP"},
+ {0x0023, "Slave needs SAVEOP"},
+ {0x0030, "Invalid DC SYNCH configuration"},
+ {0x0031, "Invalid DC latch configuration"},
+ {0x0032, "PLL error"},
+ {0x0033, "Invalid DC IO error"},
+ {0x0034, "Invalid DC timeout error"},
+ {0x0042, "MBOX EOE"},
+ {0x0043, "MBOX COE"},
+ {0x0044, "MBOX FOE"},
+ {0x0045, "MBOX SOE"},
+ {0x004F, "MBOX VOE"},
+ {}
+};
+
+/*****************************************************************************/
+
+/**
+ Change state: CODE.
+*/
+
+void ec_fsm_change_state_code(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ uint32_t code;
+ const ec_code_msg_t *al_msg;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ EC_WARN("Reception of AL status code datagram failed.\n");
+ }
+ else {
+ if ((code = EC_READ_U16(datagram->data))) {
+ for (al_msg = al_status_messages; al_msg->code; al_msg++) {
+ if (al_msg->code != code) continue;
+ EC_ERR("AL status message 0x%04X: \"%s\".\n",
+ al_msg->code, al_msg->message);
+ break;
+ }
+ if (!al_msg->code)
+ EC_ERR("Unknown AL status code 0x%04X.\n", code);
+ }
+ }
+
+ // acknowledge "old" slave state
+ ec_fsm_change_state_start_ack(fsm); // execute immediately
+}
+
+/*****************************************************************************/
+
+/**
+ Change state: START ACK.
+*/
+
+void ec_fsm_change_state_start_ack(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+
+ ec_datagram_npwr(datagram, slave->station_address, 0x0120, 2);
+ EC_WRITE_U16(datagram->data, slave->current_state);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_change_state_ack;
+}
+
+/*****************************************************************************/
+
+/**
+ Change state: ACK.
+*/
+
+void ec_fsm_change_state_ack(ec_fsm_change_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_ERR("Reception of state ack datagram failed.\n");
+ return;
+ }
+
+ fsm->take_time = 1;
+
+ // read new AL status
+ ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_change_state_check_ack;
+}
+
+/*****************************************************************************/
+
+/**
+ Change state: CHECK ACK.
+*/
+
+void ec_fsm_change_state_check_ack(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_ERR("Reception of state ack check datagram failed.\n");
+ return;
+ }
+
+ if (fsm->take_time) {
+ fsm->take_time = 0;
+ fsm->jiffies_start = datagram->jiffies_sent;
+ }
+
+ slave->current_state = EC_READ_U8(datagram->data);
+
+ if (!(slave->current_state & EC_SLAVE_STATE_ACK_ERR)) {
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, state_str);
+ if (fsm->mode == EC_FSM_CHANGE_MODE_FULL) {
+ fsm->state = ec_fsm_change_state_error;
+ }
+ else { // EC_FSM_CHANGE_MODE_ACK_ONLY
+ fsm->state = ec_fsm_change_state_end;
+ }
+ EC_INFO("Acknowledged state %s on slave %i.\n",
+ state_str, slave->ring_position);
+ return;
+ }
+
+ if (datagram->jiffies_received - fsm->jiffies_start >= HZ) { // 1s
+ // timeout while checking
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, state_str);
+ fsm->state = ec_fsm_change_state_error;
+ EC_ERR("Timeout while acknowledging state %s on slave %i.\n",
+ state_str, slave->ring_position);
+ return;
+ }
+
+ // reread new AL status
+ ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+}
+
+/*****************************************************************************/
+
+/**
+ State: ERROR.
+*/
+
+void ec_fsm_change_state_error(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ State: END.
+*/
+
+void ec_fsm_change_state_end(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/fsm_change.h Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,95 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT state change FSM.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_CHANGE__
+#define __EC_FSM_CHANGE__
+
+#include "globals.h"
+#include "../include/ecrt.h"
+#include "datagram.h"
+#include "slave.h"
+
+/*****************************************************************************/
+
+/**
+ Mode of the change state machine.
+*/
+
+typedef enum {
+ EC_FSM_CHANGE_MODE_FULL, /**< full state change */
+ EC_FSM_CHANGE_MODE_ACK_ONLY /**< only state acknowledgement */
+}
+ec_fsm_change_mode_t;
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_change ec_fsm_change_t; /**< \see ec_fsm_change */
+
+/**
+ EtherCAT state change FSM.
+*/
+
+struct ec_fsm_change
+{
+ ec_slave_t *slave; /**< slave the FSM runs on */
+ ec_datagram_t *datagram; /**< datagram used in the state machine */
+
+ void (*state)(ec_fsm_change_t *); /**< slave state change state function */
+ ec_fsm_change_mode_t mode; /**< full state change, or ack only. */
+ ec_slave_state_t requested_state; /**< input: state */
+ ec_slave_state_t old_state; /**< prior slave state */
+ unsigned long jiffies_start; /**< change timer */
+ uint8_t take_time; /**< take sending timestamp */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_change_init(ec_fsm_change_t *, ec_datagram_t *);
+void ec_fsm_change_clear(ec_fsm_change_t *);
+
+void ec_fsm_change_start(ec_fsm_change_t *, ec_slave_t *, ec_slave_state_t);
+void ec_fsm_change_ack(ec_fsm_change_t *, ec_slave_t *);
+
+int ec_fsm_change_exec(ec_fsm_change_t *);
+int ec_fsm_change_success(ec_fsm_change_t *);
+
+/*****************************************************************************/
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/fsm_coe.c Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,1440 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT CoE state machines.
+*/
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "fsm_coe.h"
+
+/*****************************************************************************/
+
+void ec_fsm_coe_dict_start(ec_fsm_coe_t *);
+void ec_fsm_coe_dict_request(ec_fsm_coe_t *);
+void ec_fsm_coe_dict_check(ec_fsm_coe_t *);
+void ec_fsm_coe_dict_response(ec_fsm_coe_t *);
+void ec_fsm_coe_dict_desc_request(ec_fsm_coe_t *);
+void ec_fsm_coe_dict_desc_check(ec_fsm_coe_t *);
+void ec_fsm_coe_dict_desc_response(ec_fsm_coe_t *);
+void ec_fsm_coe_dict_entry_request(ec_fsm_coe_t *);
+void ec_fsm_coe_dict_entry_check(ec_fsm_coe_t *);
+void ec_fsm_coe_dict_entry_response(ec_fsm_coe_t *);
+
+void ec_fsm_coe_down_start(ec_fsm_coe_t *);
+void ec_fsm_coe_down_request(ec_fsm_coe_t *);
+void ec_fsm_coe_down_check(ec_fsm_coe_t *);
+void ec_fsm_coe_down_response(ec_fsm_coe_t *);
+
+void ec_fsm_coe_up_start(ec_fsm_coe_t *);
+void ec_fsm_coe_up_request(ec_fsm_coe_t *);
+void ec_fsm_coe_up_check(ec_fsm_coe_t *);
+void ec_fsm_coe_up_response(ec_fsm_coe_t *);
+void ec_fsm_coe_up_seg_request(ec_fsm_coe_t *);
+void ec_fsm_coe_up_seg_check(ec_fsm_coe_t *);
+void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *);
+
+void ec_fsm_coe_end(ec_fsm_coe_t *);
+void ec_fsm_coe_error(ec_fsm_coe_t *);
+
+/*****************************************************************************/
+
+/**
+ SDO abort messages.
+ The "abort SDO transfer request" supplies an abort code,
+ which can be translated to clear text. This table does
+ the mapping of the codes and messages.
+*/
+
+const ec_code_msg_t sdo_abort_messages[] = {
+ {0x05030000, "Toggle bit not changed"},
+ {0x05040000, "SDO protocol timeout"},
+ {0x05040001, "Client/Server command specifier not valid or unknown"},
+ {0x05040005, "Out of memory"},
+ {0x06010000, "Unsupported access to an object"},
+ {0x06010001, "Attempt to read a write-only object"},
+ {0x06010002, "Attempt to write a read-only object"},
+ {0x06020000, "This object does not exist in the object directory"},
+ {0x06040041, "The object cannot be mapped into the PDO"},
+ {0x06040042, "The number and length of the objects to be mapped would"
+ " exceed the PDO length"},
+ {0x06040043, "General parameter incompatibility reason"},
+ {0x06040047, "Gerneral internal incompatibility in device"},
+ {0x06060000, "Access failure due to a hardware error"},
+ {0x06070010, "Data type does not match, length of service parameter does"
+ " not match"},
+ {0x06070012, "Data type does not match, length of service parameter too"
+ " high"},
+ {0x06070013, "Data type does not match, length of service parameter too"
+ " low"},
+ {0x06090011, "Subindex does not exist"},
+ {0x06090030, "Value range of parameter exceeded"},
+ {0x06090031, "Value of parameter written too high"},
+ {0x06090032, "Value of parameter written too low"},
+ {0x06090036, "Maximum value is less than minimum value"},
+ {0x08000000, "General error"},
+ {0x08000020, "Data cannot be transferred or stored to the application"},
+ {0x08000021, "Data cannot be transferred or stored to the application"
+ " because of local control"},
+ {0x08000022, "Data cannot be transferred or stored to the application"
+ " because of the present device state"},
+ {0x08000023, "Object dictionary dynamic generation fails or no object"
+ " dictionary is present"},
+ {}
+};
+
+/*****************************************************************************/
+
+/**
+ Outputs an SDO abort message.
+*/
+
+void ec_canopen_abort_msg(uint32_t abort_code)
+{
+ const ec_code_msg_t *abort_msg;
+
+ for (abort_msg = sdo_abort_messages; abort_msg->code; abort_msg++) {
+ if (abort_msg->code == abort_code) {
+ EC_ERR("SDO abort message 0x%08X: \"%s\".\n",
+ abort_msg->code, abort_msg->message);
+ return;
+ }
+ }
+
+ EC_ERR("Unknown SDO abort code 0x%08X.\n", abort_code);
+}
+
+/*****************************************************************************/
+
+/**
+ Constructor.
+*/
+
+void ec_fsm_coe_init(ec_fsm_coe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< datagram */
+ )
+{
+ fsm->state = NULL;
+ fsm->datagram = datagram;
+}
+
+/*****************************************************************************/
+
+/**
+ Destructor.
+*/
+
+void ec_fsm_coe_clear(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ Starts reading a slaves' SDO dictionary.
+*/
+
+void ec_fsm_coe_dictionary(ec_fsm_coe_t *fsm, /**< finite state machine */
+ ec_slave_t *slave /**< EtherCAT slave */
+ )
+{
+ fsm->slave = slave;
+ fsm->state = ec_fsm_coe_dict_start;
+}
+
+/*****************************************************************************/
+
+/**
+ Starts to download an SDO to a slave.
+*/
+
+void ec_fsm_coe_download(ec_fsm_coe_t *fsm, /**< finite state machine */
+ ec_slave_t *slave, /**< EtherCAT slave */
+ ec_sdo_data_t *sdodata /**< SDO data object */
+ )
+{
+ fsm->slave = slave;
+ fsm->sdodata = sdodata;
+ fsm->state = ec_fsm_coe_down_start;
+}
+
+/*****************************************************************************/
+
+/**
+ Starts to upload an SDO from a slave.
+*/
+
+void ec_fsm_coe_upload(ec_fsm_coe_t *fsm, /**< finite state machine */
+ ec_slave_t *slave, /**< EtherCAT slave */
+ ec_sdo_request_t *request /**< SDO request */
+ )
+{
+ fsm->slave = slave;
+ fsm->request = request;
+ fsm->state = ec_fsm_coe_up_start;
+}
+
+/*****************************************************************************/
+
+/**
+ Executes the current state of the state machine.
+ \return false, if state machine has terminated
+*/
+
+int ec_fsm_coe_exec(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ fsm->state(fsm);
+
+ return fsm->state != ec_fsm_coe_end && fsm->state != ec_fsm_coe_error;
+}
+
+/*****************************************************************************/
+
+/**
+ Returns, if the state machine terminated with success.
+ \return non-zero if successful.
+*/
+
+int ec_fsm_coe_success(ec_fsm_coe_t *fsm /**< Finite state machine */)
+{
+ return fsm->state == ec_fsm_coe_end;
+}
+
+/******************************************************************************
+ * CoE dictionary state machine
+ *****************************************************************************/
+
+/**
+ CoE state: DICT START.
+*/
+
+void ec_fsm_coe_dict_start(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ uint8_t *data;
+
+ if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 8))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x8 << 12); // SDO information
+ EC_WRITE_U8 (data + 2, 0x01); // Get OD List Request
+ EC_WRITE_U8 (data + 3, 0x00);
+ EC_WRITE_U16(data + 4, 0x0000);
+ EC_WRITE_U16(data + 6, 0x0001); // deliver all SDOs!
+
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_request;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT REQUEST.
+*/
+
+void ec_fsm_coe_dict_request(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE dictionary request failed on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ fsm->cycles_start = datagram->cycles_sent;
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT CHECK.
+*/
+
+void ec_fsm_coe_dict_check(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE mailbox check datagram failed on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ if (!ec_slave_mbox_check(datagram)) {
+ if (datagram->cycles_received
+ - fsm->cycles_start >= (cycles_t) 100 * cpu_khz) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Timeout while checking SDO dictionary on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ return;
+ }
+
+ // Fetch response
+ ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_response;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT RESPONSE.
+*/
+
+void ec_fsm_coe_dict_response(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ uint8_t *data, mbox_prot;
+ size_t rec_size;
+ unsigned int sdo_count, i;
+ uint16_t sdo_index;
+ ec_sdo_t *sdo;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE dictionary response failed on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ if (!(data = ec_slave_mbox_fetch(slave, datagram,
+ &mbox_prot, &rec_size))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_ERR("Received mailbox protocol 0x%02X as response.\n", mbox_prot);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x8 && // SDO information
+ (EC_READ_U8(data + 2) & 0x7F) == 0x07) { // error response
+ EC_ERR("SDO information error response at slave %i!\n",
+ slave->ring_position);
+ ec_canopen_abort_msg(EC_READ_U32(data + 6));
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x8 || // SDO information
+ (EC_READ_U8 (data + 2) & 0x7F) != 0x02) { // Get OD List response
+ EC_ERR("Invalid SDO list response at slave %i!\n",
+ slave->ring_position);
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (rec_size < 8) {
+ EC_ERR("Invalid data size!\n");
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ sdo_count = (rec_size - 8) / 2;
+
+ for (i = 0; i < sdo_count; i++) {
+ sdo_index = EC_READ_U16(data + 8 + i * 2);
+ if (!sdo_index) {
+ if (slave->master->debug_level)
+ EC_WARN("SDO dictionary of slave %i contains index 0x0000.\n",
+ slave->ring_position);
+ continue;
+ }
+
+ if (!(sdo = (ec_sdo_t *) kmalloc(sizeof(ec_sdo_t), GFP_ATOMIC))) {
+ EC_ERR("Failed to allocate memory for SDO!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (ec_sdo_init(sdo, sdo_index, slave)) {
+ EC_ERR("Failed to init SDO!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ list_add_tail(&sdo->list, &slave->sdo_dictionary);
+ }
+
+ if (EC_READ_U8(data + 2) & 0x80) { // more messages waiting. check again.
+ fsm->cycles_start = datagram->cycles_sent;
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_check;
+ return;
+ }
+
+ if (list_empty(&slave->sdo_dictionary)) {
+ // no SDOs in dictionary. finished.
+ fsm->state = ec_fsm_coe_end; // success
+ return;
+ }
+
+ // fetch SDO descriptions
+ fsm->sdo = list_entry(slave->sdo_dictionary.next, ec_sdo_t, list);
+
+ if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 8))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x8 << 12); // SDO information
+ EC_WRITE_U8 (data + 2, 0x03); // Get object description request
+ EC_WRITE_U8 (data + 3, 0x00);
+ EC_WRITE_U16(data + 4, 0x0000);
+ EC_WRITE_U16(data + 6, fsm->sdo->index); // SDO index
+
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_desc_request;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT DESC REQUEST.
+*/
+
+void ec_fsm_coe_dict_desc_request(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE SDO description"
+ " request failed on slave %i.\n", slave->ring_position);
+ return;
+ }
+
+ fsm->cycles_start = datagram->cycles_sent;
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_desc_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT DESC CHECK.
+*/
+
+void ec_fsm_coe_dict_desc_check(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE mailbox check datagram failed on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ if (!ec_slave_mbox_check(datagram)) {
+ if (datagram->cycles_received
+ - fsm->cycles_start >= (cycles_t) 100 * cpu_khz) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Timeout while checking SDO description on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ return;
+ }
+
+ // Fetch response
+ ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_desc_response;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT DESC RESPONSE.
+*/
+
+void ec_fsm_coe_dict_desc_response(ec_fsm_coe_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_t *sdo = fsm->sdo;
+ uint8_t *data, mbox_prot;
+ size_t rec_size, name_size;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE SDO description"
+ "response failed on slave %i.\n", slave->ring_position);
+ return;
+ }
+
+ if (!(data = ec_slave_mbox_fetch(slave, datagram,
+ &mbox_prot, &rec_size))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_ERR("Received mailbox protocol 0x%02X as response.\n", mbox_prot);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x8 && // SDO information
+ (EC_READ_U8 (data + 2) & 0x7F) == 0x07) { // error response
+ EC_ERR("SDO information error response at slave %i while"
+ " fetching SDO 0x%04X!\n", slave->ring_position,
+ sdo->index);
+ ec_canopen_abort_msg(EC_READ_U32(data + 6));
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x8 || // SDO information
+ (EC_READ_U8 (data + 2) & 0x7F) != 0x04 || // Object desc. response
+ EC_READ_U16(data + 6) != sdo->index) { // SDO index
+ EC_ERR("Invalid object description response at slave %i while"
+ " fetching SDO 0x%04X!\n", slave->ring_position,
+ sdo->index);
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (rec_size < 12) {
+ EC_ERR("Invalid data size!\n");
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ sdo->subindices = EC_READ_U8(data + 10);
+ sdo->object_code = EC_READ_U8(data + 11);
+
+ name_size = rec_size - 12;
+ if (name_size) {
+ if (!(sdo->name = kmalloc(name_size + 1, GFP_ATOMIC))) {
+ EC_ERR("Failed to allocate SDO name!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ memcpy(sdo->name, data + 12, name_size);
+ sdo->name[name_size] = 0;
+ }
+
+ if (EC_READ_U8(data + 2) & 0x80) {
+ EC_ERR("Fragment follows (not implemented)!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ // start fetching entries
+
+ fsm->subindex = 0;
+
+ if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 10))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x8 << 12); // SDO information
+ EC_WRITE_U8 (data + 2, 0x05); // Get entry description request
+ EC_WRITE_U8 (data + 3, 0x00);
+ EC_WRITE_U16(data + 4, 0x0000);
+ EC_WRITE_U16(data + 6, sdo->index); // SDO index
+ EC_WRITE_U8 (data + 8, fsm->subindex); // SDO subindex
+ EC_WRITE_U8 (data + 9, 0x00); // value info (no values)
+
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_entry_request;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT ENTRY REQUEST.
+*/
+
+void ec_fsm_coe_dict_entry_request(ec_fsm_coe_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE SDO entry request failed on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ fsm->cycles_start = datagram->cycles_sent;
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_entry_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT ENTRY CHECK.
+*/
+
+void ec_fsm_coe_dict_entry_check(ec_fsm_coe_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE mailbox check datagram failed on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ if (!ec_slave_mbox_check(datagram)) {
+ if (datagram->cycles_received
+ - fsm->cycles_start >= (cycles_t) 100 * cpu_khz) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Timeout while checking SDO entry on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ return;
+ }
+
+ // Fetch response
+ ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_entry_response;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT ENTRY RESPONSE.
+*/
+
+void ec_fsm_coe_dict_entry_response(ec_fsm_coe_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_t *sdo = fsm->sdo;
+ uint8_t *data, mbox_prot;
+ size_t rec_size, data_size;
+ ec_sdo_entry_t *entry;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE SDO description"
+ " response failed on slave %i.\n", slave->ring_position);
+ return;
+ }
+
+ if (!(data = ec_slave_mbox_fetch(slave, datagram,
+ &mbox_prot, &rec_size))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_ERR("Received mailbox protocol 0x%02X as response.\n", mbox_prot);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x8 && // SDO information
+ (EC_READ_U8 (data + 2) & 0x7F) == 0x07) { // error response
+ EC_ERR("SDO information error response at slave %i while"
+ " fetching SDO entry 0x%04X:%i!\n", slave->ring_position,
+ sdo->index, fsm->subindex);
+ ec_canopen_abort_msg(EC_READ_U32(data + 6));
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x8 || // SDO information
+ (EC_READ_U8(data + 2) & 0x7F) != 0x06 || // Entry desc. response
+ EC_READ_U16(data + 6) != sdo->index || // SDO index
+ EC_READ_U8(data + 8) != fsm->subindex) { // SDO subindex
+ EC_ERR("Invalid entry description response at slave %i while"
+ " fetching SDO entry 0x%04X:%i!\n", slave->ring_position,
+ sdo->index, fsm->subindex);
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (rec_size < 16) {
+ EC_ERR("Invalid data size!\n");
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ data_size = rec_size - 16;
+
+ if (!(entry = (ec_sdo_entry_t *)
+ kmalloc(sizeof(ec_sdo_entry_t), GFP_ATOMIC))) {
+ EC_ERR("Failed to allocate entry!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (ec_sdo_entry_init(entry, fsm->subindex, sdo)) {
+ EC_ERR("Failed to init entry!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ entry->data_type = EC_READ_U16(data + 10);
+ entry->bit_length = EC_READ_U16(data + 12);
+
+ if (data_size) {
+ uint8_t *desc;
+ if (!(desc = kmalloc(data_size + 1, GFP_ATOMIC))) {
+ EC_ERR("Failed to allocate SDO entry name!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+ memcpy(desc, data + 16, data_size);
+ desc[data_size] = 0;
+ entry->description = desc;
+ }
+
+ list_add_tail(&entry->list, &sdo->entries);
+
+ if (fsm->subindex < sdo->subindices) {
+ fsm->subindex++;
+
+ if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 10))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x8 << 12); // SDO information
+ EC_WRITE_U8 (data + 2, 0x05); // Get entry description request
+ EC_WRITE_U8 (data + 3, 0x00);
+ EC_WRITE_U16(data + 4, 0x0000);
+ EC_WRITE_U16(data + 6, sdo->index); // SDO index
+ EC_WRITE_U8 (data + 8, fsm->subindex); // SDO subindex
+ EC_WRITE_U8 (data + 9, 0x00); // value info (no values)
+
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_entry_request;
+ return;
+ }
+
+ // another SDO description to fetch?
+ if (fsm->sdo->list.next != &slave->sdo_dictionary) {
+ fsm->sdo = list_entry(fsm->sdo->list.next, ec_sdo_t, list);
+
+ if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 8))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x8 << 12); // SDO information
+ EC_WRITE_U8 (data + 2, 0x03); // Get object description request
+ EC_WRITE_U8 (data + 3, 0x00);
+ EC_WRITE_U16(data + 4, 0x0000);
+ EC_WRITE_U16(data + 6, fsm->sdo->index); // SDO index
+
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_dict_desc_request;
+ return;
+ }
+
+ fsm->state = ec_fsm_coe_end;
+}
+
+/******************************************************************************
+ * CoE state machine
+ *****************************************************************************/
+
+/**
+ CoE state: DOWN START.
+*/
+
+void ec_fsm_coe_down_start(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_data_t *sdodata = fsm->sdodata;
+ uint8_t *data;
+
+ if (fsm->slave->master->debug_level)
+ EC_DBG("Downloading SDO 0x%04X:%i to slave %i.\n",
+ sdodata->index, sdodata->subindex, slave->ring_position);
+
+ if (slave->sii_rx_mailbox_size < 6 + 10 + sdodata->size) {
+ EC_ERR("SDO fragmenting not supported yet!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03,
+ sdodata->size + 10))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x2 << 12); // SDO request
+ EC_WRITE_U8 (data + 2, (0x1 // size specified
+ | 0x1 << 5)); // Download request
+ EC_WRITE_U16(data + 3, sdodata->index);
+ EC_WRITE_U8 (data + 5, sdodata->subindex);
+ EC_WRITE_U32(data + 6, sdodata->size);
+ memcpy(data + 10, sdodata->data, sdodata->size);
+
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_down_request;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DOWN REQUEST.
+*/
+
+void ec_fsm_coe_down_request(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE download request failed.\n");
+ return;
+ }
+
+ fsm->cycles_start = datagram->cycles_sent;
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_down_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DOWN CHECK.
+*/
+
+void ec_fsm_coe_down_check(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE mailbox check datagram failed.\n");
+ return;
+ }
+
+ if (!ec_slave_mbox_check(datagram)) {
+ if (datagram->cycles_received
+ - fsm->cycles_start >= (cycles_t) 100 * cpu_khz) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Timeout while checking SDO configuration on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ return;
+ }
+
+ // Fetch response
+ ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_down_response;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DOWN RESPONSE.
+*/
+
+void ec_fsm_coe_down_response(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ uint8_t *data, mbox_prot;
+ size_t rec_size;
+ ec_sdo_data_t *sdodata = fsm->sdodata;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE download response failed.\n");
+ return;
+ }
+
+ if (!(data = ec_slave_mbox_fetch(slave, datagram,
+ &mbox_prot, &rec_size))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_ERR("Received mailbox protocol 0x%02X as response.\n", mbox_prot);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (rec_size < 6) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Received data is too small (%i bytes):\n", rec_size);
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
+ EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("SDO download 0x%04X:%X (%i bytes) aborted on slave %i.\n",
+ sdodata->index, sdodata->subindex, sdodata->size,
+ slave->ring_position);
+ if (rec_size < 10) {
+ EC_ERR("Incomplete Abort command:\n");
+ ec_print_data(data, rec_size);
+ }
+ else
+ ec_canopen_abort_msg(EC_READ_U32(data + 6));
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
+ EC_READ_U8 (data + 2) >> 5 != 0x3 || // Download response
+ EC_READ_U16(data + 3) != sdodata->index || // index
+ EC_READ_U8 (data + 5) != sdodata->subindex) { // subindex
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("SDO download 0x%04X:%X (%i bytes) failed:\n",
+ sdodata->index, sdodata->subindex, sdodata->size);
+ EC_ERR("Invalid SDO download response at slave %i!\n",
+ slave->ring_position);
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ fsm->state = ec_fsm_coe_end; // success
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP START.
+*/
+
+void ec_fsm_coe_up_start(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = slave->master;
+ ec_sdo_request_t *request = fsm->request;
+ ec_sdo_t *sdo = request->sdo;
+ ec_sdo_entry_t *entry = request->entry;
+ uint8_t *data;
+
+ if (master->debug_level)
+ EC_DBG("Uploading SDO 0x%04X:%i from slave %i.\n",
+ sdo->index, entry->subindex, slave->ring_position);
+
+ if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 10))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x2 << 12); // SDO request
+ EC_WRITE_U8 (data + 2, 0x2 << 5); // initiate upload request
+ EC_WRITE_U16(data + 3, sdo->index);
+ EC_WRITE_U8 (data + 5, entry->subindex);
+ memset(data + 6, 0x00, 4);
+
+ if (master->debug_level) {
+ EC_DBG("Upload request:\n");
+ ec_print_data(data, 10);
+ }
+
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_up_request;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP REQUEST.
+*/
+
+void ec_fsm_coe_up_request(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE upload request failed.\n");
+ return;
+ }
+
+ fsm->cycles_start = datagram->cycles_sent;
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_up_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP CHECK.
+*/
+
+void ec_fsm_coe_up_check(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE mailbox check datagram failed.\n");
+ return;
+ }
+
+ if (!ec_slave_mbox_check(datagram)) {
+ if (datagram->cycles_received
+ - fsm->cycles_start >= (cycles_t) 100 * cpu_khz) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Timeout while checking SDO upload on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ return;
+ }
+
+ // Fetch response
+ ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_up_response;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP RESPONSE.
+*/
+
+void ec_fsm_coe_up_response(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = slave->master;
+ uint8_t *data, mbox_prot;
+ size_t rec_size, data_size;
+ ec_sdo_request_t *request = fsm->request;
+ ec_sdo_t *sdo = request->sdo;
+ ec_sdo_entry_t *entry = request->entry;
+ uint32_t complete_size;
+ unsigned int expedited, size_specified;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE upload response failed.\n");
+ return;
+ }
+
+ if (!(data = ec_slave_mbox_fetch(slave, datagram,
+ &mbox_prot, &rec_size))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (master->debug_level) {
+ EC_DBG("Upload response:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_WARN("Received mailbox protocol 0x%02X as response.\n", mbox_prot);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (rec_size < 10) {
+ EC_ERR("Received currupted SDO upload response!\n");
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
+ EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
+ EC_ERR("SDO upload 0x%04X:%X aborted on slave %i.\n",
+ sdo->index, entry->subindex, slave->ring_position);
+ ec_canopen_abort_msg(EC_READ_U32(data + 6));
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
+ EC_READ_U8 (data + 2) >> 5 != 0x2 || // upload response
+ EC_READ_U16(data + 3) != sdo->index || // index
+ EC_READ_U8 (data + 5) != entry->subindex) { // subindex
+ EC_ERR("SDO upload 0x%04X:%X failed:\n", sdo->index, entry->subindex);
+ EC_ERR("Invalid SDO upload response at slave %i!\n",
+ slave->ring_position);
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ data_size = rec_size - 10;
+ expedited = EC_READ_U8(data + 2) & 0x02;
+
+ if (expedited) {
+ size_specified = EC_READ_U8(data + 2) & 0x01;
+ if (size_specified) {
+ complete_size = 4 - ((EC_READ_U8(data + 2) & 0x0C) >> 2);
+ }
+ else {
+ complete_size = 4;
+ }
+ }
+ else {
+ complete_size = EC_READ_U32(data + 6);
+ }
+
+ if (request->data) {
+ kfree(request->data);
+ request->data = NULL;
+ }
+ request->size = 0;
+
+ if (complete_size) {
+ if (!(request->data = (uint8_t *)
+ kmalloc(complete_size + 1, GFP_ATOMIC))) {
+ EC_ERR("Failed to allocate %i bytes of SDO data!\n",
+ complete_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+ request->data[complete_size] = 0x00; // just to be sure...
+ }
+
+ if (expedited) {
+ memcpy(request->data, data + 6, complete_size);
+ request->size = complete_size;
+ }
+ else {
+ memcpy(request->data, data + 10, data_size);
+ request->size = data_size;
+ fsm->toggle = 0;
+
+ if (data_size < complete_size) {
+ EC_WARN("SDO data incomplete (%i / %i).\n",
+ data_size, complete_size);
+
+ if (!(data = ec_slave_mbox_prepare_send(slave, datagram,
+ 0x03, 3))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x2 << 12); // SDO request
+ EC_WRITE_U8 (data + 2, (fsm->toggle << 4 // toggle
+ | 0x3 << 5)); // upload segment request
+
+ if (master->debug_level) {
+ EC_DBG("Upload segment request:\n");
+ ec_print_data(data, 3);
+ }
+
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_up_seg_request;
+ return;
+ }
+ }
+
+ fsm->state = ec_fsm_coe_end; // success
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP REQUEST.
+*/
+
+void ec_fsm_coe_up_seg_request(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE upload segment request failed.\n");
+ return;
+ }
+
+ fsm->cycles_start = datagram->cycles_sent;
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_up_seg_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP CHECK.
+*/
+
+void ec_fsm_coe_up_seg_check(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE mailbox check datagram failed.\n");
+ return;
+ }
+
+ if (!ec_slave_mbox_check(datagram)) {
+ if (datagram->cycles_received
+ - fsm->cycles_start >= (cycles_t) 100 * cpu_khz) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Timeout while checking SDO upload segment on slave %i.\n",
+ slave->ring_position);
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ return;
+ }
+
+ // Fetch response
+ ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_up_seg_response;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP RESPONSE.
+*/
+
+void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = slave->master;
+ uint8_t *data, mbox_prot;
+ size_t rec_size, data_size;
+ ec_sdo_request_t *request = fsm->request;
+ ec_sdo_t *sdo = request->sdo;
+ ec_sdo_entry_t *entry = request->entry;
+ uint32_t seg_size;
+ unsigned int last_segment;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_ERR("Reception of CoE upload segment response failed.\n");
+ return;
+ }
+
+ if (!(data = ec_slave_mbox_fetch(slave, datagram,
+ &mbox_prot, &rec_size))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (master->debug_level) {
+ EC_DBG("Upload segment response:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_ERR("Received mailbox protocol 0x%02X as response.\n", mbox_prot);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (rec_size < 10) {
+ EC_ERR("Received currupted SDO upload segment response!\n");
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
+ EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
+ EC_ERR("SDO upload 0x%04X:%X aborted on slave %i.\n",
+ sdo->index, entry->subindex, slave->ring_position);
+ ec_canopen_abort_msg(EC_READ_U32(data + 6));
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
+ EC_READ_U8 (data + 2) >> 5 != 0x0) { // upload segment response
+ EC_ERR("SDO upload 0x%04X:%X failed:\n", sdo->index, entry->subindex);
+ EC_ERR("Invalid SDO upload segment response at slave %i!\n",
+ slave->ring_position);
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ last_segment = EC_READ_U8(data + 2) & 0x01;
+ seg_size = (EC_READ_U8(data + 2) & 0xE) >> 1;
+ data_size = rec_size - 10;
+
+ if (data_size != seg_size) {
+ EC_WARN("SDO segment data invalid (%i / %i)"
+ " - Fragmenting not implemented.\n",
+ data_size, seg_size);
+ }
+
+ memcpy(request->data + request->size, data + 10, data_size);
+ request->size += data_size;
+
+ if (!last_segment) {
+ fsm->toggle = !fsm->toggle;
+
+ if (!(data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 3))) {
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x2 << 12); // SDO request
+ EC_WRITE_U8 (data + 2, (fsm->toggle << 4 // toggle
+ | 0x3 << 5)); // upload segment request
+
+ if (master->debug_level) {
+ EC_DBG("Upload segment request:\n");
+ ec_print_data(data, 3);
+ }
+
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_coe_up_seg_request;
+ return;
+ }
+
+ fsm->state = ec_fsm_coe_end; // success
+}
+
+/*****************************************************************************/
+
+/**
+ State: ERROR.
+*/
+
+void ec_fsm_coe_error(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ State: END.
+*/
+
+void ec_fsm_coe_end(ec_fsm_coe_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/fsm_coe.h Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,86 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT CoE state machines.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_COE__
+#define __EC_FSM_COE__
+
+#include "globals.h"
+#include "../include/ecrt.h"
+#include "datagram.h"
+#include "slave.h"
+#include "canopen.h"
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_coe ec_fsm_coe_t; /**< \see ec_fsm_coe */
+
+/**
+ Finite state machine of an EtherCAT master.
+*/
+
+struct ec_fsm_coe
+{
+ ec_slave_t *slave; /**< slave the FSM runs on */
+ ec_datagram_t *datagram; /**< datagram used in the state machine */
+
+ void (*state)(ec_fsm_coe_t *); /**< CoE state function */
+ ec_sdo_data_t *sdodata; /**< input/output: SDO data object */
+ cycles_t cycles_start; /**< CoE timestamp */
+ ec_sdo_t *sdo; /**< current SDO */
+ uint8_t subindex; /**< current subindex */
+ ec_sdo_request_t *request; /**< SDO request */
+ uint8_t toggle; /**< toggle bit for segment commands */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_coe_init(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_clear(ec_fsm_coe_t *);
+
+void ec_fsm_coe_dictionary(ec_fsm_coe_t *, ec_slave_t *);
+void ec_fsm_coe_download(ec_fsm_coe_t *, ec_slave_t *, ec_sdo_data_t *);
+void ec_fsm_coe_upload(ec_fsm_coe_t *, ec_slave_t *, ec_sdo_request_t *);
+
+int ec_fsm_coe_exec(ec_fsm_coe_t *);
+int ec_fsm_coe_success(ec_fsm_coe_t *);
+
+/*****************************************************************************/
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/fsm_sii.c Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,384 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT slave information interface FSM.
+*/
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "mailbox.h"
+#include "master.h"
+#include "fsm_sii.h"
+
+/*****************************************************************************/
+
+void ec_fsm_sii_start_reading(ec_fsm_sii_t *);
+void ec_fsm_sii_read_check(ec_fsm_sii_t *);
+void ec_fsm_sii_read_fetch(ec_fsm_sii_t *);
+void ec_fsm_sii_start_writing(ec_fsm_sii_t *);
+void ec_fsm_sii_write_check(ec_fsm_sii_t *);
+void ec_fsm_sii_write_check2(ec_fsm_sii_t *);
+void ec_fsm_sii_end(ec_fsm_sii_t *);
+void ec_fsm_sii_error(ec_fsm_sii_t *);
+
+/*****************************************************************************/
+
+/**
+ Constructor.
+*/
+
+void ec_fsm_sii_init(ec_fsm_sii_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< datagram structure to use */
+ )
+{
+ fsm->state = NULL;
+ fsm->datagram = datagram;
+}
+
+/*****************************************************************************/
+
+/**
+ Destructor.
+*/
+
+void ec_fsm_sii_clear(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ Initializes the SII read state machine.
+*/
+
+void ec_fsm_sii_read(ec_fsm_sii_t *fsm, /**< finite state machine */
+ ec_slave_t *slave, /**< slave to read from */
+ uint16_t offset, /**< offset to read from */
+ ec_fsm_sii_addressing_t mode /**< addressing scheme */
+ )
+{
+ fsm->state = ec_fsm_sii_start_reading;
+ fsm->slave = slave;
+ fsm->offset = offset;
+ fsm->mode = mode;
+}
+
+/*****************************************************************************/
+
+/**
+ Initializes the SII write state machine.
+*/
+
+void ec_fsm_sii_write(ec_fsm_sii_t *fsm, /**< finite state machine */
+ ec_slave_t *slave, /**< slave to read from */
+ uint16_t offset, /**< offset to read from */
+ uint16_t *value, /**< pointer to 2 bytes of data */
+ ec_fsm_sii_addressing_t mode /**< addressing scheme */
+ )
+{
+ fsm->state = ec_fsm_sii_start_writing;
+ fsm->slave = slave;
+ fsm->offset = offset;
+ fsm->mode = mode;
+ memcpy(fsm->value, value, 2);
+}
+
+/*****************************************************************************/
+
+/**
+ Executes the SII state machine.
+ \return false, if the state machine has terminated
+*/
+
+int ec_fsm_sii_exec(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+ fsm->state(fsm);
+
+ return fsm->state != ec_fsm_sii_end && fsm->state != ec_fsm_sii_error;
+}
+
+/*****************************************************************************/
+
+/**
+ Returns, if the master startup state machine terminated with success.
+ \return non-zero if successful.
+*/
+
+int ec_fsm_sii_success(ec_fsm_sii_t *fsm /**< Finite state machine */)
+{
+ return fsm->state == ec_fsm_sii_end;
+}
+
+/******************************************************************************
+ * SII state machine
+ *****************************************************************************/
+
+/**
+ SII state: START READING.
+ Starts reading the slave information interface.
+*/
+
+void ec_fsm_sii_start_reading(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ // initiate read operation
+ switch (fsm->mode) {
+ case EC_FSM_SII_POSITION:
+ ec_datagram_apwr(datagram, fsm->slave->ring_position, 0x502, 4);
+ break;
+ case EC_FSM_SII_NODE:
+ ec_datagram_npwr(datagram, fsm->slave->station_address, 0x502, 4);
+ break;
+ }
+
+ EC_WRITE_U8 (datagram->data, 0x00); // read-only access
+ EC_WRITE_U8 (datagram->data + 1, 0x01); // request read operation
+ EC_WRITE_U16(datagram->data + 2, fsm->offset);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_sii_read_check;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: READ CHECK.
+ Checks, if the SII-read-datagram has been sent and issues a fetch datagram.
+*/
+
+void ec_fsm_sii_read_check(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ EC_ERR("SII: Reception of read datagram failed.\n");
+ fsm->state = ec_fsm_sii_error;
+ return;
+ }
+
+ fsm->cycles_start = datagram->cycles_sent;
+ fsm->check_once_more = 1;
+
+ // issue check/fetch datagram
+ switch (fsm->mode) {
+ case EC_FSM_SII_POSITION:
+ ec_datagram_aprd(datagram, fsm->slave->ring_position, 0x502, 10);
+ break;
+ case EC_FSM_SII_NODE:
+ ec_datagram_nprd(datagram, fsm->slave->station_address, 0x502, 10);
+ break;
+ }
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_sii_read_fetch;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: READ FETCH.
+ Fetches the result of an SII-read datagram.
+*/
+
+void ec_fsm_sii_read_fetch(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ EC_ERR("SII: Reception of check/fetch datagram failed.\n");
+ fsm->state = ec_fsm_sii_error;
+ return;
+ }
+
+ // check "busy bit"
+ if (EC_READ_U8(datagram->data + 1) & 0x81) {
+ // still busy... timeout?
+ if (datagram->cycles_received
+ - fsm->cycles_start >= (cycles_t) 10 * cpu_khz) {
+ if (!fsm->check_once_more) {
+ EC_ERR("SII: Read timeout.\n");
+ fsm->state = ec_fsm_sii_error;
+#if 0
+ EC_DBG("SII busy: %02X %02X %02X %02X\n",
+ EC_READ_U8(datagram->data + 0),
+ EC_READ_U8(datagram->data + 1),
+ EC_READ_U8(datagram->data + 2),
+ EC_READ_U8(datagram->data + 3));
+#endif
+ return;
+ }
+ fsm->check_once_more = 0;
+ }
+
+ // issue check/fetch datagram again
+ switch (fsm->mode) {
+ case EC_FSM_SII_POSITION:
+ ec_datagram_aprd(datagram, fsm->slave->ring_position, 0x502, 10);
+ break;
+ case EC_FSM_SII_NODE:
+ ec_datagram_nprd(datagram, fsm->slave->station_address, 0x502, 10);
+ break;
+ }
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ return;
+ }
+
+#if 0
+ EC_DBG("SII rec: %02X %02X %02X %02X - %02X %02X %02X %02X\n",
+ EC_READ_U8(datagram->data + 0), EC_READ_U8(datagram->data + 1),
+ EC_READ_U8(datagram->data + 2), EC_READ_U8(datagram->data + 3),
+ EC_READ_U8(datagram->data + 6), EC_READ_U8(datagram->data + 7),
+ EC_READ_U8(datagram->data + 8), EC_READ_U8(datagram->data + 9));
+#endif
+
+ // SII value received.
+ memcpy(fsm->value, datagram->data + 6, 4);
+ fsm->state = ec_fsm_sii_end;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: START WRITING.
+ Starts reading the slave information interface.
+*/
+
+void ec_fsm_sii_start_writing(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ // initiate write operation
+ ec_datagram_npwr(datagram, fsm->slave->station_address, 0x502, 8);
+ EC_WRITE_U8 (datagram->data, 0x01); // enable write access
+ EC_WRITE_U8 (datagram->data + 1, 0x02); // request write operation
+ EC_WRITE_U32(datagram->data + 2, fsm->offset);
+ memcpy(datagram->data + 6, fsm->value, 2);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_sii_write_check;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: WRITE CHECK.
+*/
+
+void ec_fsm_sii_write_check(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ EC_ERR("SII: Reception of write datagram failed.\n");
+ fsm->state = ec_fsm_sii_error;
+ return;
+ }
+
+ fsm->cycles_start = datagram->cycles_sent;
+ fsm->check_once_more = 1;
+
+ // issue check/fetch datagram
+ ec_datagram_nprd(datagram, fsm->slave->station_address, 0x502, 2);
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ fsm->state = ec_fsm_sii_write_check2;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: WRITE CHECK 2.
+*/
+
+void ec_fsm_sii_write_check2(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED
+ || datagram->working_counter != 1) {
+ EC_ERR("SII: Reception of write check datagram failed.\n");
+ fsm->state = ec_fsm_sii_error;
+ return;
+ }
+
+ if (EC_READ_U8(datagram->data + 1) & 0x82) {
+ // still busy... timeout?
+ if (datagram->cycles_received
+ - fsm->cycles_start >= (cycles_t) 10 * cpu_khz) {
+ if (!fsm->check_once_more) {
+ EC_ERR("SII: Write timeout.\n");
+ fsm->state = ec_fsm_sii_error;
+ return;
+ }
+ fsm->check_once_more = 0;
+ }
+
+ // issue check/fetch datagram again
+ ec_master_queue_datagram(fsm->slave->master, datagram);
+ return;
+ }
+
+ if (EC_READ_U8(datagram->data + 1) & 0x40) {
+ EC_ERR("SII: Write operation failed!\n");
+ fsm->state = ec_fsm_sii_error;
+ return;
+ }
+
+ // success
+ fsm->state = ec_fsm_sii_end;
+}
+
+/*****************************************************************************/
+
+/**
+ State: ERROR.
+*/
+
+void ec_fsm_sii_error(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ State: END.
+*/
+
+void ec_fsm_sii_end(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/fsm_sii.h Tue Nov 07 12:13:30 2006 +0000
@@ -0,0 +1,94 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master 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.
+ *
+ * The IgH EtherCAT Master 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 the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The right to use EtherCAT Technology is granted and comes free of
+ * charge under condition of compatibility of product made by
+ * Licensee. People intending to distribute/sell products based on the
+ * code, have to sign an agreement to guarantee that products using
+ * software based on IgH EtherCAT master stay compatible with the actual
+ * EtherCAT specification (which are released themselves as an open
+ * standard) as the (only) precondition to have the right to use EtherCAT
+ * Technology, IP and trade marks.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT slave information interface FSM structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_SII__
+#define __EC_FSM_SII__
+
+#include "globals.h"
+#include "../include/ecrt.h"
+#include "datagram.h"
+#include "slave.h"
+
+/*****************************************************************************/
+
+typedef enum
+{
+ EC_FSM_SII_POSITION,
+ EC_FSM_SII_NODE
+}
+ec_fsm_sii_addressing_t;
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_sii ec_fsm_sii_t; /**< \see ec_fsm_sii */
+
+/**
+ Slave information interface FSM.
+*/
+
+struct ec_fsm_sii
+{
+ ec_slave_t *slave; /**< slave the FSM runs on */
+ ec_datagram_t *datagram; /**< datagram used in the state machine */
+
+ void (*state)(ec_fsm_sii_t *); /**< SII state function */
+ uint16_t offset; /**< input: offset in SII */
+ ec_fsm_sii_addressing_t mode; /**< reading via APRD or NPRD */
+ uint8_t value[4]; /**< raw SII value (32bit) */
+ cycles_t cycles_start; /**< start timestamp */
+ uint8_t check_once_more; /**< one more try after timeout */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_sii_init(ec_fsm_sii_t *, ec_datagram_t *);
+void ec_fsm_sii_clear(ec_fsm_sii_t *);
+
+void ec_fsm_sii_read(ec_fsm_sii_t *, ec_slave_t *,
+ uint16_t, ec_fsm_sii_addressing_t);
+void ec_fsm_sii_write(ec_fsm_sii_t *, ec_slave_t *, uint16_t, uint16_t *,
+ ec_fsm_sii_addressing_t);
+
+int ec_fsm_sii_exec(ec_fsm_sii_t *);
+int ec_fsm_sii_success(ec_fsm_sii_t *);
+
+/*****************************************************************************/
+
+#endif
--- a/master/globals.h Fri Oct 13 10:07:10 2006 +0000
+++ b/master/globals.h Tue Nov 07 12:13:30 2006 +0000
@@ -38,33 +38,17 @@
/*****************************************************************************/
-#ifndef _EC_GLOBALS_
-#define _EC_GLOBALS_
+#ifndef _EC_MASTER_GLOBALS_
+#define _EC_MASTER_GLOBALS_
#include <linux/types.h>
-#include "../config.h"
+#include "../globals.h"
/******************************************************************************
* EtherCAT master
*****************************************************************************/
-/** master main version */
-#define EC_MASTER_VERSION_MAIN 1
-
-/** master sub version (after the dot) */
-#define EC_MASTER_VERSION_SUB 1
-
-/** master extra version (just a string) */
-#define EC_MASTER_VERSION_EXTRA "stable"
-
-/** Compile version info. */
-
-#define EC_MASTER_VERSION EC_STR(EC_MASTER_VERSION_MAIN) \
- "." EC_STR(EC_MASTER_VERSION_SUB) \
- " " EC_MASTER_VERSION_EXTRA \
- " r" EC_STR(SVNREV)
-
/** maximum number of FMMUs per slave */
#define EC_MAX_FMMUS 16
@@ -77,6 +61,13 @@
/** datagram timeout in microseconds */
#define EC_IO_TIMEOUT 500
+/** Seconds to wait before fetching SDO dictionary
+ after slave entered PREOP state. */
+#define EC_WAIT_SDO_DICT 3
+
+/** minimum size of a buffer used with ec_state_string() */
+#define EC_STATE_STRING_SIZE 32
+
/******************************************************************************
* EtherCAT protocol
*****************************************************************************/
@@ -143,20 +134,6 @@
printk(KERN_DEBUG "EtherCAT DEBUG: " fmt, ##args)
/**
- Helper macro for EC_STR(), literates a macro argument.
- \param X argument to literate.
-*/
-
-#define EC_LIT(X) #X
-
-/**
- Converts a macro argument to a string.
- \param X argument to stringify.
-*/
-
-#define EC_STR(X) EC_LIT(X)
-
-/**
Convenience macro for defining read-only SysFS attributes.
This results in creating a static variable called attr_\a NAME. The SysFS
file will be world-readable.
@@ -182,6 +159,10 @@
/*****************************************************************************/
+extern char *ec_master_version_str;
+
+/*****************************************************************************/
+
void ec_print_data(const uint8_t *, size_t);
void ec_print_data_diff(const uint8_t *, const uint8_t *, size_t);
size_t ec_state_string(uint8_t, char *);
--- a/master/mailbox.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/mailbox.c Tue Nov 07 12:13:30 2006 +0000
@@ -165,9 +165,11 @@
{
size_t data_size;
- if ((data_size = EC_READ_U16(datagram->data)) >
- slave->sii_tx_mailbox_size - 6) {
+ data_size = EC_READ_U16(datagram->data);
+
+ if (data_size > slave->sii_tx_mailbox_size - 6) {
EC_ERR("Corrupt mailbox response detected!\n");
+ ec_print_data(datagram->data, slave->sii_tx_mailbox_size);
return NULL;
}
@@ -178,16 +180,20 @@
const ec_code_msg_t *mbox_msg;
uint16_t code = EC_READ_U16(datagram->data + 8);
- EC_ERR("Mailbox error response received.\n");
+ EC_ERR("Mailbox error response received - ");
+
for (mbox_msg = mbox_error_messages; mbox_msg->code; mbox_msg++) {
if (mbox_msg->code != code) continue;
- EC_ERR("Error reply code: 0x%04X: \"%s\".\n",
+ printk("Code 0x%04X: \"%s\".\n",
mbox_msg->code, mbox_msg->message);
break;
}
if (!mbox_msg->code)
- EC_ERR("Unknown error reply code 0x%04X.\n", code);
+ printk("Unknown error reply code 0x%04X.\n", code);
+
+ if (slave->master->debug_level)
+ ec_print_data(datagram->data + 6, data_size);
return NULL;
}
--- a/master/master.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/master.c Tue Nov 07 12:13:30 2006 +0000
@@ -54,9 +54,13 @@
/*****************************************************************************/
+void ec_master_clear(struct kobject *);
+void ec_master_destroy_domains(ec_master_t *);
void ec_master_sync_io(ec_master_t *);
void ec_master_idle_run(void *);
void ec_master_eoe_run(unsigned long);
+void ec_master_check_sdo(unsigned long);
+int ec_master_measure_bus_time(ec_master_t *);
ssize_t ec_show_master_attribute(struct kobject *, struct attribute *, char *);
ssize_t ec_store_master_attribute(struct kobject *, struct attribute *,
const char *, size_t);
@@ -106,27 +110,60 @@
EC_INFO("Initializing master %i.\n", index);
+ atomic_set(&master->available, 1);
master->index = index;
+
master->device = NULL;
init_MUTEX(&master->device_sem);
- atomic_set(&master->available, 1);
+
+ master->mode = EC_MASTER_MODE_ORPHANED;
+
INIT_LIST_HEAD(&master->slaves);
+ master->slave_count = 0;
+
INIT_LIST_HEAD(&master->datagram_queue);
+ master->datagram_index = 0;
+
INIT_LIST_HEAD(&master->domains);
- INIT_LIST_HEAD(&master->eoe_handlers);
+ master->debug_level = 0;
+
+ master->stats.timeouts = 0;
+ master->stats.corrupted = 0;
+ master->stats.skipped = 0;
+ master->stats.unmatched = 0;
+ master->stats.output_jiffies = 0;
+
INIT_WORK(&master->idle_work, ec_master_idle_run, (void *) master);
+
+ for (i = 0; i < HZ; i++) {
+ master->idle_cycle_times[i] = 0;
+ master->eoe_cycle_times[i] = 0;
+ }
+ master->idle_cycle_time_pos = 0;
+ master->eoe_cycle_time_pos = 0;
+
init_timer(&master->eoe_timer);
master->eoe_timer.function = ec_master_eoe_run;
master->eoe_timer.data = (unsigned long) master;
- master->internal_lock = SPIN_LOCK_UNLOCKED;
master->eoe_running = 0;
master->eoe_checked = 0;
- for (i = 0; i < HZ; i++) {
- master->idle_cycle_times[i] = 0;
- master->eoe_cycle_times[i] = 0;
- }
- master->idle_cycle_time_pos = 0;
- master->eoe_cycle_time_pos = 0;
+ INIT_LIST_HEAD(&master->eoe_handlers);
+
+ master->internal_lock = SPIN_LOCK_UNLOCKED;
+ master->request_cb = NULL;
+ master->release_cb = NULL;
+ master->cb_data = NULL;
+
+ master->eeprom_write_enable = 0;
+
+ master->sdo_request = NULL;
+ master->sdo_seq_user = 0;
+ master->sdo_seq_master = 0;
+ init_MUTEX(&master->sdo_sem);
+ init_timer(&master->sdo_timer);
+ master->sdo_timer.function = ec_master_check_sdo;
+ master->sdo_timer.data = (unsigned long) master;
+ init_completion(&master->sdo_complete);
// create workqueue
if (!(master->workqueue = create_singlethread_workqueue("EtherCAT"))) {
@@ -159,8 +196,12 @@
kobject_put(&master->kobj);
return -1;
}
-
- ec_master_reset(master);
+ if (kobject_add(&master->kobj)) {
+ EC_ERR("Failed to add master kobj.\n");
+ kobject_put(&master->kobj);
+ return -1;
+ }
+
return 0;
out_clear_eoe:
@@ -178,18 +219,40 @@
/**
Master destructor.
- Removes all pending datagrams, clears the slave list, clears all domains
- and frees the device.
+ Clears the kobj-hierarchy bottom up and frees the master.
+*/
+
+void ec_master_destroy(ec_master_t *master /**< EtherCAT master */)
+{
+ ec_master_destroy_slaves(master);
+ ec_master_destroy_domains(master);
+
+ // destroy self
+ kobject_del(&master->kobj);
+ kobject_put(&master->kobj); // free master
+}
+
+/*****************************************************************************/
+
+/**
+ Clear and free master.
+ This method is called by the kobject,
+ once there are no more references to it.
*/
void ec_master_clear(struct kobject *kobj /**< kobject of the master */)
{
ec_master_t *master = container_of(kobj, ec_master_t, kobj);
ec_eoe_t *eoe, *next_eoe;
-
- EC_INFO("Clearing master %i...\n", master->index);
-
- ec_master_reset(master);
+ ec_datagram_t *datagram, *next_datagram;
+
+ // dequeue all datagrams
+ list_for_each_entry_safe(datagram, next_datagram,
+ &master->datagram_queue, queue) {
+ datagram->state = EC_DATAGRAM_ERROR;
+ list_del_init(&datagram->queue);
+ }
+
ec_fsm_clear(&master->fsm);
destroy_workqueue(master->workqueue);
@@ -200,81 +263,193 @@
kfree(eoe);
}
- if (master->device) {
- ec_device_clear(master->device);
- kfree(master->device);
- }
-
- EC_INFO("Master %i cleared.\n", master->index);
-}
-
-/*****************************************************************************/
-
-/**
- Resets the master.
- Note: This function has to be called, everytime ec_master_release() is
- called, to free the slave list, domains etc.
-*/
-
-void ec_master_reset(ec_master_t *master /**< EtherCAT master */)
-{
- ec_datagram_t *datagram, *next_c;
+ EC_INFO("Master %i freed.\n", master->index);
+
+ kfree(master);
+}
+
+/*****************************************************************************/
+
+/**
+ Destroy all slaves.
+*/
+
+void ec_master_destroy_slaves(ec_master_t *master)
+{
+ ec_slave_t *slave, *next_slave;
+
+ list_for_each_entry_safe(slave, next_slave, &master->slaves, list) {
+ list_del(&slave->list);
+ ec_slave_destroy(slave);
+ }
+
+ master->slave_count = 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Destroy all domains.
+*/
+
+void ec_master_destroy_domains(ec_master_t *master)
+{
ec_domain_t *domain, *next_d;
- ec_master_eoe_stop(master);
- ec_master_idle_stop(master);
- ec_master_clear_slaves(master);
-
- // empty datagram queue
- list_for_each_entry_safe(datagram, next_c,
- &master->datagram_queue, queue) {
- datagram->state = EC_DATAGRAM_ERROR;
- list_del_init(&datagram->queue);
- }
-
- // clear domains
list_for_each_entry_safe(domain, next_d, &master->domains, list) {
list_del(&domain->list);
- kobject_del(&domain->kobj);
- kobject_put(&domain->kobj);
- }
-
- master->datagram_index = 0;
- master->debug_level = 0;
-
- master->stats.timeouts = 0;
- master->stats.corrupted = 0;
- master->stats.skipped = 0;
- master->stats.unmatched = 0;
- master->stats.output_jiffies = 0;
+ ec_domain_destroy(domain);
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ Flushes the SDO request queue.
+*/
+
+void ec_master_flush_sdo_requests(ec_master_t *master)
+{
+ del_timer_sync(&master->sdo_timer);
+ complete(&master->sdo_complete);
+ master->sdo_request = NULL;
+ master->sdo_seq_user = 0;
+ master->sdo_seq_master = 0;
+}
+
+/*****************************************************************************/
+
+/**
+*/
+
+int ec_master_enter_idle_mode(ec_master_t *master /**< EtherCAT master */)
+{
+ master->mode = EC_MASTER_MODE_IDLE;
+ queue_delayed_work(master->workqueue, &master->idle_work, 1);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+*/
+
+void ec_master_leave_idle_mode(ec_master_t *master /**< EtherCAT master */)
+{
+ ec_master_eoe_stop(master);
master->mode = EC_MASTER_MODE_ORPHANED;
+ while (!cancel_delayed_work(&master->idle_work)) {
+ flush_workqueue(master->workqueue);
+ }
+
+ ec_master_flush_sdo_requests(master);
+}
+
+/*****************************************************************************/
+
+/**
+*/
+
+int ec_master_enter_operation_mode(ec_master_t *master /**< EtherCAT master */)
+{
+ ec_slave_t *slave;
+ ec_datagram_t *datagram = &master->fsm.datagram;
+
+ master->mode = EC_MASTER_MODE_OPERATION;
+ while (!cancel_delayed_work(&master->idle_work)) {
+ flush_workqueue(master->workqueue);
+ }
+
+ // wait for FSM datagram
+ if (datagram->state == EC_DATAGRAM_SENT) {;
+ while (get_cycles() - datagram->cycles_sent
+ < (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000)) {}
+ ecrt_master_receive(master);
+ }
+
+ // finish running master FSM
+ if (ec_fsm_running(&master->fsm)) {
+ while (ec_fsm_exec(&master->fsm)) {
+ ec_master_sync_io(master);
+ }
+ }
+
+ if (master->debug_level) {
+ if (ec_master_measure_bus_time(master)) {
+ EC_ERR("Bus time measuring failed!\n");
+ goto out_idle;
+ }
+ }
+
+ // set initial slave states
+ list_for_each_entry(slave, &master->slaves, list) {
+ if (ec_slave_is_coupler(slave)) {
+ ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
+ }
+ else {
+ ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
+ }
+ }
+
+ return 0;
+
+ out_idle:
+ master->mode = EC_MASTER_MODE_IDLE;
+ queue_delayed_work(master->workqueue, &master->idle_work, 1);
+ return -1;
+}
+
+/*****************************************************************************/
+
+/**
+*/
+
+void ec_master_leave_operation_mode(ec_master_t *master
+ /**< EtherCAT master */)
+{
+ ec_slave_t *slave;
+ ec_fsm_t *fsm = &master->fsm;
+ ec_datagram_t *datagram = &master->fsm.datagram;
+
+ // wait for FSM datagram
+ if (datagram->state == EC_DATAGRAM_SENT) {
+ while (get_cycles() - datagram->cycles_sent
+ < (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000)) {}
+ ecrt_master_receive(master);
+ }
+
+ // finish running master FSM
+ if (ec_fsm_running(fsm)) {
+ while (ec_fsm_exec(fsm)) {
+ ec_master_sync_io(master);
+ }
+ }
+
+ // set states for all slaves
+ list_for_each_entry(slave, &master->slaves, list) {
+ ec_slave_reset(slave);
+ ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
+
+ fsm->slave = slave;
+ fsm->slave_state = ec_fsm_slaveconf_state_start;
+
+ do {
+ fsm->slave_state(fsm);
+ ec_master_sync_io(master);
+ }
+ while (fsm->slave_state != ec_fsm_slave_state_end
+ && fsm->slave_state != ec_fsm_slave_state_error);
+ }
+
+ ec_master_destroy_domains(master);
master->request_cb = NULL;
master->release_cb = NULL;
master->cb_data = NULL;
- master->eeprom_write_enable = 0;
-
- ec_fsm_reset(&master->fsm);
-}
-
-/*****************************************************************************/
-
-/**
- Clears all slaves.
-*/
-
-void ec_master_clear_slaves(ec_master_t *master)
-{
- ec_slave_t *slave, *next_slave;
-
- list_for_each_entry_safe(slave, next_slave, &master->slaves, list) {
- list_del(&slave->list);
- kobject_del(&slave->kobj);
- kobject_put(&slave->kobj);
- }
- master->slave_count = 0;
+ master->mode = EC_MASTER_MODE_IDLE;
+ queue_delayed_work(master->workqueue, &master->idle_work, 1);
}
/*****************************************************************************/
@@ -301,6 +476,7 @@
list_add_tail(&datagram->queue, &master->datagram_queue);
datagram->state = EC_DATAGRAM_QUEUED;
+ datagram->cycles_queued = get_cycles();
}
/*****************************************************************************/
@@ -312,15 +488,18 @@
void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
{
- ec_datagram_t *datagram;
+ ec_datagram_t *datagram, *next;
size_t datagram_size;
uint8_t *frame_data, *cur_data;
void *follows_word;
- cycles_t cycles_start, cycles_end;
+ cycles_t cycles_start, cycles_sent, cycles_end;
+ unsigned long jiffies_sent;
unsigned int frame_count, more_datagrams_waiting;
-
+ struct list_head sent_datagrams;
+
+ cycles_start = get_cycles();
frame_count = 0;
- cycles_start = get_cycles();
+ INIT_LIST_HEAD(&sent_datagrams);
if (unlikely(master->debug_level > 1))
EC_DBG("ec_master_send_datagrams\n");
@@ -344,8 +523,7 @@
break;
}
- datagram->state = EC_DATAGRAM_SENT;
- datagram->cycles_sent = cycles_start;
+ list_add_tail(&datagram->sent, &sent_datagrams);
datagram->index = master->datagram_index++;
if (unlikely(master->debug_level > 1))
@@ -373,7 +551,7 @@
cur_data += EC_DATAGRAM_FOOTER_SIZE;
}
- if (cur_data - frame_data == EC_FRAME_HEADER_SIZE) {
+ if (list_empty(&sent_datagrams)) {
if (unlikely(master->debug_level > 1))
EC_DBG("nothing to send.\n");
break;
@@ -392,6 +570,17 @@
// send frame
ec_device_send(master->device, cur_data - frame_data);
+ cycles_sent = get_cycles();
+ jiffies_sent = jiffies;
+
+ // set datagram states and sending timestamps
+ list_for_each_entry_safe(datagram, next, &sent_datagrams, sent) {
+ datagram->state = EC_DATAGRAM_SENT;
+ datagram->cycles_sent = cycles_sent;
+ datagram->jiffies_sent = jiffies_sent;
+ list_del_init(&datagram->sent); // empty list of sent datagrams
+ }
+
frame_count++;
}
while (more_datagrams_waiting);
@@ -487,6 +676,8 @@
// dequeue the received datagram
datagram->state = EC_DATAGRAM_RECEIVED;
+ datagram->cycles_received = master->device->cycles_isr;
+ datagram->jiffies_received = master->device->jiffies_isr;
list_del_init(&datagram->queue);
}
}
@@ -494,34 +685,6 @@
/*****************************************************************************/
/**
- Scans the EtherCAT bus for slaves.
- Creates a list of slave structures for further processing.
- \return 0 in case of success, else < 0
-*/
-
-int ec_master_bus_scan(ec_master_t *master /**< EtherCAT master */)
-{
- ec_fsm_t *fsm = &master->fsm;
-
- ec_fsm_startup(fsm); // init startup state machine
-
- do {
- ec_fsm_execute(fsm);
- ec_master_sync_io(master);
- }
- while (ec_fsm_startup_running(fsm));
-
- if (!ec_fsm_startup_success(fsm)) {
- ec_master_clear_slaves(master);
- return -1;
- }
-
- return 0;
-}
-
-/*****************************************************************************/
-
-/**
Output statistics in cyclic mode.
This function outputs statistical data on demand, but not more often than
necessary. The output happens at most once a second.
@@ -533,19 +696,23 @@
master->stats.output_jiffies = jiffies;
if (master->stats.timeouts) {
- EC_WARN("%i datagrams TIMED OUT!\n", master->stats.timeouts);
+ EC_WARN("%i datagram%s TIMED OUT!\n", master->stats.timeouts,
+ master->stats.timeouts == 1 ? "" : "s");
master->stats.timeouts = 0;
}
if (master->stats.corrupted) {
- EC_WARN("%i frame(s) CORRUPTED!\n", master->stats.corrupted);
+ EC_WARN("%i frame%s CORRUPTED!\n", master->stats.corrupted,
+ master->stats.corrupted == 1 ? "" : "s");
master->stats.corrupted = 0;
}
if (master->stats.skipped) {
- EC_WARN("%i datagram(s) SKIPPED!\n", master->stats.skipped);
+ EC_WARN("%i datagram%s SKIPPED!\n", master->stats.skipped,
+ master->stats.skipped == 1 ? "" : "s");
master->stats.skipped = 0;
}
if (master->stats.unmatched) {
- EC_WARN("%i datagram(s) UNMATCHED!\n", master->stats.unmatched);
+ EC_WARN("%i datagram%s UNMATCHED!\n", master->stats.unmatched,
+ master->stats.unmatched == 1 ? "" : "s");
master->stats.unmatched = 0;
}
}
@@ -554,52 +721,6 @@
/*****************************************************************************/
/**
- Starts the Idle mode.
-*/
-
-void ec_master_idle_start(ec_master_t *master /**< EtherCAT master */)
-{
- if (master->mode == EC_MASTER_MODE_IDLE) return;
-
- if (master->mode == EC_MASTER_MODE_OPERATION) {
- EC_ERR("ec_master_idle_start: Master already running!\n");
- return;
- }
-
- EC_INFO("Starting Idle mode.\n");
-
- master->mode = EC_MASTER_MODE_IDLE;
- ec_fsm_reset(&master->fsm);
- queue_delayed_work(master->workqueue, &master->idle_work, 1);
-}
-
-/*****************************************************************************/
-
-/**
- Stops the Idle mode.
-*/
-
-void ec_master_idle_stop(ec_master_t *master /**< EtherCAT master */)
-{
- if (master->mode != EC_MASTER_MODE_IDLE) return;
-
- ec_master_eoe_stop(master);
-
- EC_INFO("Stopping Idle mode.\n");
- master->mode = EC_MASTER_MODE_ORPHANED; // this is important for the
- // IDLE work function to not
- // queue itself again
-
- if (!cancel_delayed_work(&master->idle_work)) {
- flush_workqueue(master->workqueue);
- }
-
- ec_master_clear_slaves(master);
-}
-
-/*****************************************************************************/
-
-/**
Idle mode function.
*/
@@ -615,7 +736,7 @@
ecrt_master_receive(master);
// execute master state machine
- ec_fsm_execute(&master->fsm);
+ ec_fsm_exec(&master->fsm);
ecrt_master_send(master);
cycles_end = get_cycles();
@@ -649,11 +770,9 @@
sync_size = ec_slave_calc_sync_size(slave, sync);
if (slave->master->debug_level) {
- EC_INFO("Slave %i, sync manager %i:\n", slave->ring_position,
- sync->index);
- EC_INFO(" Address: 0x%04X\n", sync->physical_start_address);
- EC_INFO(" Size: %i\n", sync_size);
- EC_INFO(" Control: 0x%02X\n", sync->control_register);
+ EC_DBG("Slave %3i, SM %i: Addr 0x%04X, Size %3i, Ctrl 0x%02X, En %i\n",
+ slave->ring_position, sync->index, sync->physical_start_address,
+ sync_size, sync->control_register, sync->enable);
}
EC_WRITE_U16(data, sync->physical_start_address);
@@ -679,6 +798,14 @@
sync_size = ec_slave_calc_sync_size(slave, fmmu->sync);
+ if (slave->master->debug_level) {
+ EC_DBG("Slave %3i, FMMU %2i:"
+ " LogAddr 0x%08X, Size %3i, PhysAddr 0x%04X, Dir %s\n",
+ slave->ring_position, fmmu->index, fmmu->logical_start_address,
+ sync_size, fmmu->sync->physical_start_address,
+ ((fmmu->sync->control_register & 0x04) ? "out" : "in"));
+ }
+
EC_WRITE_U32(data, fmmu->logical_start_address);
EC_WRITE_U16(data + 4, sync_size); // size of fmmu
EC_WRITE_U8 (data + 6, 0x00); // logical start bit
@@ -706,7 +833,7 @@
ec_eoe_t *eoe;
uint32_t cur, sum, min, max, pos, i;
- off += sprintf(buffer + off, "\nVersion: " EC_MASTER_VERSION);
+ off += sprintf(buffer + off, "\nVersion: %s", ec_master_version_str);
off += sprintf(buffer + off, "\nMode: ");
switch (master->mode) {
case EC_MASTER_MODE_ORPHANED:
@@ -888,16 +1015,18 @@
coupled++;
EC_INFO("Coupling device %s to slave %i.\n",
eoe->dev->name, slave->ring_position);
- if (eoe->opened) slave->requested_state = EC_SLAVE_STATE_OP;
- else slave->requested_state = EC_SLAVE_STATE_INIT;
- slave->error_flag = 0;
+ if (eoe->opened)
+ ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
+ else
+ ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
break;
}
if (!found) {
- EC_WARN("No EoE handler for slave %i!\n", slave->ring_position);
- slave->requested_state = EC_SLAVE_STATE_INIT;
- slave->error_flag = 0;
+ if (master->debug_level)
+ EC_WARN("No EoE handler for slave %i!\n",
+ slave->ring_position);
+ ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
}
}
@@ -936,8 +1065,7 @@
// decouple all EoE handlers
list_for_each_entry(eoe, &master->eoe_handlers, list) {
if (eoe->slave) {
- eoe->slave->requested_state = EC_SLAVE_STATE_INIT;
- eoe->slave->error_flag = 0;
+ ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_PREOP);
eoe->slave = NULL;
}
}
@@ -946,6 +1074,7 @@
}
/*****************************************************************************/
+
/**
Does the Ethernet-over-EtherCAT processing.
*/
@@ -1009,6 +1138,25 @@
/*****************************************************************************/
/**
+*/
+
+void ec_master_check_sdo(unsigned long data /**< master pointer */)
+{
+ ec_master_t *master = (ec_master_t *) data;
+
+ if (master->sdo_seq_master != master->sdo_seq_user) {
+ master->sdo_timer.expires = jiffies + 10;
+ add_timer(&master->sdo_timer);
+ return;
+ }
+
+ // master has processed the request
+ complete(&master->sdo_complete);
+}
+
+/*****************************************************************************/
+
+/**
Calculates Advanced Position Adresses.
*/
@@ -1048,8 +1196,7 @@
int ec_master_measure_bus_time(ec_master_t *master)
{
ec_datagram_t datagram;
- cycles_t cycles_start, cycles_end, cycles_timeout;
- uint32_t times[100], sum, min, max, i;
+ uint32_t cur, sum, min, max, i;
ec_datagram_init(&datagram);
@@ -1059,7 +1206,7 @@
return -1;
}
- cycles_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000);
+ ecrt_master_receive(master);
sum = 0;
min = 0xFFFFFFFF;
@@ -1068,11 +1215,9 @@
for (i = 0; i < 100; i++) {
ec_master_queue_datagram(master, &datagram);
ecrt_master_send(master);
- cycles_start = get_cycles();
-
- while (1) { // active waiting
- ec_device_call_isr(master->device);
- cycles_end = get_cycles(); // take current time
+
+ while (1) {
+ ecrt_master_receive(master);
if (datagram.state == EC_DATAGRAM_RECEIVED) {
break;
@@ -1081,25 +1226,25 @@
EC_WARN("Failed to measure bus time.\n");
goto error;
}
- else if (cycles_end - cycles_start >= cycles_timeout) {
+ else if (datagram.state == EC_DATAGRAM_TIMED_OUT) {
EC_WARN("Timeout while measuring bus time.\n");
goto error;
}
}
- times[i] = (unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz;
- sum += times[i];
- if (times[i] > max) max = times[i];
- if (times[i] < min) min = times[i];
- }
-
- EC_INFO("Bus time is (min/avg/max) %u/%u.%u/%u us.\n",
- min, sum / 100, sum % 100, max);
+ cur = (unsigned int) (datagram.cycles_received
+ - datagram.cycles_sent) * 1000 / cpu_khz;
+ sum += cur;
+ if (cur > max) max = cur;
+ if (cur < min) min = cur;
+ }
+
+ EC_DBG("Bus time is (min/avg/max) %u / %u.%u / %u us.\n",
+ min, sum / 100, sum % 100, max);
+ ec_datagram_clear(&datagram);
return 0;
error:
- // Dequeue and free datagram
- list_del(&datagram.queue);
ec_datagram_clear(&datagram);
return -1;
}
@@ -1121,7 +1266,7 @@
if (!(domain = (ec_domain_t *) kmalloc(sizeof(ec_domain_t), GFP_KERNEL))) {
EC_ERR("Error allocating domain memory!\n");
- goto out_return;
+ return NULL;
}
if (list_empty(&master->domains)) index = 0;
@@ -1132,21 +1277,12 @@
if (ec_domain_init(domain, master, index)) {
EC_ERR("Failed to init domain.\n");
- goto out_return;
- }
-
- if (kobject_add(&domain->kobj)) {
- EC_ERR("Failed to add domain kobject.\n");
- goto out_put;
+ return NULL;
}
list_add_tail(&domain->list, &master->domains);
+
return domain;
-
- out_put:
- kobject_put(&domain->kobj);
- out_return:
- return NULL;
}
/*****************************************************************************/
@@ -1177,29 +1313,23 @@
domain_offset += domain->data_size;
}
- // determine initial states.
+ // configure all slaves
list_for_each_entry(slave, &master->slaves, list) {
- if (ec_slave_is_coupler(slave) || slave->registered) {
- slave->requested_state = EC_SLAVE_STATE_OP;
- }
- else {
- slave->requested_state = EC_SLAVE_STATE_PREOP;
- }
- }
-
- ec_fsm_configuration(fsm); // init configuration state machine
-
- do {
- ec_fsm_execute(fsm);
- ec_master_sync_io(master);
- }
- while (ec_fsm_configuration_running(fsm));
-
- if (!ec_fsm_configuration_success(fsm)) {
- return -1;
- }
-
- ec_fsm_reset(&master->fsm); // prepare for operation state machine
+ fsm->slave = slave;
+ fsm->slave_state = ec_fsm_slaveconf_state_start;
+
+ do {
+ fsm->slave_state(fsm);
+ ec_master_sync_io(master);
+ }
+ while (fsm->slave_state != ec_fsm_slave_state_end
+ && fsm->slave_state != ec_fsm_slave_state_error);
+
+ if (fsm->slave_state == ec_fsm_slave_state_error) {
+ EC_ERR("Failed to configure slave %i!\n", slave->ring_position);
+ return -1;
+ }
+ }
return 0;
}
@@ -1208,25 +1338,14 @@
/**
Resets all slaves to INIT state.
+ This method is deprecated and will disappear in the next version
+ of the realtime interface. The functionality is moved to
+ ecrt_master_release().
\ingroup RealtimeInterface
*/
void ecrt_master_deactivate(ec_master_t *master /**< EtherCAT master */)
{
- ec_fsm_t *fsm = &master->fsm;
- ec_slave_t *slave;
-
- list_for_each_entry(slave, &master->slaves, list) {
- slave->requested_state = EC_SLAVE_STATE_INIT;
- }
-
- ec_fsm_configuration(fsm); // init configuration state machine
-
- do {
- ec_fsm_execute(fsm);
- ec_master_sync_io(master);
- }
- while (ec_fsm_configuration_running(fsm));
}
/*****************************************************************************/
@@ -1294,19 +1413,28 @@
void ecrt_master_receive(ec_master_t *master /**< EtherCAT master */)
{
ec_datagram_t *datagram, *next;
- cycles_t cycles_received, cycles_timeout;
-
+ cycles_t cycles_timeout;
+
+ // receive datagrams
ec_device_call_isr(master->device);
- cycles_received = get_cycles();
- cycles_timeout = EC_IO_TIMEOUT * cpu_khz / 1000;
+ cycles_timeout = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000);
// dequeue all datagrams that timed out
list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) {
switch (datagram->state) {
+ case EC_DATAGRAM_QUEUED:
+ if (master->device->cycles_isr
+ - datagram->cycles_queued > cycles_timeout) {
+ list_del_init(&datagram->queue);
+ datagram->state = EC_DATAGRAM_TIMED_OUT;
+ master->stats.timeouts++;
+ ec_master_output_stats(master);
+ }
+ break;
case EC_DATAGRAM_SENT:
- case EC_DATAGRAM_QUEUED:
- if (cycles_received - datagram->cycles_sent > cycles_timeout) {
+ if (master->device->cycles_isr
+ - datagram->cycles_sent > cycles_timeout) {
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_TIMED_OUT;
master->stats.timeouts++;
@@ -1335,15 +1463,16 @@
// queue datagrams of all domains
list_for_each_entry(domain, &master->domains, list)
- ec_domain_queue(domain);
+ ec_domain_queue_datagrams(domain);
ecrt_master_send(master);
- cycles_start = get_cycles(); // take sending time
- cycles_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000);
+ cycles_start = get_cycles();
+ cycles_timeout = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000);
// active waiting
while (1) {
+ udelay(100);
cycles_end = get_cycles();
if (cycles_end - cycles_start >= cycles_timeout) break;
}
@@ -1361,8 +1490,8 @@
// output statistics
ec_master_output_stats(master);
- // execute master state machine
- ec_fsm_execute(&master->fsm);
+ // execute master state machine in a loop
+ ec_fsm_exec(&master->fsm);
}
/*****************************************************************************/
--- a/master/master.h Fri Oct 13 10:07:10 2006 +0000
+++ b/master/master.h Tue Nov 07 12:13:30 2006 +0000
@@ -134,39 +134,43 @@
void *cb_data; /**< data parameter of locking callbacks */
uint8_t eeprom_write_enable; /**< allow write operations to EEPROMs */
+
+ ec_sdo_request_t *sdo_request; /**< pointer to the current SDO request */
+ unsigned int sdo_seq_user; /**< sequence number for user space */
+ unsigned int sdo_seq_master; /**< sequence number for master */
+ struct semaphore sdo_sem; /**< SDO semaphore */
+ struct timer_list sdo_timer; /**< timer for polling sdo processing */
+ struct completion sdo_complete; /**< SDO request completion object */
};
/*****************************************************************************/
-// master creation and deletion
+// master creation/deletion
int ec_master_init(ec_master_t *, unsigned int, unsigned int);
-void ec_master_clear(struct kobject *);
-void ec_master_reset(ec_master_t *);
+void ec_master_destroy(ec_master_t *);
-// free run
-void ec_master_idle_start(ec_master_t *);
-void ec_master_idle_stop(ec_master_t *);
+// mode transitions
+int ec_master_enter_idle_mode(ec_master_t *);
+void ec_master_leave_idle_mode(ec_master_t *);
+int ec_master_enter_operation_mode(ec_master_t *);
+void ec_master_leave_operation_mode(ec_master_t *);
// EoE
void ec_master_eoe_start(ec_master_t *);
void ec_master_eoe_stop(ec_master_t *);
-// IO
+// datagram IO
void ec_master_receive_datagrams(ec_master_t *, const uint8_t *, size_t);
void ec_master_queue_datagram(ec_master_t *, ec_datagram_t *);
-// slave management
-int ec_master_bus_scan(ec_master_t *);
-
// misc.
void ec_master_output_stats(ec_master_t *);
-void ec_master_clear_slaves(ec_master_t *);
-int ec_master_measure_bus_time(ec_master_t *);
+void ec_master_destroy_slaves(ec_master_t *);
+void ec_master_calc_addressing(ec_master_t *);
-// other methods
+// helper functions
void ec_sync_config(const ec_sii_sync_t *, const ec_slave_t *, uint8_t *);
void ec_fmmu_config(const ec_fmmu_t *, const ec_slave_t *, uint8_t *);
-void ec_master_calc_addressing(ec_master_t *);
/*****************************************************************************/
--- a/master/module.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/module.c Tue Nov 07 12:13:30 2006 +0000
@@ -57,6 +57,8 @@
static int ec_eoeif_count = 0; /**< parameter value, number of EoE interf. */
static struct list_head ec_masters; /**< list of masters */
+char *ec_master_version_str = EC_MASTER_VERSION;
+
/*****************************************************************************/
/** \cond */
@@ -107,12 +109,6 @@
if (ec_master_init(master, i, ec_eoeif_count))
goto out_free;
- if (kobject_add(&master->kobj)) {
- EC_ERR("Failed to add kobj.\n");
- kobject_put(&master->kobj); // free master
- goto out_free;
- }
-
list_add_tail(&master->list, &ec_masters);
}
@@ -144,8 +140,7 @@
list_for_each_entry_safe(master, next, &ec_masters, list) {
list_del(&master->list);
- kobject_del(&master->kobj);
- kobject_put(&master->kobj); // free master
+ ec_master_destroy(master);
}
EC_INFO("Master driver cleaned up.\n");
@@ -225,7 +220,8 @@
*/
size_t ec_state_string(uint8_t states, /**< slave states */
- char *buffer /**< target buffer (min. 25 bytes) */
+ char *buffer /**< target buffer
+ (min. EC_STATE_STRING_SIZE bytes) */
)
{
off_t off = 0;
@@ -254,6 +250,10 @@
if (!first) off += sprintf(buffer + off, ", ");
off += sprintf(buffer + off, "OP");
}
+ if (states & EC_SLAVE_STATE_ACK_ERR) {
+ if (!first) off += sprintf(buffer + off, " + ");
+ off += sprintf(buffer + off, "ERROR");
+ }
return off;
}
@@ -369,7 +369,8 @@
return -1;
}
- ec_master_idle_start(master);
+ ec_master_enter_idle_mode(master);
+
return 0;
}
@@ -387,7 +388,7 @@
ec_master_t *master;
if (!(master = ec_find_master(master_index))) return;
- ec_master_idle_stop(master);
+ ec_master_leave_idle_mode(master);
if (ec_device_close(master->device))
EC_WARN("Failed to close device!\n");
@@ -443,31 +444,19 @@
goto out_module_put;
}
- ec_master_reset(master); // also stops idle mode
- master->mode = EC_MASTER_MODE_OPERATION;
-
- if (ec_master_measure_bus_time(master)) {
- EC_ERR("Bus time measuring failed!\n");
- goto out_reset;
- }
-
- if (ec_master_bus_scan(master)) {
- EC_ERR("Bus scan failed!\n");
- goto out_reset;
+ if (ec_master_enter_operation_mode(master)) {
+ EC_ERR("Failed to enter OPERATION mode!\n");
+ goto out_module_put;
}
EC_INFO("Successfully requested master %i.\n", master_index);
return master;
- out_reset:
- ec_master_reset(master);
- ec_master_idle_start(master);
out_module_put:
module_put(master->device->module);
out_release:
atomic_inc(&master->available);
out_return:
- EC_ERR("Failed to request master %i.\n", master_index);
return NULL;
}
@@ -480,20 +469,12 @@
void ecrt_release_master(ec_master_t *master /**< EtherCAT master */)
{
- EC_INFO("Releasing master %i...\n", master->index);
-
- if (atomic_read(&master->available)) {
- EC_ERR("Master %i was never requested!\n", master->index);
- return;
- }
-
- ec_master_reset(master);
- ec_master_idle_start(master);
+ ec_master_leave_operation_mode(master);
module_put(master->device->module);
atomic_inc(&master->available);
- EC_INFO("Successfully released master %i.\n", master->index);
+ EC_INFO("Released master %i.\n", master->index);
return;
}
--- a/master/slave.c Fri Oct 13 10:07:10 2006 +0000
+++ b/master/slave.c Tue Nov 07 12:13:30 2006 +0000
@@ -52,6 +52,8 @@
/*****************************************************************************/
+void ec_slave_clear(struct kobject *);
+void ec_slave_sdos_clear(struct kobject *);
ssize_t ec_show_slave_attribute(struct kobject *, struct attribute *, char *);
ssize_t ec_store_slave_attribute(struct kobject *, struct attribute *,
const char *, size_t);
@@ -82,6 +84,10 @@
.default_attrs = def_attrs
};
+static struct kobj_type ktype_ec_slave_sdos = {
+ .release = ec_slave_sdos_clear
+};
+
/** \endcond */
/*****************************************************************************/
@@ -102,25 +108,14 @@
slave->ring_position = ring_position;
slave->station_address = station_address;
- // init kobject and add it to the hierarchy
- memset(&slave->kobj, 0x00, sizeof(struct kobject));
- kobject_init(&slave->kobj);
- slave->kobj.ktype = &ktype_ec_slave;
- slave->kobj.parent = &master->kobj;
- if (kobject_set_name(&slave->kobj, "slave%03i", slave->ring_position)) {
- EC_ERR("Failed to set kobject name.\n");
- kobject_put(&slave->kobj);
- return -1;
- }
-
slave->master = master;
slave->requested_state = EC_SLAVE_STATE_UNKNOWN;
slave->current_state = EC_SLAVE_STATE_UNKNOWN;
+ slave->configured = 0;
slave->error_flag = 0;
slave->online = 1;
slave->fmmu_count = 0;
- slave->registered = 0;
slave->coupler_index = 0;
slave->coupler_subindex = 0xFFFF;
@@ -156,7 +151,9 @@
INIT_LIST_HEAD(&slave->sii_pdos);
INIT_LIST_HEAD(&slave->sdo_dictionary);
INIT_LIST_HEAD(&slave->sdo_confs);
- INIT_LIST_HEAD(&slave->varsize_fields);
+
+ slave->sdo_dictionary_fetched = 0;
+ slave->jiffies_preop = 0;
for (i = 0; i < 4; i++) {
slave->dl_link[i] = 0;
@@ -165,13 +162,76 @@
slave->sii_physical_layer[i] = 0xFF;
}
+ // init kobject and add it to the hierarchy
+ memset(&slave->kobj, 0x00, sizeof(struct kobject));
+ kobject_init(&slave->kobj);
+ slave->kobj.ktype = &ktype_ec_slave;
+ slave->kobj.parent = &master->kobj;
+ if (kobject_set_name(&slave->kobj, "slave%03i", slave->ring_position)) {
+ EC_ERR("Failed to set kobject name.\n");
+ goto out_slave_put;
+ }
+ if (kobject_add(&slave->kobj)) {
+ EC_ERR("Failed to add slave's kobject.\n");
+ goto out_slave_put;
+ }
+
+ // init SDO kobject and add it to the hierarchy
+ memset(&slave->sdo_kobj, 0x00, sizeof(struct kobject));
+ kobject_init(&slave->sdo_kobj);
+ slave->sdo_kobj.ktype = &ktype_ec_slave_sdos;
+ slave->sdo_kobj.parent = &slave->kobj;
+ if (kobject_set_name(&slave->sdo_kobj, "sdos")) {
+ EC_ERR("Failed to set kobject name.\n");
+ goto out_sdo_put;
+ }
+ if (kobject_add(&slave->sdo_kobj)) {
+ EC_ERR("Failed to add SDOs kobject.\n");
+ goto out_sdo_put;
+ }
+
return 0;
+
+ out_sdo_put:
+ kobject_put(&slave->sdo_kobj);
+ kobject_del(&slave->kobj);
+ out_slave_put:
+ kobject_put(&slave->kobj);
+ return -1;
}
/*****************************************************************************/
/**
Slave destructor.
+ Clears and frees a slave object.
+*/
+
+void ec_slave_destroy(ec_slave_t *slave /**< EtherCAT slave */)
+{
+ ec_sdo_t *sdo, *next_sdo;
+
+ // free all SDOs
+ list_for_each_entry_safe(sdo, next_sdo, &slave->sdo_dictionary, list) {
+ list_del(&sdo->list);
+ ec_sdo_destroy(sdo);
+ }
+
+ // free SDO kobject
+ kobject_del(&slave->sdo_kobj);
+ kobject_put(&slave->sdo_kobj);
+
+ // destroy self
+ kobject_del(&slave->kobj);
+ kobject_put(&slave->kobj);
+}
+
+/*****************************************************************************/
+
+/**
+ Clear and free slave.
+ This method is called by the kobject,
+ once there are no more references to it.
*/
void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */)
@@ -181,10 +241,7 @@
ec_sii_sync_t *sync, *next_sync;
ec_sii_pdo_t *pdo, *next_pdo;
ec_sii_pdo_entry_t *entry, *next_ent;
- ec_sdo_t *sdo, *next_sdo;
- ec_sdo_entry_t *en, *next_en;
ec_sdo_data_t *sdodata, *next_sdodata;
- ec_varsize_t *var, *next_var;
slave = container_of(kobj, ec_slave_t, kobj);
@@ -220,19 +277,6 @@
if (slave->sii_order) kfree(slave->sii_order);
if (slave->sii_name) kfree(slave->sii_name);
- // free all SDOs
- list_for_each_entry_safe(sdo, next_sdo, &slave->sdo_dictionary, list) {
- list_del(&sdo->list);
- if (sdo->name) kfree(sdo->name);
-
- // free all SDO entries
- list_for_each_entry_safe(en, next_en, &sdo->entries, list) {
- list_del(&en->list);
- kfree(en);
- }
- kfree(sdo);
- }
-
// free all SDO configurations
list_for_each_entry_safe(sdodata, next_sdodata, &slave->sdo_confs, list) {
list_del(&sdodata->list);
@@ -240,14 +284,59 @@
kfree(sdodata);
}
- // free information about variable sized data fields
- list_for_each_entry_safe(var, next_var, &slave->varsize_fields, list) {
- list_del(&var->list);
- kfree(var);
- }
-
if (slave->eeprom_data) kfree(slave->eeprom_data);
if (slave->new_eeprom_data) kfree(slave->new_eeprom_data);
+
+ kfree(slave);
+}
+
+/*****************************************************************************/
+
+/**
+*/
+
+void ec_slave_sdos_clear(struct kobject *kobj /**< kobject for SDOs */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ Reset slave from operation mode.
+*/
+
+void ec_slave_reset(ec_slave_t *slave /**< EtherCAT slave */)
+{
+ ec_sdo_data_t *sdodata, *next_sdodata;
+ ec_sii_sync_t *sync;
+
+ // remove FMMU configurations
+ slave->fmmu_count = 0;
+
+ // free all SDO configurations
+ list_for_each_entry_safe(sdodata, next_sdodata, &slave->sdo_confs, list) {
+ list_del(&sdodata->list);
+ kfree(sdodata->data);
+ kfree(sdodata);
+ }
+
+ // remove estimated sync manager sizes
+ list_for_each_entry(sync, &slave->sii_syncs, list) {
+ sync->est_length = 0;
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ */
+
+void ec_slave_request_state(ec_slave_t *slave, /**< ETherCAT slave */
+ ec_slave_state_t state /**< new state */
+ )
+{
+ slave->requested_state = state;
+ slave->error_flag = 0;
}
/*****************************************************************************/
@@ -341,6 +430,8 @@
sync->control_register = EC_READ_U8 (data + 4);
sync->enable = EC_READ_U8 (data + 6);
+ sync->est_length = 0;
+
list_add_tail(&sync->list, &slave->sii_syncs);
}
@@ -445,7 +536,9 @@
return 0;
}
- EC_WARN("String %i not found in slave %i.\n", index, slave->ring_position);
+ if (slave->master->debug_level)
+ EC_WARN("String %i not found in slave %i.\n",
+ index, slave->ring_position);
err_string = "(string not found)";
@@ -478,11 +571,14 @@
)
{
unsigned int i;
+ ec_fmmu_t *fmmu;
// FMMU configuration already prepared?
- for (i = 0; i < slave->fmmu_count; i++)
- if (slave->fmmus[i].domain == domain && slave->fmmus[i].sync == sync)
+ for (i = 0; i < slave->fmmu_count; i++) {
+ fmmu = &slave->fmmus[i];
+ if (fmmu->domain == domain && fmmu->sync == sync)
return 0;
+ }
// reserve new FMMU...
@@ -491,11 +587,14 @@
return -1;
}
- slave->fmmus[slave->fmmu_count].domain = domain;
- slave->fmmus[slave->fmmu_count].sync = sync;
- slave->fmmus[slave->fmmu_count].logical_start_address = 0;
+ fmmu = &slave->fmmus[slave->fmmu_count];
+
+ fmmu->index = slave->fmmu_count;
+ fmmu->domain = domain;
+ fmmu->sync = sync;
+ fmmu->logical_start_address = 0;
+
slave->fmmu_count++;
- slave->registered = 1;
return 0;
}
@@ -515,6 +614,8 @@
ec_sii_pdo_t *pdo;
ec_sii_pdo_entry_t *pdo_entry;
int first, i;
+ ec_sdo_data_t *sdodata;
+ char str[20];
off += sprintf(buffer + off, "\nName: ");
@@ -528,7 +629,10 @@
off += sprintf(buffer + off, "State: ");
off += ec_state_string(slave->current_state, buffer + off);
- off += sprintf(buffer + off, "\nRing position: %i\n",
+ off += sprintf(buffer + off, "\nFlags: %s, %s\n",
+ slave->online ? "online" : "OFFLINE",
+ slave->error_flag ? "ERROR" : "ok");
+ off += sprintf(buffer + off, "Ring position: %i\n",
slave->ring_position);
off += sprintf(buffer + off, "Advanced position: %i:%i\n",
slave->coupler_index, slave->coupler_subindex);
@@ -643,6 +747,20 @@
}
}
+ if (!list_empty(&slave->sdo_confs))
+ off += sprintf(buffer + off, "\nSDO configurations:\n");
+
+ list_for_each_entry(sdodata, &slave->sdo_confs, list) {
+ switch (sdodata->size) {
+ case 1: sprintf(str, "%i", EC_READ_U8(sdodata->data)); break;
+ case 2: sprintf(str, "%i", EC_READ_U16(sdodata->data)); break;
+ case 4: sprintf(str, "%i", EC_READ_U32(sdodata->data)); break;
+ default: sprintf(str, "(invalid size)"); break;
+ }
+ off += sprintf(buffer + off, " 0x%04X:%-3i -> %s\n",
+ sdodata->index, sdodata->subindex, str);
+ }
+
off += sprintf(buffer + off, "\n");
return off;
@@ -789,15 +907,15 @@
ec_slave_t *slave = container_of(kobj, ec_slave_t, kobj);
if (attr == &attr_state) {
- char state[25];
+ char state[EC_STATE_STRING_SIZE];
if (!strcmp(buffer, "INIT\n"))
- slave->requested_state = EC_SLAVE_STATE_INIT;
+ ec_slave_request_state(slave, EC_SLAVE_STATE_INIT);
else if (!strcmp(buffer, "PREOP\n"))
- slave->requested_state = EC_SLAVE_STATE_PREOP;
+ ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
else if (!strcmp(buffer, "SAVEOP\n"))
- slave->requested_state = EC_SLAVE_STATE_SAVEOP;
+ ec_slave_request_state(slave, EC_SLAVE_STATE_SAVEOP);
else if (!strcmp(buffer, "OP\n"))
- slave->requested_state = EC_SLAVE_STATE_OP;
+ ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
else {
EC_ERR("Invalid slave state \"%s\"!\n", buffer);
return -EINVAL;
@@ -806,7 +924,6 @@
ec_state_string(slave->requested_state, state);
EC_INFO("Accepted new state %s for slave %i.\n",
state, slave->ring_position);
- slave->error_flag = 0;
return size;
}
else if (attr == &attr_eeprom) {
@@ -832,9 +949,10 @@
{
ec_sii_pdo_t *pdo;
ec_sii_pdo_entry_t *pdo_entry;
- unsigned int bit_size;
+ unsigned int bit_size, byte_size;
if (sync->length) return sync->length;
+ if (sync->est_length) return sync->est_length;
bit_size = 0;
list_for_each_entry(pdo, &slave->sii_pdos, list) {
@@ -846,9 +964,11 @@
}
if (bit_size % 8) // round up to full bytes
- return bit_size / 8 + 1;
+ byte_size = bit_size / 8 + 1;
else
- return bit_size / 8;
+ byte_size = bit_size / 8;
+
+ return byte_size;
}
/*****************************************************************************/
@@ -873,7 +993,7 @@
int ec_slave_has_subbus(const ec_slave_t *slave /**< EtherCAT slave */)
{
return slave->sii_vendor_id == 0x00000002
- && slave->sii_product_code == 0x13ED3052;
+ && slave->sii_product_code == 0x04602c22;
}
/*****************************************************************************/
@@ -917,6 +1037,54 @@
return 0;
}
+/*****************************************************************************/
+
+/**
+ \return 0 in case of success, else < 0
+*/
+
+int ec_slave_validate(const ec_slave_t *slave, /**< EtherCAT slave */
+ uint32_t vendor_id, /**< vendor ID */
+ uint32_t product_code /**< product code */
+ )
+{
+ if (vendor_id != slave->sii_vendor_id ||
+ product_code != slave->sii_product_code) {
+ EC_ERR("Invalid slave type at position %i - Requested: 0x%08X 0x%08X,"
+ " found: 0x%08X 0x%08X\".\n", slave->ring_position, vendor_id,
+ product_code, slave->sii_vendor_id, slave->sii_product_code);
+ return -1;
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Counts the total number of SDOs and entries in the dictionary.
+*/
+
+void ec_slave_sdo_dict_info(const ec_slave_t *slave, /**< EtherCAT slave */
+ unsigned int *sdo_count, /**< number of SDOs */
+ unsigned int *entry_count /**< total number of
+ entries */
+ )
+{
+ unsigned int sdos = 0, entries = 0;
+ ec_sdo_t *sdo;
+ ec_sdo_entry_t *entry;
+
+ list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
+ sdos++;
+ list_for_each_entry(entry, &sdo->entries, list) {
+ entries++;
+ }
+ }
+
+ *sdo_count = sdos;
+ *entry_count = entries;
+}
+
/******************************************************************************
* Realtime interface
*****************************************************************************/
--- a/master/slave.h Fri Oct 13 10:07:10 2006 +0000
+++ b/master/slave.h Tue Nov 07 12:13:30 2006 +0000
@@ -67,8 +67,8 @@
/**< SAVEOP (mailbox communication and input update) */
EC_SLAVE_STATE_OP = 0x08,
/**< OP (mailbox communication and input/output update) */
- EC_ACK = 0x10
- /**< Acknoledge bit (no state) */
+ EC_SLAVE_STATE_ACK_ERR = 0x10
+ /**< Acknowledge/Error bit (no actual state) */
}
ec_slave_state_t;
@@ -116,6 +116,9 @@
uint16_t length; /**< data length in bytes */
uint8_t control_register; /**< control register value */
uint8_t enable; /**< enable bit */
+ uint16_t est_length; /**< Estimated length. This is no field of the SII,
+ but it is used to calculate the length via
+ PDO ranges */
}
ec_sii_sync_t;
@@ -168,55 +171,12 @@
/*****************************************************************************/
/**
- CANopen SDO.
-*/
-
-typedef struct
-{
- struct list_head list; /**< list item */
- uint16_t index; /**< SDO index */
- uint8_t object_code; /**< object code */
- char *name; /**< SDO name */
- struct list_head entries; /**< entry list */
-}
-ec_sdo_t;
-
-/*****************************************************************************/
-
-/**
- CANopen SDO entry.
-*/
-
-typedef struct
-{
- struct list_head list; /**< list item */
- uint8_t subindex; /**< entry subindex */
- uint16_t data_type; /**< entry data type */
- uint16_t bit_length; /**< entry length in bit */
- char *name; /**< entry name */
-}
-ec_sdo_entry_t;
-
-/*****************************************************************************/
-
-typedef struct
-{
- struct list_head list; /**< list item */
- uint16_t index; /**< SDO index */
- uint8_t subindex; /**< SDO subindex */
- uint8_t *data; /**< pointer to SDO data */
- size_t size; /**< size of SDO data */
-}
-ec_sdo_data_t;
-
-/*****************************************************************************/
-
-/**
FMMU configuration.
*/
typedef struct
{
+ unsigned int index; /**< FMMU index */
const ec_domain_t *domain; /**< domain */
const ec_sii_sync_t *sync; /**< sync manager */
uint32_t logical_start_address; /**< logical start address */
@@ -226,20 +186,6 @@
/*****************************************************************************/
/**
- Variable-sized field information.
-*/
-
-typedef struct
-{
- struct list_head list; /**< list item */
- const ec_sii_pdo_t *pdo; /**< PDO */
- size_t size; /**< field size */
-}
-ec_varsize_t;
-
-/*****************************************************************************/
-
-/**
EtherCAT slave.
*/
@@ -251,9 +197,9 @@
ec_slave_state_t requested_state; /**< requested slave state */
ec_slave_state_t current_state; /**< current slave state */
+ unsigned int configured; /**< the slave was configured by this master */
unsigned int error_flag; /**< stop processing after an error */
unsigned int online; /**< non-zero, if the slave responds. */
- uint8_t registered; /**< true, if slave has been registered */
// addresses
uint16_t ring_position; /**< ring position */
@@ -302,24 +248,25 @@
ec_fmmu_t fmmus[EC_MAX_FMMUS]; /**< FMMU configurations */
uint8_t fmmu_count; /**< number of FMMUs used */
- struct list_head sdo_dictionary; /**< SDO directory list */
+ struct kobject sdo_kobj; /**< kobject for SDOs */
+ struct list_head sdo_dictionary; /**< SDO dictionary list */
struct list_head sdo_confs; /**< list of SDO configurations */
-
- struct list_head varsize_fields; /**< size information for variable-sized
- data fields. */
+ uint8_t sdo_dictionary_fetched; /**< dictionary has been fetched */
+ unsigned long jiffies_preop; /**< time, the slave went to PREOP */
};
/*****************************************************************************/
// slave construction/destruction
int ec_slave_init(ec_slave_t *, ec_master_t *, uint16_t, uint16_t);
-void ec_slave_clear(struct kobject *);
+void ec_slave_destroy(ec_slave_t *);
+
+void ec_slave_reset(ec_slave_t *);
int ec_slave_prepare_fmmu(ec_slave_t *, const ec_domain_t *,
const ec_sii_sync_t *);
-// CoE
-//int ec_slave_fetch_sdo_list(ec_slave_t *);
+void ec_slave_request_state(ec_slave_t *, ec_slave_state_t);
// SII categories
int ec_slave_fetch_strings(ec_slave_t *, const uint8_t *);
@@ -336,6 +283,11 @@
int ec_slave_is_coupler(const ec_slave_t *);
int ec_slave_has_subbus(const ec_slave_t *);
+int ec_slave_validate(const ec_slave_t *, uint32_t, uint32_t);
+
+void ec_slave_sdo_dict_info(const ec_slave_t *,
+ unsigned int *, unsigned int *);
+
/*****************************************************************************/
#endif