MERGE trunk -r588:681 -> branches/stable-1.1 (Bugfixes, Ack behaviour, CoE via Sysfs, PDO ranges, 2.6.17 support, improved autotools, documentation) stable-1.1
authorFlorian Pose <fp@igh-essen.com>
Tue, 07 Nov 2006 12:13:30 +0000
branchstable-1.1
changeset 1732 1cc865ba17c2
parent 1731 60b2aad9d40b
child 1733 69f0fbc0dcab
MERGE trunk -r588:681 -> branches/stable-1.1 (Bugfixes, Ack behaviour, CoE via Sysfs, PDO ranges, 2.6.17 support, improved autotools, documentation)
AUTHORS
COPYING
ChangeLog
Doxyfile
INSTALL
Kbuild
LICENSE
Makefile.am
NEWS
TODO
bootstrap
configure.ac
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
documentation/ethercat_doc.tex
examples/mini/Kbuild
examples/mini/Makefile.am
examples/mini/mini.c
examples/msr/Kbuild
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
examples/rtai/Kbuild
examples/rtai/Makefile.am
examples/rtai/rtai_sample.c
globals.h
include/ecdb.h
include/ecrt.h
master/Kbuild
master/Makefile.am
master/canopen.c
master/canopen.h
master/datagram.c
master/datagram.h
master/debug.c
master/device.c
master/device.h
master/domain.c
master/domain.h
master/ethernet.c
master/fsm.c
master/fsm.h
master/fsm_change.c
master/fsm_change.h
master/fsm_coe.c
master/fsm_coe.h
master/fsm_sii.c
master/fsm_sii.h
master/globals.h
master/mailbox.c
master/master.c
master/master.h
master/module.c
master/slave.c
master/slave.h
--- /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