# HG changeset patch # User Florian Pose # Date 1162901610 0 # Node ID 1cc865ba17c21d812efda03fbba3444a0682a9f5 # Parent 60b2aad9d40bbd4240524f14f1320e9b75120d39 MERGE trunk -r588:681 -> branches/stable-1.1 (Bugfixes, Ack behaviour, CoE via Sysfs, PDO ranges, 2.6.17 support, improved autotools, documentation) diff -r 60b2aad9d40b -r 1cc865ba17c2 AUTHORS --- /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 diff -r 60b2aad9d40b -r 1cc865ba17c2 COPYING --- /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. + + + Copyright (C) + + 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. + + , 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. diff -r 60b2aad9d40b -r 1cc865ba17c2 ChangeLog --- /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 + diff -r 60b2aad9d40b -r 1cc865ba17c2 Doxyfile --- 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. diff -r 60b2aad9d40b -r 1cc865ba17c2 INSTALL --- 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. ------------------------------------------------------------------------------- diff -r 60b2aad9d40b -r 1cc865ba17c2 Kbuild --- /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/ + +#------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 LICENSE --- 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. - - - Copyright (C) - - 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. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff -r 60b2aad9d40b -r 1cc865ba17c2 Makefile.am --- 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 diff -r 60b2aad9d40b -r 1cc865ba17c2 NEWS --- /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. + +------------------------------------------------------------------------------- diff -r 60b2aad9d40b -r 1cc865ba17c2 TODO --- 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 ------------------------------------------------------------------------------- diff -r 60b2aad9d40b -r 1cc865ba17c2 bootstrap --- 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 diff -r 60b2aad9d40b -r 1cc865ba17c2 configure.ac --- 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=], - [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=], + [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=], + [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=], + [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=], + [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 < + 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: + + ---------- + + 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 + . + + ---------- + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 "); +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); diff -r 60b2aad9d40b -r 1cc865ba17c2 devices/8139too-2.6.13-orig.c --- /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 + 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: + + ---------- + + 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 + . + + ---------- + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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); diff -r 60b2aad9d40b -r 1cc865ba17c2 devices/8139too-2.6.17-ethercat.c --- /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 + 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: + + ---------- + + 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 + . + + ---------- + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 "); +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); diff -r 60b2aad9d40b -r 1cc865ba17c2 devices/8139too-2.6.17-orig.c --- /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 + 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: + + ---------- + + 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 + . + + ---------- + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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); diff -r 60b2aad9d40b -r 1cc865ba17c2 devices/8139too.c --- 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 - 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: - - ---------- - - 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 - . - - ---------- - - 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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 "); -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); diff -r 60b2aad9d40b -r 1cc865ba17c2 devices/Kbuild --- 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) #------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 devices/Makefile.am --- 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 - #------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 devices/original_8139too.c --- 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 - 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: - - ---------- - - 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 - . - - ---------- - - 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "); -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); diff -r 60b2aad9d40b -r 1cc865ba17c2 documentation/ethercat_doc.tex --- 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 diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/mini/Kbuild --- 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 diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/mini/Makefile.am --- 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) #------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/mini/mini.c --- 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"); } diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/msr/Kbuild --- 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 #------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/msr/Makefile.am --- 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) #------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/msr/init.sh --- /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 + +#------------------------------------------------------------------------------ + +# + +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 + +# + +#------------------------------------------------------------------------------ + +. /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 diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/msr/msr_load --- 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} - - - - - - - - - diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/msr/msr_param.h --- 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 - -/*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/msr/msr_sample.c --- 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 #include #include -#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(); diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/msr/msr_unload --- 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 diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/rtai/Kbuild --- 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 + #------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/rtai/Makefile.am --- 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) #------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 examples/rtai/rtai_sample.c --- 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); diff -r 60b2aad9d40b -r 1cc865ba17c2 globals.h --- /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 diff -r 60b2aad9d40b -r 1cc865ba17c2 include/ecdb.h --- 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 diff -r 60b2aad9d40b -r 1cc865ba17c2 include/ecrt.h --- 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 diff -r 60b2aad9d40b -r 1cc865ba17c2 master/Kbuild --- 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) #------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/Makefile.am --- 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 - #------------------------------------------------------------------------------ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/canopen.c --- /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 + +#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); +} + +/*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/canopen.h --- /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 +#include + +#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 diff -r 60b2aad9d40b -r 1cc865ba17c2 master/datagram.c --- 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; } /*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/datagram.h --- 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; diff -r 60b2aad9d40b -r 1cc865ba17c2 master/debug.c --- 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; } diff -r 60b2aad9d40b -r 1cc865ba17c2 master/device.c --- 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); } diff -r 60b2aad9d40b -r 1cc865ba17c2 master/device.h --- 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 diff -r 60b2aad9d40b -r 1cc865ba17c2 master/domain.c --- 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); diff -r 60b2aad9d40b -r 1cc865ba17c2 master/domain.h --- 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 *); /*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/ethernet.c --- 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; } diff -r 60b2aad9d40b -r 1cc865ba17c2 master/fsm.c --- 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 */) +{ +} + +/*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/fsm.h --- 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 *); /*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/fsm_change.c --- /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 */) +{ +} + +/*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/fsm_change.h --- /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 diff -r 60b2aad9d40b -r 1cc865ba17c2 master/fsm_coe.c --- /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 */) +{ +} + +/*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/fsm_coe.h --- /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 diff -r 60b2aad9d40b -r 1cc865ba17c2 master/fsm_sii.c --- /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 */) +{ +} + +/*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/fsm_sii.h --- /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 diff -r 60b2aad9d40b -r 1cc865ba17c2 master/globals.h --- 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 -#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 *); diff -r 60b2aad9d40b -r 1cc865ba17c2 master/mailbox.c --- 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; } diff -r 60b2aad9d40b -r 1cc865ba17c2 master/master.c --- 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); } /*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/master.h --- 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 *); /*****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/module.c --- 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; } diff -r 60b2aad9d40b -r 1cc865ba17c2 master/slave.c --- 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 *****************************************************************************/ diff -r 60b2aad9d40b -r 1cc865ba17c2 master/slave.h --- 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