# HG changeset patch # User Florian Pose # Date 1171374157 0 # Node ID 5fcbd29151d212ca5dc60d86820e41b3e7ebdae4 # Parent bc89e3fba1a5fea19417c51ecc61e55b3bf61c6f MERGE -r688:759 trunk -> branches/stable-1.2 (release 1.2, part one). diff -r bc89e3fba1a5 -r 5fcbd29151d2 AUTHORS --- a/AUTHORS Tue Feb 13 13:36:31 2007 +0000 +++ b/AUTHORS Tue Feb 13 13:42:37 2007 +0000 @@ -1,1 +1,5 @@ -Florian Pose + +$Id$ + +Authors: +Dipl.-Ing. (FH) Florian Pose diff -r bc89e3fba1a5 -r 5fcbd29151d2 ChangeLog --- a/ChangeLog Tue Feb 13 13:36:31 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2582 +0,0 @@ -2006-11-07 13:39 fp - - * configure.ac: VERSION 1.1.1 - -2006-11-07 13:38 fp - - * ., include/ecrt.h, master/fsm.h, master/xmldev.c: Updated source - code documentation. - -2006-11-07 13:30 fp - - * Makefile.am: Fixed dist files. - -2006-11-07 12:10 fp - - * Doxyfile, INSTALL, documentation/ethercat_doc.tex: Updated - documentation. - -2006-11-06 16:33 fp - - * Makefile.am: Improved clean targets. - -2006-11-06 16:27 fp - - * ., Kbuild, Makefile.am, configure.ac, devices/Kbuild, - devices/Makefile.am, examples/mini/Kbuild, examples/msr/Kbuild, - examples/rtai/Kbuild, examples/rtai/Makefile.am, master/Kbuild, - master/Makefile.am: Improved autotools, added config.kbuild, - --with-msr-dir, --with-rtai-dir, --with-8139too-kernel. - -2006-11-06 14:22 fp - - * TODO, master/canopen.c, master/domain.c, master/fsm.c, - master/fsm_coe.c, master/master.c, master/module.c, - master/slave.c: kobject_add() in constructors. - -2006-11-06 14:21 fp - - * master/master.c: Minor change in master.c. - -2006-11-06 14:19 fp - - * master/slave.c: Added clear function for SDO kobject. - -2006-11-06 14:15 fp - - * master/master.c: Fixed workqueue cancelling. - -2006-11-06 09:51 fp - - * Makefile.am: Added mrproper make target. - -2006-11-06 09:43 fp - - * AUTHORS, COPYING, ChangeLog, LICENSE, NEWS, bootstrap, - configure.ac: Removed --foreign switch for autotools. - -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 bc89e3fba1a5 -r 5fcbd29151d2 FEATURES --- a/FEATURES Tue Feb 13 13:36:31 2007 +0000 +++ b/FEATURES Tue Feb 13 13:42:37 2007 +0000 @@ -21,10 +21,10 @@ * Supports any realtime extension through independent architecture. - RTAI, IPIPE, ADEOS, etc. - - Runs well even without realtime extensions. + - Runs well even without realtime extension. -* Common kernel interface, for realtime modules using EtherCAT functionality. - - Synchronous and asynchronous sending and receiving of frames. +* Common kernel interface for realtime modules using EtherCAT functionality. + - Synchronous transmission and reception of EtherCAT frames. - Avoidance of unnecessary copy operations for process data. * Separating slave groups through domains. @@ -38,29 +38,30 @@ operation. - Controlling of slave states during realtime operation. -* Special Idle mode, when master is not in use. +* Special IDLE mode, when master is not in use. - Automatic scanning of slaves upon topology changes. - Bus visualisation and EoE processing without realtime process connected. -* Implements the CANopen-over-EtherCAT (CoE) protocol. +* Implementation of the CANopen-over-EtherCAT (CoE) protocol. - Configuration of CoE-capable slaves via SDO interface. - SDO dictionary listing. -* Implements the Ethernet-over-EtherCAT (EoE) protocol. +* Implementation of the Ethernet-over-EtherCAT (EoE) protocol. - Creates virtual network devices that are automatically coupled to EoE-capable slaves. - Thus natively supports either a switched or a routed EoE network - architecture. + architecture with standard GNU/Linux tools. -* User space interface via the System Filesystem (SysFS). - - User space tool for bus visualisation. - - Slave E²PROM image reading and writing. +* User space interface via the system filesystem (Sysfs). + - Detailed information about master and slaves in a tree structure. + - User space tool 'lsec' for quick bus visualisation. + - Slave EEPROM image reading and writing. -* Seamless integration in your favourite Linux distibution. +* Seamless integration in your favourite GNU/Linux distibution. - Master and network device configuration via sysconfig files. - - UnitedLinux compatible init script for master control. + - "Linux standard base"-compatible init script for master control. * Virtual read-only network interface for debugging purposes and for - "sniffing" the EtherCAT traffic (through Ethereal, or others). + monitoring the EtherCAT traffic (through Wireshark, or others). ------------------------------------------------------------------------------- diff -r bc89e3fba1a5 -r 5fcbd29151d2 INSTALL --- a/INSTALL Tue Feb 13 13:36:31 2007 +0000 +++ b/INSTALL Tue Feb 13 13:42:37 2007 +0000 @@ -10,9 +10,7 @@ ======================= The building and installation procedure is described in section 2.1 in the -EtherCAT master documentation: - -documentation/ethercat-doc.pdf +EtherCAT master documentation available from http://etherlab.org/ethercat. ------------------------------------------------------------------------------- @@ -25,8 +23,8 @@ (and as root) +# make install # 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 bc89e3fba1a5 -r 5fcbd29151d2 Makefile.am --- a/Makefile.am Tue Feb 13 13:36:31 2007 +0000 +++ b/Makefile.am Tue Feb 13 13:42:37 2007 +0000 @@ -36,7 +36,6 @@ DIST_SUBDIRS = master devices script include examples EXTRA_DIST = \ - documentation/ethercat_doc.pdf \ Doxyfile \ FEATURES \ globals.h \ @@ -54,6 +53,7 @@ @rm -f Modules.symvers mydist: + svn2cl $(srcdir) @SVNREV=`svnversion $(srcdir)` && \ $(MAKE) dist-bzip2 \ distdir=$(PACKAGE)-$(VERSION)-$(BRANCH)-r$${SVNREV} diff -r bc89e3fba1a5 -r 5fcbd29151d2 NEWS --- a/NEWS Tue Feb 13 13:36:31 2007 +0000 +++ b/NEWS Tue Feb 13 13:42:37 2007 +0000 @@ -4,6 +4,36 @@ ------------------------------------------------------------------------------- +Changes in version 1.2.0: + +* Realtime interface changes: + - Re-introduced ecrt_domain_queue() to allow datagram queuing apart + from datagram processing. The queuing of a domain's datagrams is not + done in ecrt_domain_process() any more! + - Removed ecrt_master_deactivate(). Its functionality was moved into + ecrt_master_release(). + - Removed ecrt_master_prepare(). Its functionality was moved into + ecrt_master_activate(). + - Renamed ecdev_start() and ecdev_stop() to ecdev_open() and ecdev_close(). + These two functions now take a pointer to ec_device_t as their arguments. + - The data_ptr parameter of ecrt_domain_register_pdo(), + ecrt_domain_register_pdo_list() and ecrt_domain_register_pdo_range() may + not be NULL any more. + - Removed ecrt_slave_pdo_size(). This function was deprecated long before. + - Introduced ECRT_VERSION_MAGIC macro and ecrt_version_magic() function. +* Device interface changes: + - ec_isr_t was replaced by ec_pollfunc_t, the device driver has to supply + a poll function to call its ISR. This was introduced because some network + drivers' interrupt functions have different arguments. +* State machines now try to re-send datagrams on datagram timeout. +* New option -c of the lsec script, displays slave current consumptions and + remaining current. +* Added frame counter in master info file. + +------------------------------------------------------------------------------- + +Changes in version 1.1.1: + * State change FSM: Clearing of sync managers before PREOP. * Added modules_install make target. * Device modules for kernel 2.6.17. diff -r bc89e3fba1a5 -r 5fcbd29151d2 README --- a/README Tue Feb 13 13:36:31 2007 +0000 +++ b/README Tue Feb 13 13:42:37 2007 +0000 @@ -19,12 +19,11 @@ This is an Open-Source EtherCAT master implementation for Linux 2.6. See the FEATURES file for a list of features. For more information, see - -http://etherlab.org +http://etherlab.org/ethercat. or contact -Florian Pose +Dipl.-Ing. (FH) Florian Pose Ingenieurgemeinschaft IgH Heinz-Baecker-Str. 34 D-45356 Essen diff -r bc89e3fba1a5 -r 5fcbd29151d2 TODO --- a/TODO Tue Feb 13 13:36:31 2007 +0000 +++ b/TODO Tue Feb 13 13:42:37 2007 +0000 @@ -6,21 +6,37 @@ ------------------------------------------------------------------------------- -Important things to do: +* Release 1.3: + - Remove ecrt_master_run(). Make master FSM run in process context instead. + - Separate slave objects from configurations. Make bus dynamic in OPERATION + mode. + - New sysfs structure with slaves, configurations and links. + - Remove addressing scheme "X:Y". + - Tool to set aliases. + - Remove ugly ec_slave_is_coupler() and ec_slave_has_subbus(). + - Add statistics object. + - SDO dictionary and -access in operation mode. + - SDO write access in sysfs. + - Speed up IDLE-FSM through fast mode with schedule(). -* Read AL Status Code with AL Control Response (if possible?) -* Coupling of EoE handlers before configuring slaves. - (avoiding duplicate configuration) -* Implement all EtherCAT commands -* Determine number of frames the NIC can handle -* Implement e100 driver +* Future features: + - e100 driver. + - Distributed clocks. + - Read dynamic PDO mapping from SDO dictionary (see can-cia.org: cia301ds4). + - Redundancy with 2 network adapters. + - Interface for alternative PDO mapping. + +* Smaller changes: + - Simplify FSMs with _enter() functions. + - Rename "sysconfig" file to "sysconfig/ethercat". + - Dynamic creation of EoE handlers. + - Output intermediate results during lsec. + - State change FSM: Follow spontaneous state changes. + +* Less important changes: + - Implement all EtherCAT datagram types. + - File access over EtherCAT (FoE). + - Allow VLAN tagging. + - Determine number of frames the NIC can handle. ------------------------------------------------------------------------------- - -Not-so-important things to do: - -* Calculate bus topology -* File access over EtherCAT (FoE) -* Allow VLAN tagging - -------------------------------------------------------------------------------- diff -r bc89e3fba1a5 -r 5fcbd29151d2 bootstrap --- a/bootstrap Tue Feb 13 13:36:31 2007 +0000 +++ b/bootstrap Tue Feb 13 13:42:37 2007 +0000 @@ -1,8 +1,11 @@ #!/bin/bash +# $Id$ + set -x mkdir -p autoconf aclocal -I autoconf autoheader +touch ChangeLog automake --add-missing --copy autoconf diff -r bc89e3fba1a5 -r 5fcbd29151d2 configure.ac --- a/configure.ac Tue Feb 13 13:36:31 2007 +0000 +++ b/configure.ac Tue Feb 13 13:42:37 2007 +0000 @@ -3,7 +3,7 @@ #------------------------------------------------------------------------------ AC_PREREQ(2.59) -AC_INIT([ethercat],[1.1.1],[fp@igh-essen.com]) +AC_INIT([ethercat],[1.2.0-rc2],[fp@igh-essen.com]) AC_CONFIG_AUX_DIR([autoconf]) AM_INIT_AUTOMAKE([-Wall -Werror dist-bzip2]) AC_PREFIX_DEFAULT([/opt/etherlab]) @@ -50,10 +50,15 @@ fi # Try to get kernel release string -if test -r ${sourcedir}/.kernelrelease; then +if test -r ${sourcedir}/include/config/kernel.release; then + kernelrelease=`cat $sourcedir/include/config/kernel.release` +elif test -r ${sourcedir}/.kernelrelease; then kernelrelease=`cat $sourcedir/.kernelrelease` +elif test -r ${sourcedir}/include/linux/utsrelease.h; then + hdr=${sourcedir}/include/linux/utsrelease.h + kernelrelease=`grep UTS_RELEASE $hdr | cut -d " " -f 3- | tr -d \"` elif test -r ${sourcedir}/include/linux/version.h; then - hdr=$sourcedir/include/linux/version.h + hdr=${sourcedir}/include/linux/version.h kernelrelease=`grep UTS_RELEASE $hdr | cut -d " " -f 3- | tr -d \"` fi @@ -63,7 +68,7 @@ fi # Extract three numbers from kernel release string -linuxversion=`echo $kernelrelease | grep -oE [[0-9]]+\.[[0-9]]+\.[[0-9]]+` +linuxversion=`echo $kernelrelease | grep -oE "^[[0-9]]+\.[[0-9]]+\.[[0-9]]+"` AC_SUBST(LINUX_SOURCE_DIR,[$sourcedir]) AC_SUBST(LINUX_KERNEL_RELEASE,[$kernelrelease]) diff -r bc89e3fba1a5 -r 5fcbd29151d2 devices/8139too-2.6.13-ethercat.c --- a/devices/8139too-2.6.13-ethercat.c Tue Feb 13 13:36:31 2007 +0000 +++ b/devices/8139too-2.6.13-ethercat.c Tue Feb 13 13:42:37 2007 +0000 @@ -693,6 +693,8 @@ MODULE_PARM_DESC(ec_device_master_index, "Index of the EtherCAT master to register the device."); +void ec_poll(struct net_device *); + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ static int read_eeprom (void __iomem *ioaddr, int location, int addr_len); @@ -709,8 +711,8 @@ #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 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); @@ -2337,6 +2339,15 @@ return !done; } +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +void ec_poll(struct net_device *dev) +{ + rtl8139_interrupt(0, dev, NULL); +} + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + /* 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, @@ -2903,16 +2914,18 @@ 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))) { + rtl_ec_net_dev, ec_poll, 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"); + printk(KERN_INFO "Opening EtherCAT device...\n"); + if (ecdev_open(rtl_ec_dev)) { + printk(KERN_ERR "Failed to open EtherCAT device!\n"); goto out_unregister; } + + printk(KERN_INFO "EtherCAT device ready.\n"); } else { printk(KERN_WARNING "No EtherCAT device registered!\n"); } @@ -2920,7 +2933,9 @@ return 0; out_unregister: + printk(KERN_INFO "Unregistering EtherCAT device...\n"); ecdev_unregister(ec_device_master_index, rtl_ec_dev); + rtl_ec_dev = NULL; out_pci: pci_unregister_driver(&rtl8139_pci_driver); out_return: @@ -2937,9 +2952,9 @@ 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"); + printk(KERN_INFO "Closing EtherCAT device...\n"); + ecdev_close(rtl_ec_dev); + printk(KERN_INFO "Unregistering EtherCAT device...\n"); ecdev_unregister(ec_device_master_index, rtl_ec_dev); rtl_ec_dev = NULL; } diff -r bc89e3fba1a5 -r 5fcbd29151d2 devices/8139too-2.6.17-ethercat.c --- a/devices/8139too-2.6.17-ethercat.c Tue Feb 13 13:36:31 2007 +0000 +++ b/devices/8139too-2.6.17-ethercat.c Tue Feb 13 13:42:37 2007 +0000 @@ -695,6 +695,8 @@ MODULE_PARM_DESC(ec_device_master_index, "Index of the EtherCAT master to register the device."); +void ec_poll(struct net_device *); + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ static int read_eeprom (void __iomem *ioaddr, int location, int addr_len); @@ -711,8 +713,8 @@ #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 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); @@ -2344,6 +2346,15 @@ return !done; } +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +void ec_poll(struct net_device *dev) +{ + rtl8139_interrupt(0, dev, NULL); +} + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + /* 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, @@ -2901,16 +2912,18 @@ 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))) { + rtl_ec_net_dev, ec_poll, 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"); + printk(KERN_INFO "Opening EtherCAT device...\n"); + if (ecdev_open(rtl_ec_dev)) { + printk(KERN_ERR "Failed to open EtherCAT device!\n"); goto out_unregister; } + + printk(KERN_INFO "EtherCAT device ready.\n"); } else { printk(KERN_WARNING "No EtherCAT device registered!\n"); } @@ -2918,7 +2931,9 @@ return 0; out_unregister: + printk(KERN_INFO "Unregistering EtherCAT device...\n"); ecdev_unregister(ec_device_master_index, rtl_ec_dev); + rtl_ec_dev = NULL; out_pci: pci_unregister_driver(&rtl8139_pci_driver); out_return: @@ -2935,9 +2950,9 @@ 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"); + printk(KERN_INFO "Closing EtherCAT device...\n"); + ecdev_close(rtl_ec_dev); + printk(KERN_INFO "Unregistering EtherCAT device...\n"); ecdev_unregister(ec_device_master_index, rtl_ec_dev); rtl_ec_dev = NULL; } diff -r bc89e3fba1a5 -r 5fcbd29151d2 devices/8139too-2.6.18-ethercat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/8139too-2.6.18-ethercat.c Tue Feb 13 13:42:37 2007 +0000 @@ -0,0 +1,2974 @@ +/****************************************************************************** + * + * $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 + +/* 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."); + +void ec_poll(struct net_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 +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) { + dev_err(&pdev->dev, "Unable to alloc new net device\n"); + 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)) { + dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n"); + rc = -ENODEV; + goto err_out; + } + /* check for weird/broken PCI region reporting */ + if (pio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } +#else + /* make sure PCI base addr 1 is MMIO */ + if (!(mmio_flags & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); + rc = -ENODEV; + goto err_out; + } + if (mmio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } +#endif + + rc = pci_request_regions (pdev, DRV_NAME); + 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) { + dev_err(&pdev->dev, "cannot map PIO, aborting\n"); + 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) { + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); + 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) { + dev_err(&pdev->dev, "Chip not responding, ignoring board\n"); + 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 */ + dev_printk (KERN_DEBUG, &pdev->dev, + "unknown chip version, assuming RTL-8139\n"); + dev_printk (KERN_DEBUG, &pdev->dev, + "TxConfig = 0x%lx\n", 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) { + dev_info(&pdev->dev, + "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", + pdev->vendor, pdev->device, pci_rev); + dev_info(&pdev->dev, + "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, + IRQF_SHARED, 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 %#llx IRQ %d" + " GP Pins %2.2x %s-duplex.\n", dev->name, + (unsigned long long)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; + unsigned long flags; + + /* 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; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + if (dev != rtl_ec_net_dev) { + spin_lock_irqsave(&tp->lock, flags); + + 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_irqrestore(&tp->lock, flags); + + if (netif_msg_tx_queued(tp)) + printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n", + dev->name, len, entry); + } + else { + 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 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + 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; +} + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +void ec_poll(struct net_device *dev) +{ + rtl8139_interrupt(0, dev, NULL); +} + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/* 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, ec_poll, THIS_MODULE))) { + printk(KERN_ERR "Failed to register EtherCAT device!\n"); + goto out_pci; + } + + printk(KERN_INFO "Opening EtherCAT device...\n"); + if (ecdev_open(rtl_ec_dev)) { + printk(KERN_ERR "Failed to open EtherCAT device!\n"); + goto out_unregister; + } + + printk(KERN_INFO "EtherCAT device ready.\n"); + } else { + printk(KERN_WARNING "No EtherCAT device registered!\n"); + } + + return 0; + + out_unregister: + printk(KERN_INFO "Unregistering EtherCAT device...\n"); + ecdev_unregister(ec_device_master_index, rtl_ec_dev); + rtl_ec_dev = NULL; + 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 "Closing EtherCAT device...\n"); + ecdev_close(rtl_ec_dev); + printk(KERN_INFO "Unregistering EtherCAT 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 bc89e3fba1a5 -r 5fcbd29151d2 devices/8139too-2.6.18-orig.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/8139too-2.6.18-orig.c Tue Feb 13 13:42:37 2007 +0000 @@ -0,0 +1,2643 @@ +/* + + 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 + +#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) { + dev_err(&pdev->dev, "Unable to alloc new net device\n"); + 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)) { + dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n"); + rc = -ENODEV; + goto err_out; + } + /* check for weird/broken PCI region reporting */ + if (pio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } +#else + /* make sure PCI base addr 1 is MMIO */ + if (!(mmio_flags & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); + rc = -ENODEV; + goto err_out; + } + if (mmio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } +#endif + + rc = pci_request_regions (pdev, DRV_NAME); + 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) { + dev_err(&pdev->dev, "cannot map PIO, aborting\n"); + 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) { + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); + 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) { + dev_err(&pdev->dev, "Chip not responding, ignoring board\n"); + 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 */ + dev_printk (KERN_DEBUG, &pdev->dev, + "unknown chip version, assuming RTL-8139\n"); + dev_printk (KERN_DEBUG, &pdev->dev, + "TxConfig = 0x%lx\n", 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) { + dev_info(&pdev->dev, + "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", + pdev->vendor, pdev->device, pci_rev); + dev_info(&pdev->dev, + "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, IRQF_SHARED, 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 %#llx IRQ %d" + " GP Pins %2.2x %s-duplex.\n", dev->name, + (unsigned long long)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; + unsigned long flags; + + /* 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_irqsave(&tp->lock, flags); + 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_irqrestore(&tp->lock, flags); + + 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 bc89e3fba1a5 -r 5fcbd29151d2 devices/8139too-2.6.19-ethercat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/8139too-2.6.19-ethercat.c Tue Feb 13 13:42:37 2007 +0000 @@ -0,0 +1,2969 @@ +/****************************************************************************** + * + * $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.28" + + +#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."); + +void ec_poll(struct net_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 +static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance); +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 const 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) { + dev_err(&pdev->dev, "Unable to alloc new net device\n"); + 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)) { + dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n"); + rc = -ENODEV; + goto err_out; + } + /* check for weird/broken PCI region reporting */ + if (pio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } +#else + /* make sure PCI base addr 1 is MMIO */ + if (!(mmio_flags & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); + rc = -ENODEV; + goto err_out; + } + if (mmio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } +#endif + + rc = pci_request_regions (pdev, DRV_NAME); + 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) { + dev_err(&pdev->dev, "cannot map PIO, aborting\n"); + 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) { + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); + 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) { + dev_err(&pdev->dev, "Chip not responding, ignoring board\n"); + 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 */ + dev_printk (KERN_DEBUG, &pdev->dev, + "unknown chip version, assuming RTL-8139\n"); + dev_printk (KERN_DEBUG, &pdev->dev, + "TxConfig = 0x%lx\n", 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) { + dev_info(&pdev->dev, + "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", + pdev->vendor, pdev->device, pci_rev); + dev_info(&pdev->dev, + "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, + IRQF_SHARED, 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 %#llx IRQ %d" + " GP Pins %2.2x %s-duplex.\n", dev->name, + (unsigned long long)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; + unsigned long flags; + + /* 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; + } + + /* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + if (dev != rtl_ec_net_dev) { + spin_lock_irqsave(&tp->lock, flags); + + 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_irqrestore(&tp->lock, flags); + + if (netif_msg_tx_queued(tp)) + printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n", + dev->name, len, entry); + } + else { + 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 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + 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; +} + +/* EtherCAT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + +void ec_poll(struct net_device *dev) +{ + rtl8139_interrupt(0, dev); +} + +/* EtherCAT <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/* 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 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); + 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 const 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) { + 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_register_driver(&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, ec_poll, THIS_MODULE))) { + printk(KERN_ERR "Failed to register EtherCAT device!\n"); + goto out_pci; + } + + printk(KERN_INFO "Opening EtherCAT device...\n"); + if (ecdev_open(rtl_ec_dev)) { + printk(KERN_ERR "Failed to open EtherCAT device!\n"); + goto out_unregister; + } + + printk(KERN_INFO "EtherCAT device ready.\n"); + } else { + printk(KERN_WARNING "No EtherCAT device registered!\n"); + } + + return 0; + + out_unregister: + printk(KERN_INFO "Unregistering EtherCAT device...\n"); + ecdev_unregister(ec_device_master_index, rtl_ec_dev); + rtl_ec_dev = NULL; + 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 "Closing EtherCAT device...\n"); + ecdev_close(rtl_ec_dev); + printk(KERN_INFO "Unregistering EtherCAT 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 bc89e3fba1a5 -r 5fcbd29151d2 devices/8139too-2.6.19-orig.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devices/8139too-2.6.19-orig.c Tue Feb 13 13:42:37 2007 +0000 @@ -0,0 +1,2638 @@ +/* + + 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.28" + + +#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); +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 const 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) { + dev_err(&pdev->dev, "Unable to alloc new net device\n"); + 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)) { + dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n"); + rc = -ENODEV; + goto err_out; + } + /* check for weird/broken PCI region reporting */ + if (pio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } +#else + /* make sure PCI base addr 1 is MMIO */ + if (!(mmio_flags & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); + rc = -ENODEV; + goto err_out; + } + if (mmio_len < RTL_MIN_IO_SIZE) { + dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n"); + rc = -ENODEV; + goto err_out; + } +#endif + + rc = pci_request_regions (pdev, DRV_NAME); + 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) { + dev_err(&pdev->dev, "cannot map PIO, aborting\n"); + 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) { + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); + 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) { + dev_err(&pdev->dev, "Chip not responding, ignoring board\n"); + 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 */ + dev_printk (KERN_DEBUG, &pdev->dev, + "unknown chip version, assuming RTL-8139\n"); + dev_printk (KERN_DEBUG, &pdev->dev, + "TxConfig = 0x%lx\n", 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) { + dev_info(&pdev->dev, + "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", + pdev->vendor, pdev->device, pci_rev); + dev_info(&pdev->dev, + "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, IRQF_SHARED, 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 %#llx IRQ %d" + " GP Pins %2.2x %s-duplex.\n", dev->name, + (unsigned long long)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; + unsigned long flags; + + /* 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_irqsave(&tp->lock, flags); + 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_irqrestore(&tp->lock, flags); + + 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 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); + 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 const 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) { + 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_register_driver(&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 bc89e3fba1a5 -r 5fcbd29151d2 devices/Makefile.am --- a/devices/Makefile.am Tue Feb 13 13:36:31 2007 +0000 +++ b/devices/Makefile.am Tue Feb 13 13:42:37 2007 +0000 @@ -37,12 +37,17 @@ 8139too-2.6.13-ethercat.c \ 8139too-2.6.13-orig.c \ 8139too-2.6.17-ethercat.c \ - 8139too-2.6.17-orig.c + 8139too-2.6.17-orig.c \ + 8139too-2.6.18-ethercat.c \ + 8139too-2.6.18-orig.c \ + 8139too-2.6.19-ethercat.c \ + 8139too-2.6.19-orig.c modules: $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_top_srcdir@" modules modules_install: + mkdir -p $(DESTDIR)$(LINUX_MOD_PATH) cp $(srcdir)/ec_8139too.ko $(DESTDIR)$(LINUX_MOD_PATH) clean-local: diff -r bc89e3fba1a5 -r 5fcbd29151d2 devices/ecdev.h --- a/devices/ecdev.h Tue Feb 13 13:36:31 2007 +0000 +++ b/devices/ecdev.h Tue Feb 13 13:42:37 2007 +0000 @@ -58,25 +58,24 @@ typedef struct ec_device ec_device_t; /**< \see ec_device */ /** - Interrupt-Service-Routine Type + Device poll function type. */ -typedef irqreturn_t (*ec_isr_t)(int, void *, struct pt_regs *); +typedef void (*ec_pollfunc_t)(struct net_device *); /*****************************************************************************/ // Registration functions ec_device_t *ecdev_register(unsigned int master_index, - struct net_device *net_dev, ec_isr_t isr, + struct net_device *net_dev, ec_pollfunc_t poll, struct module *module); void ecdev_unregister(unsigned int master_index, ec_device_t *device); -int ecdev_start(unsigned int master_index); -void ecdev_stop(unsigned int master_index); - /*****************************************************************************/ // Device methods +int ecdev_open(ec_device_t *device); +void ecdev_close(ec_device_t *device); void ecdev_receive(ec_device_t *device, const void *data, size_t size); void ecdev_link_state(ec_device_t *device, uint8_t newstate); diff -r bc89e3fba1a5 -r 5fcbd29151d2 examples/mini/mini.c --- a/examples/mini/mini.c Tue Feb 13 13:36:31 2007 +0000 +++ b/examples/mini/mini.c Tue Feb 13 13:42:37 2007 +0000 @@ -32,7 +32,6 @@ *****************************************************************************/ #include -#include #include #include #include @@ -42,7 +41,7 @@ #define FREQUENCY 100 -#define KBUS +//#define KBUS /*****************************************************************************/ @@ -59,11 +58,11 @@ void *r_outputs; #endif -void *r_ana_in; +void *r_dig_out; #if 1 ec_pdo_reg_t domain1_pdos[] = { - {"2", Beckhoff_EL3102_Input1, &r_ana_in}, + {"4", Beckhoff_EL2004_Outputs, &r_dig_out}, {} }; #endif @@ -73,33 +72,40 @@ void run(unsigned long data) { static unsigned int counter = 0; - static unsigned int einaus = 0; - - spin_lock(&master_lock); + static unsigned int blink = 0; // receive + spin_lock(&master_lock); ecrt_master_receive(master); ecrt_domain_process(domain1); + spin_unlock(&master_lock); // 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); - ecrt_master_send(master); - - spin_unlock(&master_lock); + // k_pos = EC_READ_U32(r_ssi); + EC_WRITE_U8(r_dig_out, blink ? 0x0F : 0x00); if (counter) { counter--; } else { counter = FREQUENCY; - einaus = !einaus; - } + blink = !blink; + } + +#ifdef KBUS + EC_WRITE_U8(r_outputs + 2, blink ? 0xFF : 0x00); +#endif + + // send + spin_lock(&master_lock); + ecrt_domain_queue(domain1); + spin_unlock(&master_lock); + + ecrt_master_run(master); + + spin_lock(&master_lock); + ecrt_master_send(master); + spin_unlock(&master_lock); // restart timer timer.expires += HZ / FREQUENCY; @@ -110,7 +116,7 @@ int request_lock(void *data) { - spin_lock_bh(&master_lock); + spin_lock(&master_lock); return 0; // access allowed } @@ -118,20 +124,20 @@ void release_lock(void *data) { - spin_unlock_bh(&master_lock); + spin_unlock(&master_lock); } /*****************************************************************************/ int __init init_mini_module(void) { -#if 1 +#if 0 ec_slave_t *slave; #endif printk(KERN_INFO "=== Starting Minimal EtherCAT environment... ===\n"); - if ((master = ecrt_request_master(0)) == NULL) { + if (!(master = ecrt_request_master(0))) { printk(KERN_ERR "Requesting master 0 failed!\n"); goto out_return; } @@ -139,8 +145,7 @@ ecrt_master_callbacks(master, request_lock, release_lock, NULL); printk(KERN_INFO "Registering domain...\n"); - if (!(domain1 = ecrt_master_create_domain(master))) - { + if (!(domain1 = ecrt_master_create_domain(master))) { printk(KERN_ERR "Domain creation failed!\n"); goto out_release_master; } @@ -166,7 +171,7 @@ } #endif -#if 1 +#if 0 if (!(slave = ecrt_master_get_slave(master, "2"))) goto out_release_master; @@ -180,8 +185,6 @@ goto out_release_master; } - ecrt_master_prepare(master); - printk("Starting cyclic sample thread.\n"); init_timer(&timer); timer.function = run; @@ -213,11 +216,10 @@ /*****************************************************************************/ MODULE_LICENSE("GPL"); -MODULE_AUTHOR ("Florian Pose "); -MODULE_DESCRIPTION ("EtherCAT minimal test environment"); +MODULE_AUTHOR("Florian Pose "); +MODULE_DESCRIPTION("EtherCAT minimal test environment"); module_init(init_mini_module); module_exit(cleanup_mini_module); /*****************************************************************************/ - diff -r bc89e3fba1a5 -r 5fcbd29151d2 examples/msr/msr_sample.c --- a/examples/msr/msr_sample.c Tue Feb 13 13:36:31 2007 +0000 +++ b/examples/msr/msr_sample.c Tue Feb 13 13:42:37 2007 +0000 @@ -66,7 +66,7 @@ // raw process data void *r_ana_out; -// Channels +// channels double k_ana_out; ec_pdo_reg_t domain1_pdos[] = { @@ -78,19 +78,20 @@ void msr_controller_run(void) { + // receive rt_sem_wait(&master_sem); - - // receive ecrt_master_receive(master); ecrt_domain_process(domain1); + rt_sem_signal(&master_sem); // Process data EC_WRITE_S16(r_ana_out, k_ana_out / 10.0 * 0x7FFF); // Send + rt_sem_wait(&master_sem); + ecrt_domain_queue(domain1); ecrt_master_run(master); ecrt_master_send(master); - rt_sem_signal(&master_sem); msr_write_kanal_list(); @@ -175,8 +176,6 @@ goto out_release_master; } - ecrt_master_prepare(master); - printk("Starting cyclic sample thread...\n"); ticks = start_rt_timer(nano2count(TIMERTICKS)); if (rt_task_init(&task, msr_run, 0, 2000, 0, 1, NULL)) { @@ -195,7 +194,6 @@ rt_task_delete(&task); out_stop_timer: stop_rt_timer(); - ecrt_master_deactivate(master); out_release_master: ecrt_release_master(master); out_msr_cleanup: @@ -223,8 +221,8 @@ /*****************************************************************************/ MODULE_LICENSE("GPL"); -MODULE_AUTHOR ("Florian Pose "); -MODULE_DESCRIPTION ("EtherCAT RTAI MSR sample module"); +MODULE_AUTHOR("Florian Pose "); +MODULE_DESCRIPTION("EtherCAT RTAI MSR sample module"); module_init(init_mod); module_exit(cleanup_mod); diff -r bc89e3fba1a5 -r 5fcbd29151d2 examples/rtai/rtai_sample.c --- a/examples/rtai/rtai_sample.c Tue Feb 13 13:36:31 2007 +0000 +++ b/examples/rtai/rtai_sample.c Tue Feb 13 13:42:37 2007 +0000 @@ -46,14 +46,8 @@ /*****************************************************************************/ -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Florian Pose "); -MODULE_DESCRIPTION("EtherCAT RTAI sample module"); - -/*****************************************************************************/ - // RTAI task frequency in Hz -#define FREQUENCY 10000 +#define FREQUENCY 4000 #define INHIBIT_TIME 20 #define TIMERTICKS (1000000000 / FREQUENCY) @@ -70,13 +64,10 @@ ec_domain_t *domain1 = NULL; // data fields -void *r_ana_out; - -// channels -uint32_t k_pos; +void *r_dig_out; ec_pdo_reg_t domain1_pdos[] = { - {"2", Beckhoff_EL4132_Output1, &r_ana_out}, + {"2", Beckhoff_EL2004_Outputs, &r_dig_out}, {} }; @@ -84,21 +75,34 @@ void run(long data) { - while (1) - { + static unsigned int blink = 0; + static unsigned int counter = 0; + + while (1) { t_last_cycle = get_cycles(); + rt_sem_wait(&master_sem); - ecrt_master_receive(master); ecrt_domain_process(domain1); + rt_sem_signal(&master_sem); // process data - //k_pos = EC_READ_U32(r_ssi_input); - + EC_WRITE_U8(r_dig_out, blink ? 0x0F : 0x00); + + rt_sem_wait(&master_sem); + ecrt_domain_queue(domain1); ecrt_master_run(master); ecrt_master_send(master); - rt_sem_signal(&master_sem); + + if (counter) { + counter--; + } + else { + counter = FREQUENCY; + blink = !blink; + } + rt_task_wait_period(); } } @@ -107,7 +111,7 @@ int request_lock(void *data) { - // too close to the next RT cycle: deny access... + // too close to the next real time cycle: deny access... if (get_cycles() - t_last_cycle > t_critical) return -1; // allow access @@ -139,10 +143,9 @@ goto out_return; } - ecrt_master_callbacks(master, request_lock, release_lock, NULL); - printk(KERN_INFO "Registering domain...\n"); + printk(KERN_INFO "Creating domain...\n"); if (!(domain1 = ecrt_master_create_domain(master))) { printk(KERN_ERR "Domain creation failed!\n"); goto out_release_master; @@ -160,8 +163,6 @@ goto out_release_master; } - ecrt_master_prepare(master); - printk("Starting cyclic sample thread...\n"); requested_ticks = nano2count(TIMERTICKS); tick_period = start_rt_timer(requested_ticks); @@ -186,7 +187,6 @@ rt_task_delete(&task); out_stop_timer: stop_rt_timer(); - ecrt_master_deactivate(master); out_release_master: ecrt_release_master(master); out_return: @@ -210,8 +210,11 @@ /*****************************************************************************/ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Florian Pose "); +MODULE_DESCRIPTION("EtherCAT RTAI sample module"); + module_init(init_mod); module_exit(cleanup_mod); /*****************************************************************************/ - diff -r bc89e3fba1a5 -r 5fcbd29151d2 include/ecrt.h --- a/include/ecrt.h Tue Feb 13 13:36:31 2007 +0000 +++ b/include/ecrt.h Tue Feb 13 13:42:37 2007 +0000 @@ -59,6 +59,14 @@ /*****************************************************************************/ +#define ECRT_VER_MAJOR 1U +#define ECRT_VER_MINOR 2U + +#define ECRT_VERSION(a,b) (((a) << 8) + (b)) +#define ECRT_VERSION_MAGIC ECRT_VERSION(ECRT_VER_MAJOR, ECRT_VER_MINOR) + +/*****************************************************************************/ + struct ec_master; typedef struct ec_master ec_master_t; /**< \see ec_master */ @@ -99,6 +107,8 @@ ec_master_t *ecrt_request_master(unsigned int master_index); void ecrt_release_master(ec_master_t *master); +unsigned int ecrt_version_magic(void); + /****************************************************************************** * Master methods *****************************************************************************/ @@ -109,9 +119,6 @@ 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); // deprecated! - -void ecrt_master_prepare(ec_master_t *master); void ecrt_master_send(ec_master_t *master); void ecrt_master_receive(ec_master_t *master); @@ -149,6 +156,7 @@ void **data_ptr); void ecrt_domain_process(ec_domain_t *domain); +void ecrt_domain_queue(ec_domain_t *domain); int ecrt_domain_state(const ec_domain_t *domain); /****************************************************************************** @@ -162,9 +170,6 @@ int ecrt_slave_conf_sdo32(ec_slave_t *slave, uint16_t sdo_index, 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); // deprecated - /****************************************************************************** * Bitwise read/write macros *****************************************************************************/ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/Kbuild --- a/master/Kbuild Tue Feb 13 13:36:31 2007 +0000 +++ b/master/Kbuild Tue Feb 13 13:42:37 2007 +0000 @@ -37,7 +37,7 @@ ec_master-objs := module.o master.o device.o slave.o datagram.o \ domain.o mailbox.o canopen.o ethernet.o \ - fsm_sii.o fsm_change.o fsm_coe.o fsm.o + fsm_sii.o fsm_change.o fsm_coe.o fsm_slave.o fsm_master.o ifeq ($(EC_DBG_IF),1) ec_master-objs += debug.o diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/Makefile.am --- a/master/Makefile.am Tue Feb 13 13:36:31 2007 +0000 +++ b/master/Makefile.am Tue Feb 13 13:42:37 2007 +0000 @@ -43,7 +43,8 @@ fsm_sii.c fsm_sii.h \ fsm_change.c fsm_change.h \ fsm_coe.c fsm_coe.h \ - fsm.c fsm.h \ + fsm_slave.c fsm_slave.h \ + fsm_master.c fsm_master.h \ globals.h \ mailbox.c mailbox.h \ master.c master.h \ @@ -54,6 +55,7 @@ $(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_top_srcdir@" modules modules_install: + mkdir -p $(DESTDIR)$(LINUX_MOD_PATH) cp $(srcdir)/ec_master.ko $(DESTDIR)$(LINUX_MOD_PATH) clean-local: diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/datagram.c --- a/master/datagram.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/datagram.c Tue Feb 13 13:42:37 2007 +0000 @@ -78,7 +78,6 @@ 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; @@ -116,7 +115,7 @@ datagram->mem_size = 0; } - if (!(datagram->data = kmalloc(size, GFP_KERNEL))) { + if (!(datagram->data = kmalloc(size, GFP_ATOMIC))) { EC_ERR("Failed to allocate %i bytes of datagram memory!\n", size); return -1; } diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/datagram.h --- a/master/datagram.h Tue Feb 13 13:36:31 2007 +0000 +++ b/master/datagram.h Tue Feb 13 13:42:37 2007 +0000 @@ -72,12 +72,12 @@ typedef enum { - EC_DATAGRAM_INIT, /**< new datagram */ - EC_DATAGRAM_QUEUED, /**< datagram queued by master */ - EC_DATAGRAM_SENT, /**< datagram has been sent and still in the queue */ - EC_DATAGRAM_RECEIVED, /**< datagram has been received and dequeued */ - EC_DATAGRAM_TIMED_OUT, /**< datagram timed out and was dequeued */ - EC_DATAGRAM_ERROR /**< error while sending/receiving, datagram dequeued */ + EC_DATAGRAM_INIT, /**< new datagram */ + EC_DATAGRAM_QUEUED, /**< datagram queued for sending */ + EC_DATAGRAM_SENT, /**< datagram has been sent (still in the queue) */ + EC_DATAGRAM_RECEIVED, /**< datagram has been received (dequeued) */ + EC_DATAGRAM_TIMED_OUT, /**< datagram timed out (dequeued) */ + EC_DATAGRAM_ERROR /**< error while sending/receiving (dequeued) */ } ec_datagram_state_t; @@ -89,12 +89,15 @@ typedef union { + /** + * Physical address. + */ struct { uint16_t slave; /**< configured or autoincrement address */ uint16_t mem; /**< physical memory address */ } - physical; /**< physical address */ + physical; uint32_t logical; /**< logical address */ } @@ -119,11 +122,10 @@ 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 */ + unsigned long jiffies_sent; /**< jiffies, when the datagram was sent */ + cycles_t cycles_received; /**< time, when the datagram was received */ + unsigned long jiffies_received; /**< jiffies, when the datagram was rec. */ } ec_datagram_t; diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/device.c --- a/master/device.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/device.c Tue Feb 13 13:42:37 2007 +0000 @@ -57,20 +57,23 @@ int ec_device_init(ec_device_t *device, /**< EtherCAT device */ ec_master_t *master, /**< master owning the device */ struct net_device *net_dev, /**< net_device structure */ - ec_isr_t isr, /**< pointer to device's ISR */ - struct module *module /**< pointer to the owning module */ + ec_pollfunc_t poll, /**< pointer to device's poll function */ + struct module *module /**< the device's module */ ) { struct ethhdr *eth; device->master = master; device->dev = net_dev; - device->isr = isr; + device->poll = poll; device->module = module; device->open = 0; device->link_state = 0; // down + device->tx_count = 0; + device->rx_count = 0; + #ifdef EC_DBG_IF if (ec_debug_init(&device->dbg)) { EC_ERR("Failed to init debug device!\n"); @@ -143,9 +146,11 @@ } // device could have received frames before - for (i = 0; i < 4; i++) ec_device_call_isr(device); + for (i = 0; i < 4; i++) ec_device_poll(device); device->link_state = 0; + device->tx_count = 0; + device->rx_count = 0; if (device->dev->open(device->dev) == 0) device->open = 1; @@ -217,22 +222,23 @@ // start sending device->dev->hard_start_xmit(device->tx_skb, device->dev); -} - -/*****************************************************************************/ - -/** - Calls the interrupt service routine of the assigned net_device. + device->tx_count++; +} + +/*****************************************************************************/ + +/** + Calls the poll function of the assigned net_device. The master itself works without using interrupts. Therefore the processing of received data and status changes of the network device has to be done by the master calling the ISR "manually". */ -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); +void ec_device_poll(ec_device_t *device /**< EtherCAT device */) +{ + device->cycles_poll = get_cycles(); + device->jiffies_poll = jiffies; + device->poll(device->dev); } /****************************************************************************** @@ -251,6 +257,8 @@ size_t size /**< number of bytes received */ ) { + device->rx_count++; + if (unlikely(device->master->debug_level > 1)) { EC_DBG("Received frame:\n"); ec_print_data_diff(device->tx_skb->data + ETH_HLEN, diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/device.h --- a/master/device.h Tue Feb 13 13:36:31 2007 +0000 +++ b/master/device.h Tue Feb 13 13:42:37 2007 +0000 @@ -65,11 +65,13 @@ struct net_device *dev; /**< pointer to the assigned net_device */ 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 */ + ec_pollfunc_t poll; /**< pointer to the device's poll function */ + cycles_t cycles_poll; /**< cycles of last poll */ + unsigned long jiffies_poll; /**< jiffies of last poll */ struct module *module; /**< pointer to the device's owning module */ uint8_t link_state; /**< device link state */ + unsigned int tx_count; /**< number of frames sent */ + unsigned int rx_count; /**< number of frames received */ #ifdef EC_DBG_IF ec_debug_t dbg; /**< debug device */ #endif @@ -78,13 +80,13 @@ /*****************************************************************************/ int ec_device_init(ec_device_t *, ec_master_t *, struct net_device *, - ec_isr_t, struct module *); + ec_pollfunc_t, struct module *); void ec_device_clear(ec_device_t *); int ec_device_open(ec_device_t *); int ec_device_close(ec_device_t *); -void ec_device_call_isr(ec_device_t *); +void ec_device_poll(ec_device_t *); uint8_t *ec_device_tx_data(ec_device_t *); void ec_device_send(ec_device_t *, size_t); diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/domain.c --- a/master/domain.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/domain.c Tue Feb 13 13:42:37 2007 +0000 @@ -479,21 +479,6 @@ /*****************************************************************************/ /** - Places all process data datagrams in the masters datagram queue. -*/ - -void ec_domain_queue_datagrams(ec_domain_t *domain /**< EtherCAT domain */) -{ - ec_datagram_t *datagram; - - list_for_each_entry(datagram, &domain->datagrams, list) { - ec_master_queue_datagram(domain->master, datagram); - } -} - -/*****************************************************************************/ - -/** Formats attribute data for SysFS reading. \return number of bytes to read */ @@ -518,7 +503,6 @@ /** Registers a PDO 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 */ @@ -552,8 +536,6 @@ 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; - list_for_each_entry(pdo, &slave->sii_pdos, list) { list_for_each_entry(entry, &pdo->entries, list) { if (entry->index != pdo_index @@ -576,7 +558,7 @@ /** Registeres a bunch of data fields. - Caution! The list has to be terminated with a NULL structure ({})! + \attention The list has to be terminated with a NULL structure ({})! \return 0 in case of success, else < 0 \ingroup RealtimeInterface */ @@ -605,7 +587,6 @@ /** 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 */ @@ -639,8 +620,6 @@ 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; @@ -691,8 +670,22 @@ } domain->working_counter_changes = 0; } - - ec_domain_queue_datagrams(domain); +} + +/*****************************************************************************/ + +/** + Places all process data datagrams in the masters datagram queue. + \ingroup RealtimeInterface +*/ + +void ecrt_domain_queue(ec_domain_t *domain /**< EtherCAT domain */) +{ + ec_datagram_t *datagram; + + list_for_each_entry(datagram, &domain->datagrams, list) { + ec_master_queue_datagram(domain->master, datagram); + } } /*****************************************************************************/ @@ -716,6 +709,7 @@ EXPORT_SYMBOL(ecrt_domain_register_pdo_list); EXPORT_SYMBOL(ecrt_domain_register_pdo_range); EXPORT_SYMBOL(ecrt_domain_process); +EXPORT_SYMBOL(ecrt_domain_queue); EXPORT_SYMBOL(ecrt_domain_state); /** \endcond */ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/domain.h --- a/master/domain.h Tue Feb 13 13:36:31 2007 +0000 +++ b/master/domain.h Tue Feb 13 13:42:37 2007 +0000 @@ -79,7 +79,6 @@ void ec_domain_destroy(ec_domain_t *); int ec_domain_alloc(ec_domain_t *, uint32_t); -void ec_domain_queue_datagrams(ec_domain_t *); /*****************************************************************************/ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/ethernet.c --- a/master/ethernet.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/ethernet.c Tue Feb 13 13:42:37 2007 +0000 @@ -161,7 +161,7 @@ void ec_eoe_clear(ec_eoe_t *eoe /**< EoE handler */) { - unregister_netdev(eoe->dev); + unregister_netdev(eoe->dev); // possibly calls close callback free_netdev(eoe->dev); // empty transmit queue @@ -648,11 +648,8 @@ eoe->opened = 0; 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); - else { + if (eoe->slave) ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_PREOP); - } return 0; } diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm.c --- a/master/fsm.c Tue Feb 13 13:36:31 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1704 +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 finite state machines. -*/ - -/*****************************************************************************/ - -#include "globals.h" -#include "fsm.h" -#include "master.h" -#include "mailbox.h" - -/*****************************************************************************/ - -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_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 *); -void ec_fsm_slavescan_state(ec_fsm_t *); -void ec_fsm_slavescan_base(ec_fsm_t *); -void ec_fsm_slavescan_datalink(ec_fsm_t *); -void ec_fsm_slavescan_eeprom_size(ec_fsm_t *); -void ec_fsm_slavescan_eeprom_data(ec_fsm_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 *); - -/*****************************************************************************/ - -/** - Constructor. -*/ - -int ec_fsm_init(ec_fsm_t *fsm, /**< finite state machine */ - ec_master_t *master /**< EtherCAT master */ - ) -{ - fsm->master = master; - fsm->master_state = ec_fsm_master_start; - fsm->master_slaves_responding = 0; - fsm->master_slave_states = EC_SLAVE_STATE_UNKNOWN; - fsm->master_validation = 0; - - ec_datagram_init(&fsm->datagram); - if (ec_datagram_prealloc(&fsm->datagram, EC_MAX_DATA_SIZE)) { - EC_ERR("Failed to allocate FSM datagram.\n"); - 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; -} - -/*****************************************************************************/ - -/** - Destructor. -*/ - -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); -} - -/*****************************************************************************/ - -/** - Executes the current state of the state machine. - \return false, if state machine has terminated -*/ - -int ec_fsm_exec(ec_fsm_t *fsm /**< finite state machine */) -{ - fsm->master_state(fsm); - - 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; -} - -/****************************************************************************** - * operation/idle state machine - *****************************************************************************/ - -/** - Master state: START. - Starts with getting slave count and slave states. -*/ - -void ec_fsm_master_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_master_broadcast; -} - -/*****************************************************************************/ - -/** - Master state: BROADCAST. - Processes the broadcast read slave count and slaves states. -*/ - -void ec_fsm_master_broadcast(ec_fsm_t *fsm /**< finite state machine */) -{ - ec_datagram_t *datagram = &fsm->datagram; - unsigned int topology_change, states_change, i; - ec_slave_t *slave; - ec_master_t *master = fsm->master; - - if (datagram->state != EC_DATAGRAM_RECEIVED) { - if (!master->device->link_state) { - fsm->master_slaves_responding = 0; - list_for_each_entry(slave, &master->slaves, list) { - slave->online = 0; - } - } - else { - EC_ERR("Failed to receive broadcast datagram.\n"); - } - fsm->master_state = ec_fsm_master_error; - return; - } - - topology_change = (datagram->working_counter != - fsm->master_slaves_responding); - states_change = (EC_READ_U8(datagram->data) != fsm->master_slave_states); - - fsm->master_slave_states = EC_READ_U8(datagram->data); - fsm->master_slaves_responding = datagram->working_counter; - - if (topology_change) { - EC_INFO("%i slave%s responding.\n", - fsm->master_slaves_responding, - fsm->master_slaves_responding == 1 ? "" : "s"); - - if (master->mode == EC_MASTER_MODE_OPERATION) { - if (fsm->master_slaves_responding == master->slave_count) { - fsm->master_validation = 1; // start validation later - } - else { - EC_WARN("Invalid slave count. Bus in tainted state.\n"); - } - } - } - - if (states_change) { - 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_master_eoe_stop(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_end; - return; - } - - // init slaves - for (i = 0; i < master->slave_count; i++) { - if (!(slave = (ec_slave_t *) kmalloc(sizeof(ec_slave_t), - GFP_ATOMIC))) { - EC_ERR("Failed to allocate slave %i!\n", i); - 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_destroy_slaves(master); - fsm->master_state = ec_fsm_master_error; - 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; - fsm->master_state = ec_fsm_master_scan_slaves; - fsm->master_state(fsm); // execute immediately - return; - } - - // fetch state from each slave - fsm->slave = list_entry(master->slaves.next, ec_slave_t, list); - ec_datagram_nprd(&fsm->datagram, fsm->slave->station_address, 0x0130, 2); - ec_master_queue_datagram(master, &fsm->datagram); - fsm->master_state = ec_fsm_master_read_states; -} - -/*****************************************************************************/ - -/** - Master action: PROC_STATES. - Processes the slave states. -*/ - -void ec_fsm_master_action_process_states(ec_fsm_t *fsm - /**< finite state machine */ - ) -{ - ec_master_t *master = fsm->master; - ec_slave_t *slave; - 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 - && 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_state_start; - fsm->slave_state(fsm); // execute immediately - return; - } - - // Check, if EoE processing has to be started - ec_master_eoe_start(master); - - if (master->mode == EC_MASTER_MODE_IDLE) { - - // 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; - - if (!slave->online || slave->error_flag) { - kfree(slave->new_eeprom_data); - slave->new_eeprom_data = NULL; - EC_ERR("Discarding EEPROM data, slave %i not ready.\n", - slave->ring_position); - continue; - } - - // 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; - 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; - } - } - - fsm->master_state = ec_fsm_master_end; -} - -/*****************************************************************************/ - -/** - Master action: Get state of next slave. -*/ - -void ec_fsm_master_action_next_slave_state(ec_fsm_t *fsm - /**< finite state machine */) -{ - ec_master_t *master = fsm->master; - ec_slave_t *slave = fsm->slave; - - // is there another slave to query? - if (slave->list.next != &master->slaves) { - // process next slave - fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); - ec_datagram_nprd(&fsm->datagram, fsm->slave->station_address, - 0x0130, 2); - ec_master_queue_datagram(master, &fsm->datagram); - fsm->master_state = ec_fsm_master_read_states; - return; - } - - // all slave states read - - // check, if a bus validation has to be done - if (fsm->master_validation) { - fsm->master_validation = 0; - list_for_each_entry(slave, &master->slaves, list) { - if (slave->online) continue; - - // At least one slave is offline. validate! - EC_INFO("Validating bus.\n"); - fsm->slave = list_entry(master->slaves.next, ec_slave_t, list); - fsm->master_state = ec_fsm_master_validate_vendor; - ec_fsm_sii_read(&fsm->fsm_sii, slave, 0x0008, EC_FSM_SII_POSITION); - ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately - return; - } - } - - ec_fsm_master_action_process_states(fsm); -} - -/*****************************************************************************/ - -/** - Master state: READ STATES. - Fetches the AL- and online state of a slave. -*/ - -void ec_fsm_master_read_states(ec_fsm_t *fsm /**< finite state machine */) -{ - ec_slave_t *slave = fsm->slave; - ec_datagram_t *datagram = &fsm->datagram; - uint8_t new_state; - - if (datagram->state != EC_DATAGRAM_RECEIVED) { - EC_ERR("Failed to receive AL state datagram for slave %i!\n", - slave->ring_position); - fsm->master_state = ec_fsm_master_error; - return; - } - - // did the slave not respond to its station address? - if (datagram->working_counter != 1) { - if (slave->online) { - slave->online = 0; - if (slave->master->debug_level) - EC_DBG("Slave %i: offline.\n", slave->ring_position); - } - ec_fsm_master_action_next_slave_state(fsm); - return; - } - - // slave responded - new_state = EC_READ_U8(datagram->data); - if (!slave->online) { // slave was offline before - slave->online = 1; - slave->error_flag = 0; // clear error flag - slave->current_state = new_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) { - 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); -} - -/*****************************************************************************/ - -/** - Master state: VALIDATE_VENDOR. - Validates the vendor ID of a slave. -*/ - -void ec_fsm_master_validate_vendor(ec_fsm_t *fsm /**< finite state machine */) -{ - ec_slave_t *slave = fsm->slave; - - 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_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; - ec_fsm_sii_read(&fsm->fsm_sii, slave, 0x000A, EC_FSM_SII_POSITION); - ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately -} - -/*****************************************************************************/ - -/** - Master action: ADDRESS. - Looks for slave, that have lost their configuration and writes - their station address, so that they can be reconfigured later. -*/ - -void ec_fsm_master_action_addresses(ec_fsm_t *fsm /**< finite state machine */) -{ - ec_datagram_t *datagram = &fsm->datagram; - - while (fsm->slave->online) { - if (fsm->slave->list.next == &fsm->master->slaves) { // last slave? - fsm->master_state = ec_fsm_master_start; - fsm->master_state(fsm); // execute immediately - return; - } - // check next slave - fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); - } - - 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); - EC_WRITE_U16(datagram->data, fsm->slave->station_address); - ec_master_queue_datagram(fsm->master, datagram); - fsm->master_state = ec_fsm_master_rewrite_addresses; -} - -/*****************************************************************************/ - -/** - Master state: VALIDATE_PRODUCT. - Validates the product ID of a slave. -*/ - -void ec_fsm_master_validate_product(ec_fsm_t *fsm /**< finite state machine */) -{ - ec_slave_t *slave = fsm->slave; - - 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_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->fsm_sii.value)); - fsm->master_state = ec_fsm_master_error; - return; - } - - // have all states been validated? - if (slave->list.next == &fsm->master->slaves) { - fsm->slave = list_entry(fsm->master->slaves.next, ec_slave_t, list); - // start writing addresses to offline slaves - ec_fsm_master_action_addresses(fsm); - return; - } - - // validate next slave - fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); - fsm->master_state = ec_fsm_master_validate_vendor; - 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. -*/ - -void ec_fsm_master_rewrite_addresses(ec_fsm_t *fsm - /**< finite state machine */ - ) -{ - ec_slave_t *slave = fsm->slave; - ec_datagram_t *datagram = &fsm->datagram; - - if (datagram->state != EC_DATAGRAM_RECEIVED - || datagram->working_counter != 1) { - EC_ERR("Failed to write station address on slave %i.\n", - slave->ring_position); - } - - if (fsm->slave->list.next == &fsm->master->slaves) { // last slave? - fsm->master_state = ec_fsm_master_start; - fsm->master_state(fsm); // execute immediately - return; - } - - // check next slave - fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); - // Write new station address to slave - ec_fsm_master_action_addresses(fsm); -} - -/*****************************************************************************/ - -/** - Master state: SCAN SLAVES. - Executes the sub-statemachine for the scanning of a slave. -*/ - -void ec_fsm_master_scan_slaves(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_slave_state_end - && fsm->slave_state != ec_fsm_slave_state_error) return; - - // another slave to fetch? - 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); - - // set initial states of all slaves to PREOP to make mailbox - // communication possible - list_for_each_entry(slave, &master->slaves, list) { - ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); - } - - fsm->master_state = ec_fsm_master_end; -} - -/*****************************************************************************/ - -/** - Master state: CONFIGURE SLAVES. - Starts configuring a slave. -*/ - -void ec_fsm_master_configure_slave(ec_fsm_t *fsm - /**< finite state machine */ - ) -{ - fsm->slave_state(fsm); // execute slave's state machine - - 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); -} - -/*****************************************************************************/ - -/** - Master state: WRITE EEPROM. -*/ - -void ec_fsm_master_write_eeprom(ec_fsm_t *fsm /**< finite state machine */) -{ - ec_slave_t *slave = fsm->slave; - - 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_error; - return; - } - - fsm->sii_offset++; - if (fsm->sii_offset < slave->new_eeprom_size) { - 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; - } - - // finished writing EEPROM - EC_INFO("Finished writing EEPROM of slave %i.\n", slave->ring_position); - kfree(slave->new_eeprom_data); - slave->new_eeprom_data = NULL; - - // TODO: Evaluate new EEPROM contents! - - // restart master state machine. - fsm->master_state = ec_fsm_master_start; - fsm->master_state(fsm); // execute immediately -} - -/*****************************************************************************/ - -/** - 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; -} - -/****************************************************************************** - * slave scan state machine - *****************************************************************************/ - -/** - Slave scan state: START. - First state of the slave state machine. Writes the station address to the - slave, according to its ring position. -*/ - -void ec_fsm_slavescan_start(ec_fsm_t *fsm /**< finite state machine */) -{ - ec_datagram_t *datagram = &fsm->datagram; - - // write station address - ec_datagram_apwr(datagram, fsm->slave->ring_position, 0x0010, 2); - EC_WRITE_U16(datagram->data, fsm->slave->station_address); - ec_master_queue_datagram(fsm->master, datagram); - fsm->slave_state = ec_fsm_slavescan_address; -} - -/*****************************************************************************/ - -/** - Slave scan state: ADDRESS. -*/ - -void ec_fsm_slavescan_address(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 write station address of slave %i.\n", - fsm->slave->ring_position); - return; - } - - // Read AL state - ec_datagram_nprd(datagram, fsm->slave->station_address, 0x0130, 2); - ec_master_queue_datagram(fsm->master, datagram); - fsm->slave_state = ec_fsm_slavescan_state; -} - -/*****************************************************************************/ - -/** - Slave scan state: STATE. -*/ - -void ec_fsm_slavescan_state(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->slave->error_flag = 1; - 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_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 - ec_datagram_nprd(datagram, fsm->slave->station_address, 0x0000, 6); - ec_master_queue_datagram(fsm->master, datagram); - fsm->slave_state = ec_fsm_slavescan_base; -} - -/*****************************************************************************/ - -/** - Slave scan state: BASE. -*/ - -void ec_fsm_slavescan_base(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->slave->error_flag = 1; - fsm->slave_state = ec_fsm_slave_state_error; - EC_ERR("Failed to read base data of slave %i.\n", - slave->ring_position); - return; - } - - slave->base_type = EC_READ_U8 (datagram->data); - slave->base_revision = EC_READ_U8 (datagram->data + 1); - slave->base_build = EC_READ_U16(datagram->data + 2); - slave->base_fmmu_count = EC_READ_U8 (datagram->data + 4); - slave->base_sync_count = EC_READ_U8 (datagram->data + 5); - - if (slave->base_fmmu_count > EC_MAX_FMMUS) - slave->base_fmmu_count = EC_MAX_FMMUS; - - // read data link status - ec_datagram_nprd(datagram, slave->station_address, 0x0110, 2); - ec_master_queue_datagram(slave->master, datagram); - fsm->slave_state = ec_fsm_slavescan_datalink; -} - -/*****************************************************************************/ - -/** - Slave scan state: DATALINK. -*/ - -void ec_fsm_slavescan_datalink(ec_fsm_t *fsm /**< finite state machine */) -{ - ec_datagram_t *datagram = &fsm->datagram; - ec_slave_t *slave = fsm->slave; - uint16_t dl_status; - unsigned int i; - - 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 read DL status of slave %i.\n", - slave->ring_position); - return; - } - - dl_status = EC_READ_U16(datagram->data); - for (i = 0; i < 4; i++) { - slave->dl_link[i] = dl_status & (1 << (4 + i)) ? 1 : 0; - slave->dl_loop[i] = dl_status & (1 << (8 + i * 2)) ? 1 : 0; - slave->dl_signal[i] = dl_status & (1 << (9 + i * 2)) ? 1 : 0; - } - - // Start fetching EEPROM size - - fsm->sii_offset = 0x0040; // first category header - 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 -} - -/*****************************************************************************/ - -/** - Slave scan state: EEPROM SIZE. -*/ - -void ec_fsm_slavescan_eeprom_size(ec_fsm_t *fsm /**< finite state machine */) -{ - ec_slave_t *slave = fsm->slave; - uint16_t cat_type, cat_size; - - 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_slave_state_error; - EC_ERR("Failed to read EEPROM size of slave %i.\n", - slave->ring_position); - return; - } - - 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; - 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; - } - - slave->eeprom_size = (fsm->sii_offset + 1) * 2; - - if (slave->eeprom_data) { - EC_INFO("Freeing old EEPROM data on slave %i...\n", - slave->ring_position); - kfree(slave->eeprom_data); - } - - if (!(slave->eeprom_data = - (uint8_t *) kmalloc(slave->eeprom_size, GFP_ATOMIC))) { - fsm->slave->error_flag = 1; - fsm->slave_state = ec_fsm_slave_state_error; - EC_ERR("Failed to allocate EEPROM data on slave %i.\n", - slave->ring_position); - return; - } - - // Start fetching EEPROM contents - - fsm->slave_state = ec_fsm_slavescan_eeprom_data; - fsm->sii_offset = 0x0000; - 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 */) -{ - ec_slave_t *slave = fsm->slave; - uint16_t *cat_word, cat_type, cat_size; - - 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_slave_state_error; - EC_ERR("Failed to fetch EEPROM contents of slave %i.\n", - slave->ring_position); - 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->fsm_sii.value, 4); - } - else { // copy the last word - 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; - 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; - } - - // Evaluate EEPROM contents - - slave->sii_alias = - EC_READ_U16(slave->eeprom_data + 2 * 0x0004); - slave->sii_vendor_id = - EC_READ_U32(slave->eeprom_data + 2 * 0x0008); - slave->sii_product_code = - EC_READ_U32(slave->eeprom_data + 2 * 0x000A); - slave->sii_revision_number = - EC_READ_U32(slave->eeprom_data + 2 * 0x000C); - slave->sii_serial_number = - EC_READ_U32(slave->eeprom_data + 2 * 0x000E); - slave->sii_rx_mailbox_offset = - EC_READ_U16(slave->eeprom_data + 2 * 0x0018); - slave->sii_rx_mailbox_size = - EC_READ_U16(slave->eeprom_data + 2 * 0x0019); - slave->sii_tx_mailbox_offset = - EC_READ_U16(slave->eeprom_data + 2 * 0x001A); - slave->sii_tx_mailbox_size = - EC_READ_U16(slave->eeprom_data + 2 * 0x001B); - slave->sii_mailbox_protocols = - EC_READ_U16(slave->eeprom_data + 2 * 0x001C); - - // evaluate category data - cat_word = (uint16_t *) slave->eeprom_data + 0x0040; - while (EC_READ_U16(cat_word) != 0xFFFF) { - cat_type = EC_READ_U16(cat_word) & 0x7FFF; - cat_size = EC_READ_U16(cat_word + 1); - - switch (cat_type) { - case 0x000A: - if (ec_slave_fetch_strings(slave, (uint8_t *) (cat_word + 2))) - goto end; - break; - case 0x001E: - ec_slave_fetch_general(slave, (uint8_t *) (cat_word + 2)); - break; - case 0x0028: - break; - case 0x0029: - if (ec_slave_fetch_sync(slave, (uint8_t *) (cat_word + 2), - cat_size)) - goto end; - break; - case 0x0032: - if (ec_slave_fetch_pdo(slave, (uint8_t *) (cat_word + 2), - cat_size, EC_TX_PDO)) - goto end; - break; - case 0x0033: - if (ec_slave_fetch_pdo(slave, (uint8_t *) (cat_word + 2), - cat_size, EC_RX_PDO)) - goto end; - break; - default: - 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_slave_state_end; - return; - -end: - EC_ERR("Failed to analyze category data.\n"); - fsm->slave->error_flag = 1; - fsm->slave_state = ec_fsm_slave_state_error; -} - -/****************************************************************************** - * slave configuration 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; - - 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->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 - 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 - 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); - - 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_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; - - 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 sync managers on slave %i.\n", - slave->ring_position); - return; - } - - 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; - - if (!slave->base_fmmu_count) { // skip FMMU configuration - ec_fsm_slaveconf_enter_sdoconf(fsm); - return; - } - - // configure FMMUs - 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); - for (j = 0; j < slave->fmmu_count; j++) { - ec_fmmu_config(&slave->fmmus[j], slave, - datagram->data + EC_FMMU_SIZE * j); - } - - ec_master_queue_datagram(master, datagram); - 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; - - 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 set FMMUs on slave %i.\n", - fsm->slave->ring_position); - return; - } - - // No CoE configuration to be applied? Jump to SAVEOP state. - if (list_empty(&slave->sdo_confs)) { // skip SDO configuration - 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_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); - 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 - 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_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_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_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 - - 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 -} - -/****************************************************************************** - * Common state functions - *****************************************************************************/ - -/** - State: ERROR. -*/ - -void ec_fsm_slave_state_error(ec_fsm_t *fsm /**< finite state machine */) -{ -} - -/*****************************************************************************/ - -/** - State: END. -*/ - -void ec_fsm_slave_state_end(ec_fsm_t *fsm /**< finite state machine */) -{ -} - -/*****************************************************************************/ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm.h --- a/master/fsm.h Tue Feb 13 13:36:31 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +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 finite state machines. -*/ - -/*****************************************************************************/ - -#ifndef __EC_STATES__ -#define __EC_STATES__ - -#include "globals.h" -#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" - -/*****************************************************************************/ - -typedef struct ec_fsm ec_fsm_t; /**< \see ec_fsm */ - -/** - Finite state machine of an EtherCAT master. -*/ - -struct ec_fsm -{ - ec_master_t *master; /**< master the FSM runs on */ - ec_slave_t *slave; /**< slave the FSM runs on */ - ec_datagram_t datagram; /**< datagram used in the state machine */ - - void (*master_state)(ec_fsm_t *); /**< master state function */ - 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 */ - - 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 *); - -int ec_fsm_exec(ec_fsm_t *); -int ec_fsm_running(ec_fsm_t *); -int ec_fsm_success(ec_fsm_t *); - -// TODO: layout slave state machines - -/** \cond */ -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 *); -/** \endcond */ - -/*****************************************************************************/ - -#endif diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_change.c --- a/master/fsm_change.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/fsm_change.c Tue Feb 13 13:42:37 2007 +0000 @@ -159,6 +159,7 @@ 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->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_change_state_check; } @@ -174,9 +175,14 @@ ec_datagram_t *datagram = fsm->datagram; ec_slave_t *slave = fsm->slave; + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + if (datagram->state != EC_DATAGRAM_RECEIVED) { fsm->state = ec_fsm_change_state_error; - EC_ERR("Failed to send state datagram to slave %i!\n", + EC_ERR("Failed to receive state datagram from slave %i!\n", fsm->slave->ring_position); return; } @@ -200,6 +206,7 @@ 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->retries = EC_FSM_RETRIES; return; } @@ -208,6 +215,7 @@ // read AL status from slave ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2); ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_change_state_status; } @@ -223,8 +231,19 @@ ec_datagram_t *datagram = fsm->datagram; ec_slave_t *slave = fsm->slave; - if (datagram->state != EC_DATAGRAM_RECEIVED - || datagram->working_counter != 1) { + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_change_state_error; + EC_ERR("Failed to receive state checking datagram from slave %i.\n", + slave->ring_position); + return; + } + + if (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; @@ -271,6 +290,7 @@ // fetch AL status error code ec_datagram_nprd(datagram, slave->station_address, 0x0134, 2); ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_change_state_code; return; } @@ -291,6 +311,7 @@ // no timeout yet. check again ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2); ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; } /*****************************************************************************/ @@ -346,8 +367,19 @@ uint32_t code; const ec_code_msg_t *al_msg; - if (datagram->state != EC_DATAGRAM_RECEIVED - || datagram->working_counter != 1) { + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_change_state_error; + EC_ERR("Failed to receive AL status code datagram from slave %i.\n", + fsm->slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { EC_WARN("Reception of AL status code datagram failed.\n"); } else { @@ -382,6 +414,7 @@ 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->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_change_state_ack; } @@ -396,10 +429,22 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_change_state_error; + EC_ERR("Failed to receive state ack datagram for slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_change_state_error; + EC_ERR("Reception of state ack datagram failed - slave %i did not" + " respond.\n", slave->ring_position); return; } @@ -408,6 +453,7 @@ // read new AL status ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2); ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_change_state_check_ack; } @@ -423,10 +469,22 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_change_state_error; + EC_ERR("Failed to receive state ack check datagram from slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_change_state_error; + EC_ERR("Reception of state ack check datagram failed - slave %i did" + " not respond.\n", slave->ring_position); return; } @@ -464,6 +522,7 @@ // reread new AL status ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2); ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; } /*****************************************************************************/ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_change.h --- a/master/fsm_change.h Tue Feb 13 13:36:31 2007 +0000 +++ b/master/fsm_change.h Tue Feb 13 13:42:37 2007 +0000 @@ -70,6 +70,7 @@ { ec_slave_t *slave; /**< slave the FSM runs on */ ec_datagram_t *datagram; /**< datagram used in the state machine */ + unsigned int retries; /**< retries upon datagram timeout */ void (*state)(ec_fsm_change_t *); /**< slave state change state function */ ec_fsm_change_mode_t mode; /**< full state change, or ack only. */ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_coe.c --- a/master/fsm_coe.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/fsm_coe.c Tue Feb 13 13:42:37 2007 +0000 @@ -261,6 +261,7 @@ EC_WRITE_U16(data + 6, 0x0001); // deliver all SDOs! ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_request; } @@ -268,6 +269,7 @@ /** CoE state: DICT REQUEST. + \todo Timeout behavior */ void ec_fsm_coe_dict_request(ec_fsm_coe_t *fsm /**< finite state machine */) @@ -275,11 +277,23 @@ 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); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: request again? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE dictionary request datagram for" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE dictionary request failed - slave %i did" + " not respond.\n", slave->ring_position); return; } @@ -287,6 +301,7 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_check; } @@ -301,10 +316,22 @@ 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", + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE mailbox check datagram for slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE mailbox check datagram failed - slave %i did" + " not respond.\n", slave->ring_position); return; } @@ -320,12 +347,14 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; return; } // Fetch response ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_response; } @@ -333,6 +362,7 @@ /** CoE state: DICT RESPONSE. + \todo Timeout behavior */ void ec_fsm_coe_dict_response(ec_fsm_coe_t *fsm /**< finite state machine */) @@ -345,11 +375,23 @@ 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); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: request again? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE dictionary response datagram for" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE dictionary response failed - slave %i did" + " not respond.\n", slave->ring_position); return; } @@ -420,6 +462,7 @@ 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->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_check; return; } @@ -445,6 +488,7 @@ EC_WRITE_U16(data + 6, fsm->sdo->index); // SDO index ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_desc_request; } @@ -452,6 +496,7 @@ /** CoE state: DICT DESC REQUEST. + \todo Timeout behavior */ void ec_fsm_coe_dict_desc_request(ec_fsm_coe_t *fsm /**< finite state machine */) @@ -459,11 +504,23 @@ 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); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: check for response first? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE SDO description request datagram for" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE SDO description request failed - slave %i did" + " not respond.\n", slave->ring_position); return; } @@ -471,6 +528,7 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_desc_check; } @@ -485,14 +543,25 @@ 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", + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE mailbox check datagram from slave %i.\n", slave->ring_position); return; } + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE mailbox check datagram failed - slave %i did" + " not respond.\n", slave->ring_position); + return; + } + if (!ec_slave_mbox_check(datagram)) { if (datagram->cycles_received - fsm->cycles_start >= (cycles_t) 100 * cpu_khz) { @@ -504,12 +573,14 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; return; } // Fetch response ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_desc_response; } @@ -517,6 +588,7 @@ /** CoE state: DICT DESC RESPONSE. + \todo Timeout behavior */ void ec_fsm_coe_dict_desc_response(ec_fsm_coe_t *fsm @@ -528,11 +600,23 @@ 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); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: request again? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE SDO description response datagram from" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE SDO description response failed - slave %i" + " did not respond.\n", slave->ring_position); return; } @@ -615,6 +699,7 @@ EC_WRITE_U8 (data + 9, 0x00); // value info (no values) ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_entry_request; } @@ -622,6 +707,7 @@ /** CoE state: DICT ENTRY REQUEST. + \todo Timeout behavior */ void ec_fsm_coe_dict_entry_request(ec_fsm_coe_t *fsm @@ -630,18 +716,31 @@ 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); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: check for response first? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE SDO entry request datagram for" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE SDO entry request failed - slave %i did" + " not respond.\n", slave->ring_position); return; } fsm->cycles_start = datagram->cycles_sent; - ec_slave_mbox_prepare_check(slave, datagram); // can not fail. + ec_slave_mbox_prepare_check(slave, datagram); // can not fail ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_entry_check; } @@ -657,14 +756,25 @@ 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", + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE mailbox check datagram from slave %i.\n", slave->ring_position); return; } + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE mailbox check datagram failed - slave %i did" + " not respond.\n", slave->ring_position); + return; + } + if (!ec_slave_mbox_check(datagram)) { if (datagram->cycles_received - fsm->cycles_start >= (cycles_t) 100 * cpu_khz) { @@ -676,12 +786,14 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; return; } // Fetch response ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_entry_response; } @@ -689,6 +801,7 @@ /** CoE state: DICT ENTRY RESPONSE. + \todo Timeout behavior */ void ec_fsm_coe_dict_entry_response(ec_fsm_coe_t *fsm @@ -701,11 +814,23 @@ 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); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: request again? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE SDO description response datagram from" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE SDO description response failed - slave %i" + " did not respond.\n", slave->ring_position); return; } @@ -799,6 +924,7 @@ EC_WRITE_U8 (data + 9, 0x00); // value info (no values) ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_entry_request; return; } @@ -819,6 +945,7 @@ EC_WRITE_U16(data + 6, fsm->sdo->index); // SDO index ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_dict_desc_request; return; } @@ -866,6 +993,7 @@ memcpy(data + 10, sdodata->data, sdodata->size); ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_down_request; } @@ -873,6 +1001,7 @@ /** CoE state: DOWN REQUEST. + \todo Timeout behavior */ void ec_fsm_coe_down_request(ec_fsm_coe_t *fsm /**< finite state machine */) @@ -880,10 +1009,23 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: check for response first? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE download request datagram for" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE download request failed - slave %i did not" + " respond.\n", slave->ring_position); return; } @@ -891,6 +1033,7 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_down_check; } @@ -905,10 +1048,22 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE mailbox check datagram for slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE mailbox check datagram failed - slave %i did" + " not respond.\n", slave->ring_position); return; } @@ -923,12 +1078,14 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; return; } // Fetch response ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_down_response; } @@ -936,6 +1093,7 @@ /** CoE state: DOWN RESPONSE. + \todo Timeout behavior */ void ec_fsm_coe_down_response(ec_fsm_coe_t *fsm /**< finite state machine */) @@ -946,10 +1104,23 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: request again? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE download response datagram from" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE download response failed - slave %i did not" + " respond.\n", slave->ring_position); return; } @@ -1040,6 +1211,7 @@ } ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_up_request; } @@ -1047,6 +1219,7 @@ /** CoE state: UP REQUEST. + \todo Timeout behavior */ void ec_fsm_coe_up_request(ec_fsm_coe_t *fsm /**< finite state machine */) @@ -1054,10 +1227,23 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: check for response first? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE upload request for slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE upload request failed - slave %i did not" + " respond.\n", slave->ring_position); return; } @@ -1065,6 +1251,7 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_up_check; } @@ -1079,10 +1266,22 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE mailbox check datagram from slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE mailbox check datagram failed - slave %i did" + " not respond.\n", slave->ring_position); return; } @@ -1097,12 +1296,14 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; return; } // Fetch response ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_up_response; } @@ -1110,6 +1311,7 @@ /** CoE state: UP RESPONSE. + \todo Timeout behavior */ void ec_fsm_coe_up_response(ec_fsm_coe_t *fsm /**< finite state machine */) @@ -1125,10 +1327,23 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: request again? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE upload response datagram for" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE upload response failed - slave %i did not" + " respond.\n", slave->ring_position); return; } @@ -1239,6 +1454,7 @@ } ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_up_seg_request; return; } @@ -1251,6 +1467,7 @@ /** CoE state: UP REQUEST. + \todo Timeout behavior */ void ec_fsm_coe_up_seg_request(ec_fsm_coe_t *fsm /**< finite state machine */) @@ -1258,10 +1475,23 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: check for response first? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE upload segment request datagram for" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE upload segment request failed - slave %i did" + " not respond.\n", slave->ring_position); return; } @@ -1269,6 +1499,7 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_up_seg_check; } @@ -1283,10 +1514,22 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE mailbox check datagram for slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE mailbox check datagram failed - slave %i did" + " not respond.\n", slave->ring_position); return; } @@ -1301,12 +1544,14 @@ ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; return; } // Fetch response ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_up_seg_response; } @@ -1314,6 +1559,7 @@ /** CoE state: UP RESPONSE. + \todo Timeout behavior */ void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *fsm /**< finite state machine */) @@ -1329,10 +1575,23 @@ 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"); + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + // FIXME: request again? + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Failed to receive CoE upload segment response datagram for" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_coe_error; + EC_ERR("Reception of CoE upload segment response failed - slave %i" + " did not respond.\n", slave->ring_position); return; } @@ -1410,6 +1669,7 @@ } ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_coe_up_seg_request; return; } diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_coe.h --- a/master/fsm_coe.h Tue Feb 13 13:36:31 2007 +0000 +++ b/master/fsm_coe.h Tue Feb 13 13:42:37 2007 +0000 @@ -59,6 +59,7 @@ { ec_slave_t *slave; /**< slave the FSM runs on */ ec_datagram_t *datagram; /**< datagram used in the state machine */ + unsigned int retries; /**< retries upon datagram timeout */ void (*state)(ec_fsm_coe_t *); /**< CoE state function */ ec_sdo_data_t *sdodata; /**< input/output: SDO data object */ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_master.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/fsm_master.c Tue Feb 13 13:42:37 2007 +0000 @@ -0,0 +1,875 @@ +/****************************************************************************** + * + * $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 finite state machines. +*/ + +/*****************************************************************************/ + +#include "globals.h" +#include "master.h" +#include "mailbox.h" +#include "fsm_master.h" + +/*****************************************************************************/ + +void ec_fsm_master_state_start(ec_fsm_master_t *); +void ec_fsm_master_state_broadcast(ec_fsm_master_t *); +void ec_fsm_master_state_read_states(ec_fsm_master_t *); +void ec_fsm_master_state_acknowledge(ec_fsm_master_t *); +void ec_fsm_master_state_validate_vendor(ec_fsm_master_t *); +void ec_fsm_master_state_validate_product(ec_fsm_master_t *); +void ec_fsm_master_state_rewrite_addresses(ec_fsm_master_t *); +void ec_fsm_master_state_configure_slave(ec_fsm_master_t *); +void ec_fsm_master_state_scan_slaves(ec_fsm_master_t *); +void ec_fsm_master_state_write_eeprom(ec_fsm_master_t *); +void ec_fsm_master_state_sdodict(ec_fsm_master_t *); +void ec_fsm_master_state_sdo_request(ec_fsm_master_t *); +void ec_fsm_master_state_end(ec_fsm_master_t *); +void ec_fsm_master_state_error(ec_fsm_master_t *); + +/*****************************************************************************/ + +/** + Constructor. +*/ + +void ec_fsm_master_init(ec_fsm_master_t *fsm, /**< master state machine */ + ec_master_t *master, /**< EtherCAT master */ + ec_datagram_t *datagram /**< datagram object to use */ + ) +{ + fsm->master = master; + fsm->datagram = datagram; + fsm->state = ec_fsm_master_state_start; + fsm->slaves_responding = 0; + fsm->topology_change_pending = 0; + fsm->slave_states = EC_SLAVE_STATE_UNKNOWN; + fsm->validate = 0; + + // init sub-state-machines + ec_fsm_slave_init(&fsm->fsm_slave, fsm->datagram); + 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); +} + +/*****************************************************************************/ + +/** + Destructor. +*/ + +void ec_fsm_master_clear(ec_fsm_master_t *fsm /**< master state machine */) +{ + // clear sub-state machines + ec_fsm_slave_clear(&fsm->fsm_slave); + ec_fsm_sii_clear(&fsm->fsm_sii); + ec_fsm_change_clear(&fsm->fsm_change); + ec_fsm_coe_clear(&fsm->fsm_coe); +} + +/*****************************************************************************/ + +/** + Executes the current state of the state machine. + If the state machine's datagram is not sent or received yet, the execution + of the state machine is delayed to the next cycle. + \return false, if state machine has terminated +*/ + +int ec_fsm_master_exec(ec_fsm_master_t *fsm /**< master state machine */) +{ + if (fsm->datagram->state == EC_DATAGRAM_SENT + || fsm->datagram->state == EC_DATAGRAM_QUEUED) { + // datagram was not sent or received yet. + return ec_fsm_master_running(fsm); + } + + fsm->state(fsm); + return ec_fsm_master_running(fsm); +} + +/*****************************************************************************/ + +/** + \return false, if state machine has terminated +*/ + +int ec_fsm_master_running(ec_fsm_master_t *fsm /**< master state machine */) +{ + return fsm->state != ec_fsm_master_state_end + && fsm->state != ec_fsm_master_state_error; +} + +/*****************************************************************************/ + +/** + \return true, if the master state machine terminated gracefully +*/ + +int ec_fsm_master_success(ec_fsm_master_t *fsm /**< master state machine */) +{ + return fsm->state == ec_fsm_master_state_end; +} + +/****************************************************************************** + * operation/idle state machine + *****************************************************************************/ + +/** + Master state: START. + Starts with getting slave count and slave states. +*/ + +void ec_fsm_master_state_start(ec_fsm_master_t *fsm) +{ + ec_datagram_brd(fsm->datagram, 0x0130, 2); + ec_master_queue_datagram(fsm->master, fsm->datagram); + fsm->state = ec_fsm_master_state_broadcast; +} + +/*****************************************************************************/ + +/** + Master state: BROADCAST. + Processes the broadcast read slave count and slaves states. +*/ + +void ec_fsm_master_state_broadcast(ec_fsm_master_t *fsm /**< master 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_TIMED_OUT) { + // always retry + ec_master_queue_datagram(fsm->master, fsm->datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { // EC_DATAGRAM_ERROR + // link is down + fsm->slaves_responding = 0; + list_for_each_entry(slave, &master->slaves, list) { + slave->online = 0; + } + fsm->state = ec_fsm_master_state_error; + return; + } + + // bus topology change? + if (datagram->working_counter != fsm->slaves_responding) { + fsm->topology_change_pending = 1; + fsm->slaves_responding = datagram->working_counter; + + EC_INFO("%i slave%s responding.\n", + fsm->slaves_responding, + fsm->slaves_responding == 1 ? "" : "s"); + + if (master->mode == EC_MASTER_MODE_OPERATION) { + if (fsm->slaves_responding == master->slave_count) { + fsm->validate = 1; // start validation later + } + else { + EC_WARN("Invalid slave count. Bus in tainted state.\n"); + } + } + } + + // slave states changed? + if (EC_READ_U8(datagram->data) != fsm->slave_states) { + char states[EC_STATE_STRING_SIZE]; + fsm->slave_states = EC_READ_U8(datagram->data); + ec_state_string(fsm->slave_states, states); + EC_INFO("Slave states: %s.\n", states); + } + + // topology change in idle mode: clear all slaves and scan the bus + if (fsm->topology_change_pending && + master->mode == EC_MASTER_MODE_IDLE) { + fsm->topology_change_pending = 0; + + ec_master_eoe_stop(master); + ec_master_destroy_slaves(master); + + master->slave_count = datagram->working_counter; + + if (!master->slave_count) { + // no slaves present -> finish state machine. + fsm->state = ec_fsm_master_state_end; + return; + } + + // init slaves + for (i = 0; i < master->slave_count; i++) { + if (!(slave = (ec_slave_t *) kmalloc(sizeof(ec_slave_t), + GFP_ATOMIC))) { + EC_ERR("Failed to allocate slave %i!\n", i); + ec_master_destroy_slaves(master); + fsm->state = ec_fsm_master_state_error; + return; + } + + if (ec_slave_init(slave, master, i, i + 1)) { + // freeing of "slave" already done + ec_master_destroy_slaves(master); + fsm->state = ec_fsm_master_state_error; + 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); + ec_fsm_slave_start_scan(&fsm->fsm_slave, fsm->slave); + ec_fsm_slave_exec(&fsm->fsm_slave); // execute immediately + fsm->state = ec_fsm_master_state_scan_slaves; + return; + } + + // fetch state from each slave + fsm->slave = list_entry(master->slaves.next, ec_slave_t, list); + ec_datagram_nprd(fsm->datagram, fsm->slave->station_address, 0x0130, 2); + ec_master_queue_datagram(master, fsm->datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_master_state_read_states; +} + +/*****************************************************************************/ + +/** + Master action: PROC_STATES. + Processes the slave states. +*/ + +void ec_fsm_master_action_process_states(ec_fsm_master_t *fsm + /**< master state machine */ + ) +{ + ec_master_t *master = fsm->master; + ec_slave_t *slave; + 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 + && slave->self_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->self_configured) { + EC_DBG("Reconfiguring slave %i (%s).\n", + slave->ring_position, old_state); + } + } + + fsm->slave = slave; + ec_fsm_slave_start_conf(&fsm->fsm_slave, slave); + ec_fsm_slave_exec(&fsm->fsm_slave); // execute immediately + fsm->state = ec_fsm_master_state_configure_slave; + return; + } + + // Check, if EoE processing has to be started + ec_master_eoe_start(master); + + if (master->mode == EC_MASTER_MODE_IDLE) { + + // 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->state = ec_fsm_master_state_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->state = ec_fsm_master_state_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; + + if (!slave->online || slave->error_flag) { + kfree(slave->new_eeprom_data); + slave->new_eeprom_data = NULL; + EC_ERR("Discarding EEPROM data, slave %i not ready.\n", + slave->ring_position); + continue; + } + + // 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; + ec_fsm_sii_write(&fsm->fsm_sii, slave, fsm->sii_offset, + slave->new_eeprom_data, EC_FSM_SII_NODE); + fsm->state = ec_fsm_master_state_write_eeprom; + fsm->state(fsm); // execute immediately + return; + } + } + + fsm->state = ec_fsm_master_state_end; +} + +/*****************************************************************************/ + +/** + Master action: Get state of next slave. +*/ + +void ec_fsm_master_action_next_slave_state(ec_fsm_master_t *fsm + /**< master state machine */) +{ + ec_master_t *master = fsm->master; + ec_slave_t *slave = fsm->slave; + + // is there another slave to query? + if (slave->list.next != &master->slaves) { + // process next slave + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + ec_datagram_nprd(fsm->datagram, fsm->slave->station_address, + 0x0130, 2); + ec_master_queue_datagram(master, fsm->datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_master_state_read_states; + return; + } + + // all slave states read + + // check, if a bus validation has to be done + if (fsm->validate) { + fsm->validate = 0; + list_for_each_entry(slave, &master->slaves, list) { + if (slave->online) continue; + + // At least one slave is offline. validate! + EC_INFO("Validating bus.\n"); + fsm->slave = list_entry(master->slaves.next, ec_slave_t, list); + fsm->state = ec_fsm_master_state_validate_vendor; + ec_fsm_sii_read(&fsm->fsm_sii, slave, 0x0008, EC_FSM_SII_POSITION); + ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately + return; + } + } + + ec_fsm_master_action_process_states(fsm); +} + +/*****************************************************************************/ + +/** + Master state: READ STATES. + Fetches the AL- and online state of a slave. +*/ + +void ec_fsm_master_state_read_states(ec_fsm_master_t *fsm /**< master state machine */) +{ + ec_slave_t *slave = fsm->slave; + ec_datagram_t *datagram = fsm->datagram; + uint8_t new_state; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->master, fsm->datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + EC_ERR("Failed to receive AL state datagram for slave %i!\n", + slave->ring_position); + fsm->state = ec_fsm_master_state_error; + return; + } + + // did the slave not respond to its station address? + if (datagram->working_counter != 1) { + if (slave->online) { + slave->online = 0; + if (slave->master->debug_level) + EC_DBG("Slave %i: offline.\n", slave->ring_position); + } + ec_fsm_master_action_next_slave_state(fsm); + return; + } + + // slave responded + new_state = EC_READ_U8(datagram->data); + if (!slave->online) { // slave was offline before + slave->online = 1; + slave->error_flag = 0; // clear error flag + slave->current_state = new_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) { + 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->state = ec_fsm_master_state_acknowledge; + return; + } + + ec_fsm_master_action_next_slave_state(fsm); +} + +/*****************************************************************************/ + +/** + Master state: ACKNOWLEDGE +*/ + +void ec_fsm_master_state_acknowledge(ec_fsm_master_t *fsm /**< master 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->state = ec_fsm_master_state_error; + return; + } + + ec_fsm_master_action_next_slave_state(fsm); +} + +/*****************************************************************************/ + +/** + Master state: VALIDATE_VENDOR. + Validates the vendor ID of a slave. +*/ + +void ec_fsm_master_state_validate_vendor(ec_fsm_master_t *fsm /**< master state machine */) +{ + ec_slave_t *slave = fsm->slave; + + 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->state = ec_fsm_master_state_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->state = ec_fsm_master_state_error; + return; + } + + // vendor ID is ok. check product code. + fsm->state = ec_fsm_master_state_validate_product; + ec_fsm_sii_read(&fsm->fsm_sii, slave, 0x000A, EC_FSM_SII_POSITION); + ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately +} + +/*****************************************************************************/ + +/** + Master action: ADDRESS. + Looks for slave, that have lost their configuration and writes + their station address, so that they can be reconfigured later. +*/ + +void ec_fsm_master_action_addresses(ec_fsm_master_t *fsm /**< master state machine */) +{ + ec_datagram_t *datagram = fsm->datagram; + + while (fsm->slave->online) { + if (fsm->slave->list.next == &fsm->master->slaves) { // last slave? + fsm->state = ec_fsm_master_state_start; + fsm->state(fsm); // execute immediately + return; + } + // check next slave + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + } + + 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); + EC_WRITE_U16(datagram->data, fsm->slave->station_address); + ec_master_queue_datagram(fsm->master, datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_master_state_rewrite_addresses; +} + +/*****************************************************************************/ + +/** + Master state: VALIDATE_PRODUCT. + Validates the product ID of a slave. +*/ + +void ec_fsm_master_state_validate_product(ec_fsm_master_t *fsm /**< master state machine */) +{ + ec_slave_t *slave = fsm->slave; + + 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->state = ec_fsm_master_state_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->fsm_sii.value)); + fsm->state = ec_fsm_master_state_error; + return; + } + + // have all states been validated? + if (slave->list.next == &fsm->master->slaves) { + fsm->slave = list_entry(fsm->master->slaves.next, ec_slave_t, list); + // start writing addresses to offline slaves + ec_fsm_master_action_addresses(fsm); + return; + } + + // validate next slave + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + fsm->state = ec_fsm_master_state_validate_vendor; + 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. +*/ + +void ec_fsm_master_state_rewrite_addresses(ec_fsm_master_t *fsm + /**< master state machine */ + ) +{ + ec_slave_t *slave = fsm->slave; + ec_datagram_t *datagram = fsm->datagram; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->master, fsm->datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + EC_ERR("Failed to receive address datagram for slave %i.\n", + slave->ring_position); + fsm->state = ec_fsm_master_state_error; + return; + } + + if (datagram->working_counter != 1) { + EC_ERR("Failed to write station address - slave %i did not respond.\n", + slave->ring_position); + fsm->state = ec_fsm_master_state_error; + return; + } + + if (fsm->slave->list.next == &fsm->master->slaves) { // last slave? + fsm->state = ec_fsm_master_state_start; + fsm->state(fsm); // execute immediately + return; + } + + // check next slave + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + // Write new station address to slave + ec_fsm_master_action_addresses(fsm); +} + +/*****************************************************************************/ + +/** + Master state: SCAN SLAVES. + Executes the sub-statemachine for the scanning of a slave. +*/ + +void ec_fsm_master_state_scan_slaves(ec_fsm_master_t *fsm /**< master state machine */) +{ + ec_master_t *master = fsm->master; + ec_slave_t *slave; + + if (ec_fsm_slave_exec(&fsm->fsm_slave)) // execute slave state machine + return; + + // another slave to fetch? + if (fsm->slave->list.next != &master->slaves) { + fsm->slave = list_entry(fsm->slave->list.next, ec_slave_t, list); + ec_fsm_slave_start_scan(&fsm->fsm_slave, fsm->slave); + ec_fsm_slave_exec(&fsm->fsm_slave); // execute immediately + return; + } + + EC_INFO("Bus scanning completed.\n"); + + ec_master_calc_addressing(master); + + // set initial states of all slaves to PREOP to make mailbox + // communication possible + list_for_each_entry(slave, &master->slaves, list) { + ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); + } + + fsm->state = ec_fsm_master_state_end; +} + +/*****************************************************************************/ + +/** + Master state: CONFIGURE SLAVES. + Starts configuring a slave. +*/ + +void ec_fsm_master_state_configure_slave(ec_fsm_master_t *fsm + /**< master state machine */ + ) +{ + if (ec_fsm_slave_exec(&fsm->fsm_slave)) // execute slave's state machine + return; + + ec_fsm_master_action_process_states(fsm); +} + +/*****************************************************************************/ + +/** + Master state: WRITE EEPROM. +*/ + +void ec_fsm_master_state_write_eeprom(ec_fsm_master_t *fsm /**< master state machine */) +{ + ec_slave_t *slave = fsm->slave; + + 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->state = ec_fsm_master_state_error; + return; + } + + fsm->sii_offset++; + if (fsm->sii_offset < slave->new_eeprom_size) { + 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; + } + + // finished writing EEPROM + EC_INFO("Finished writing EEPROM of slave %i.\n", slave->ring_position); + kfree(slave->new_eeprom_data); + slave->new_eeprom_data = NULL; + + // TODO: Evaluate new EEPROM contents! + + // restart master state machine. + fsm->state = ec_fsm_master_state_start; + fsm->state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Master state: SDODICT. +*/ + +void ec_fsm_master_state_sdodict(ec_fsm_master_t *fsm /**< master 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->state = ec_fsm_master_state_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->state = ec_fsm_master_state_start; + fsm->state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + Master state: SDO REQUEST. +*/ + +void ec_fsm_master_state_sdo_request(ec_fsm_master_t *fsm /**< master 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->state = ec_fsm_master_state_error; + return; + } + + // SDO dictionary fetching finished + + request->return_code = 1; + master->sdo_seq_master++; + + // restart master state machine. + fsm->state = ec_fsm_master_state_start; + fsm->state(fsm); // execute immediately +} + +/*****************************************************************************/ + +/** + State: ERROR. +*/ + +void ec_fsm_master_state_error( + ec_fsm_master_t *fsm /**< master state machine */ + ) +{ + fsm->state = ec_fsm_master_state_start; +} + +/*****************************************************************************/ + +/** + State: END. +*/ + +void ec_fsm_master_state_end(ec_fsm_master_t *fsm /**< master state machine */) +{ + fsm->state = ec_fsm_master_state_start; +} + +/*****************************************************************************/ + diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_master.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/fsm_master.h Tue Feb 13 13:42:37 2007 +0000 @@ -0,0 +1,92 @@ +/****************************************************************************** + * + * $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 finite state machines. +*/ + +/*****************************************************************************/ + +#ifndef __EC_FSM_MASTER__ +#define __EC_FSM_MASTER__ + +#include "globals.h" +#include "../include/ecrt.h" +#include "datagram.h" +#include "slave.h" +#include "canopen.h" + +#include "fsm_slave.h" + +/*****************************************************************************/ + +typedef struct ec_fsm_master ec_fsm_master_t; /**< \see ec_fsm_master */ + +/** + Finite state machine of an EtherCAT master. +*/ + +struct ec_fsm_master +{ + ec_master_t *master; /**< master the FSM runs on */ + ec_datagram_t *datagram; /**< datagram used in the state machine */ + unsigned int retries; /**< retries on datagram timeout. */ + + void (*state)(ec_fsm_master_t *); /**< master state function */ + unsigned int slaves_responding; /**< number of responding slaves */ + unsigned int topology_change_pending; /**< bus topology changed */ + ec_slave_state_t slave_states; /**< states of responding slaves */ + unsigned int validate; /**< non-zero, if validation to do */ + ec_slave_t *slave; /**< current slave */ + ec_sdo_request_t *sdo_request; /**< SDO request to process */ + uint16_t sii_offset; + + ec_fsm_slave_t fsm_slave; /**< slave state machine */ + 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 */ +}; + +/*****************************************************************************/ + +void ec_fsm_master_init(ec_fsm_master_t *, ec_master_t *, ec_datagram_t *); +void ec_fsm_master_clear(ec_fsm_master_t *); + +int ec_fsm_master_exec(ec_fsm_master_t *); +int ec_fsm_master_running(ec_fsm_master_t *); +int ec_fsm_master_success(ec_fsm_master_t *); + +/*****************************************************************************/ + +#endif diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_sii.c --- a/master/fsm_sii.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/fsm_sii.c Tue Feb 13 13:42:37 2007 +0000 @@ -169,6 +169,7 @@ 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->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_sii_read_check; } @@ -183,10 +184,22 @@ { 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; + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_sii_error; + EC_ERR("Failed to receive SII read datagram from slave %i.\n", + fsm->slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_sii_error; + EC_ERR("Reception of SII read datagram failed - slave %i did not" + " respond.\n", fsm->slave->ring_position); return; } @@ -203,6 +216,7 @@ break; } ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_sii_read_fetch; } @@ -217,10 +231,22 @@ { 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; + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_sii_error; + EC_ERR("Failed to receive SII check/fetch datagram from slave %i.\n", + fsm->slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_sii_error; + EC_ERR("Reception of SII check/fetch datagram failed - slave %i did" + " not respond\n", fsm->slave->ring_position); return; } @@ -254,6 +280,7 @@ break; } ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; return; } @@ -288,6 +315,7 @@ EC_WRITE_U32(datagram->data + 2, fsm->offset); memcpy(datagram->data + 6, fsm->value, 2); ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_sii_write_check; } @@ -301,10 +329,22 @@ { 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; + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_sii_error; + EC_ERR("Failed to receive SII write datagram for slave %i.\n", + fsm->slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_sii_error; + EC_ERR("Reception of SII write datagram failed - slave %i did not" + " respond.\n", fsm->slave->ring_position); return; } @@ -314,6 +354,7 @@ // issue check/fetch datagram ec_datagram_nprd(datagram, fsm->slave->station_address, 0x502, 2); ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_sii_write_check2; } @@ -327,10 +368,22 @@ { 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; + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_sii_error; + EC_ERR("Failed to receive SII write check datagram from slave %i.\n", + fsm->slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->state = ec_fsm_sii_error; + EC_ERR("Reception of SII write check datagram failed - slave %i did" + " not respond.\n", fsm->slave->ring_position); return; } @@ -348,6 +401,7 @@ // issue check/fetch datagram again ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; return; } diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_sii.h --- a/master/fsm_sii.h Tue Feb 13 13:36:31 2007 +0000 +++ b/master/fsm_sii.h Tue Feb 13 13:42:37 2007 +0000 @@ -67,6 +67,7 @@ { ec_slave_t *slave; /**< slave the FSM runs on */ ec_datagram_t *datagram; /**< datagram used in the state machine */ + unsigned int retries; /**< retries upon datagram timeout */ void (*state)(ec_fsm_sii_t *); /**< SII state function */ uint16_t offset; /**< input: offset in SII */ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_slave.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/fsm_slave.c Tue Feb 13 13:42:37 2007 +0000 @@ -0,0 +1,1101 @@ +/****************************************************************************** + * + * $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 state machines. +*/ + +/*****************************************************************************/ + +#include "globals.h" +#include "master.h" +#include "mailbox.h" +#include "fsm_slave.h" + +/*****************************************************************************/ + +void ec_fsm_slave_scan_state_start(ec_fsm_slave_t *); +void ec_fsm_slave_scan_state_address(ec_fsm_slave_t *); +void ec_fsm_slave_scan_state_state(ec_fsm_slave_t *); +void ec_fsm_slave_scan_state_base(ec_fsm_slave_t *); +void ec_fsm_slave_scan_state_datalink(ec_fsm_slave_t *); +void ec_fsm_slave_scan_state_eeprom_size(ec_fsm_slave_t *); +void ec_fsm_slave_scan_state_eeprom_data(ec_fsm_slave_t *); + +void ec_fsm_slave_conf_state_start(ec_fsm_slave_t *); +void ec_fsm_slave_conf_state_init(ec_fsm_slave_t *); +void ec_fsm_slave_conf_state_clear_fmmus(ec_fsm_slave_t *); +void ec_fsm_slave_conf_state_sync(ec_fsm_slave_t *); +void ec_fsm_slave_conf_state_preop(ec_fsm_slave_t *); +void ec_fsm_slave_conf_state_sync2(ec_fsm_slave_t *); +void ec_fsm_slave_conf_state_fmmu(ec_fsm_slave_t *); +void ec_fsm_slave_conf_state_sdoconf(ec_fsm_slave_t *); +void ec_fsm_slave_conf_state_saveop(ec_fsm_slave_t *); +void ec_fsm_slave_conf_state_op(ec_fsm_slave_t *); + +void ec_fsm_slave_conf_enter_sync(ec_fsm_slave_t *); +void ec_fsm_slave_conf_enter_preop(ec_fsm_slave_t *); +void ec_fsm_slave_conf_enter_sync2(ec_fsm_slave_t *); +void ec_fsm_slave_conf_enter_fmmu(ec_fsm_slave_t *); +void ec_fsm_slave_conf_enter_sdoconf(ec_fsm_slave_t *); +void ec_fsm_slave_conf_enter_saveop(ec_fsm_slave_t *); + +void ec_fsm_slave_state_end(ec_fsm_slave_t *); +void ec_fsm_slave_state_error(ec_fsm_slave_t *); + +/*****************************************************************************/ + +/** + Constructor. +*/ + +void ec_fsm_slave_init(ec_fsm_slave_t *fsm, /**< slave state machine */ + ec_datagram_t *datagram /**< datagram structure to use */ + ) +{ + fsm->datagram = datagram; + + // 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); +} + +/*****************************************************************************/ + +/** + Destructor. +*/ + +void ec_fsm_slave_clear(ec_fsm_slave_t *fsm /**< slave 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); +} + +/*****************************************************************************/ + +/** + * Start slave scan state machine. + */ + +void ec_fsm_slave_start_scan(ec_fsm_slave_t *fsm, /**< slave state machine */ + ec_slave_t *slave /**< slave to configure */ + ) +{ + fsm->slave = slave; + fsm->state = ec_fsm_slave_scan_state_start; +} + +/*****************************************************************************/ + +/** + * Start slave configuration state machine. + */ + +void ec_fsm_slave_start_conf(ec_fsm_slave_t *fsm, /**< slave state machine */ + ec_slave_t *slave /**< slave to configure */ + ) +{ + fsm->slave = slave; + fsm->state = ec_fsm_slave_conf_state_start; +} + +/*****************************************************************************/ + +/** + \return false, if state machine has terminated +*/ + +int ec_fsm_slave_running(const ec_fsm_slave_t *fsm /**< slave state machine */) +{ + return fsm->state != ec_fsm_slave_state_end + && fsm->state != ec_fsm_slave_state_error; +} + +/*****************************************************************************/ + +/** + Executes the current state of the state machine. + If the state machine's datagram is not sent or received yet, the execution + of the state machine is delayed to the next cycle. + \return false, if state machine has terminated +*/ + +int ec_fsm_slave_exec(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + if (fsm->datagram->state == EC_DATAGRAM_SENT + || fsm->datagram->state == EC_DATAGRAM_QUEUED) { + // datagram was not sent or received yet. + return ec_fsm_slave_running(fsm); + } + + fsm->state(fsm); + return ec_fsm_slave_running(fsm); +} + +/*****************************************************************************/ + +/** + \return true, if the state machine terminated gracefully +*/ + +int ec_fsm_slave_success(const ec_fsm_slave_t *fsm /**< slave state machine */) +{ + return fsm->state == ec_fsm_slave_state_end; +} + +/****************************************************************************** + * slave scan state machine + *****************************************************************************/ + +/** + Slave scan state: START. + First state of the slave state machine. Writes the station address to the + slave, according to its ring position. +*/ + +void ec_fsm_slave_scan_state_start(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + // write station address + ec_datagram_apwr(fsm->datagram, fsm->slave->ring_position, 0x0010, 2); + EC_WRITE_U16(fsm->datagram->data, fsm->slave->station_address); + ec_master_queue_datagram(fsm->slave->master, fsm->datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_scan_state_address; +} + +/*****************************************************************************/ + +/** + Slave scan state: ADDRESS. +*/ + +void ec_fsm_slave_scan_state_address(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_datagram_t *datagram = fsm->datagram; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, fsm->datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to receive station address datagram for slave %i.\n", + fsm->slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to write station address - slave %i did not respond.\n", + fsm->slave->ring_position); + return; + } + + // Read AL state + ec_datagram_nprd(datagram, fsm->slave->station_address, 0x0130, 2); + ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_scan_state_state; +} + +/*****************************************************************************/ + +/** + Slave scan state: STATE. +*/ + +void ec_fsm_slave_scan_state_state(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to receive AL state datagram from slave %i.\n", + fsm->slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to read AL state - slave %i did not respond.\n", + fsm->slave->ring_position); + return; + } + + 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); + EC_WARN("Slave %i has state error bit set (%s)!\n", + slave->ring_position, state_str); + } + + // read base data + ec_datagram_nprd(datagram, fsm->slave->station_address, 0x0000, 6); + ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_scan_state_base; +} + +/*****************************************************************************/ + +/** + Slave scan state: BASE. +*/ + +void ec_fsm_slave_scan_state_base(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to receive base data datagram for slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to read base data - slave %i did not respond.\n", + slave->ring_position); + return; + } + + slave->base_type = EC_READ_U8 (datagram->data); + slave->base_revision = EC_READ_U8 (datagram->data + 1); + slave->base_build = EC_READ_U16(datagram->data + 2); + slave->base_fmmu_count = EC_READ_U8 (datagram->data + 4); + slave->base_sync_count = EC_READ_U8 (datagram->data + 5); + + if (slave->base_fmmu_count > EC_MAX_FMMUS) + slave->base_fmmu_count = EC_MAX_FMMUS; + + // read data link status + ec_datagram_nprd(datagram, slave->station_address, 0x0110, 2); + ec_master_queue_datagram(slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_scan_state_datalink; +} + +/*****************************************************************************/ + +/** + Slave scan state: DATALINK. +*/ + +void ec_fsm_slave_scan_state_datalink(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + uint16_t dl_status; + unsigned int i; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to receive DL status datagram from slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to read DL status - slave %i did not respond.\n", + slave->ring_position); + return; + } + + dl_status = EC_READ_U16(datagram->data); + for (i = 0; i < 4; i++) { + slave->dl_link[i] = dl_status & (1 << (4 + i)) ? 1 : 0; + slave->dl_loop[i] = dl_status & (1 << (8 + i * 2)) ? 1 : 0; + slave->dl_signal[i] = dl_status & (1 << (9 + i * 2)) ? 1 : 0; + } + + // Start fetching EEPROM size + + fsm->sii_offset = 0x0040; // first category header + ec_fsm_sii_read(&fsm->fsm_sii, slave, fsm->sii_offset, EC_FSM_SII_NODE); + fsm->state = ec_fsm_slave_scan_state_eeprom_size; + fsm->state(fsm); // execute state immediately +} + +/*****************************************************************************/ + +/** + Slave scan state: EEPROM SIZE. +*/ + +void ec_fsm_slave_scan_state_eeprom_size(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_slave_t *slave = fsm->slave; + uint16_t cat_type, cat_size; + + if (ec_fsm_sii_exec(&fsm->fsm_sii)) return; + + if (!ec_fsm_sii_success(&fsm->fsm_sii)) { + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to read EEPROM size of slave %i.\n", + slave->ring_position); + return; + } + + 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; + 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; + } + + slave->eeprom_size = (fsm->sii_offset + 1) * 2; + + if (slave->eeprom_data) { + EC_INFO("Freeing old EEPROM data on slave %i...\n", + slave->ring_position); + kfree(slave->eeprom_data); + } + + if (!(slave->eeprom_data = + (uint8_t *) kmalloc(slave->eeprom_size, GFP_ATOMIC))) { + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to allocate EEPROM data on slave %i.\n", + slave->ring_position); + return; + } + + // Start fetching EEPROM contents + + fsm->state = ec_fsm_slave_scan_state_eeprom_data; + fsm->sii_offset = 0x0000; + 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_slave_scan_state_eeprom_data(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_slave_t *slave = fsm->slave; + uint16_t *cat_word, cat_type, cat_size; + + if (ec_fsm_sii_exec(&fsm->fsm_sii)) return; + + if (!ec_fsm_sii_success(&fsm->fsm_sii)) { + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to fetch EEPROM contents of slave %i.\n", + slave->ring_position); + 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->fsm_sii.value, 4); + } + else { // copy the last word + 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; + 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; + } + + // Evaluate EEPROM contents + + slave->sii_alias = + EC_READ_U16(slave->eeprom_data + 2 * 0x0004); + slave->sii_vendor_id = + EC_READ_U32(slave->eeprom_data + 2 * 0x0008); + slave->sii_product_code = + EC_READ_U32(slave->eeprom_data + 2 * 0x000A); + slave->sii_revision_number = + EC_READ_U32(slave->eeprom_data + 2 * 0x000C); + slave->sii_serial_number = + EC_READ_U32(slave->eeprom_data + 2 * 0x000E); + slave->sii_rx_mailbox_offset = + EC_READ_U16(slave->eeprom_data + 2 * 0x0018); + slave->sii_rx_mailbox_size = + EC_READ_U16(slave->eeprom_data + 2 * 0x0019); + slave->sii_tx_mailbox_offset = + EC_READ_U16(slave->eeprom_data + 2 * 0x001A); + slave->sii_tx_mailbox_size = + EC_READ_U16(slave->eeprom_data + 2 * 0x001B); + slave->sii_mailbox_protocols = + EC_READ_U16(slave->eeprom_data + 2 * 0x001C); + + // evaluate category data + cat_word = (uint16_t *) slave->eeprom_data + 0x0040; + while (EC_READ_U16(cat_word) != 0xFFFF) { + cat_type = EC_READ_U16(cat_word) & 0x7FFF; + cat_size = EC_READ_U16(cat_word + 1); + + switch (cat_type) { + case 0x000A: + if (ec_slave_fetch_strings(slave, (uint8_t *) (cat_word + 2))) + goto end; + break; + case 0x001E: + ec_slave_fetch_general(slave, (uint8_t *) (cat_word + 2)); + break; + case 0x0028: + break; + case 0x0029: + if (ec_slave_fetch_sync(slave, (uint8_t *) (cat_word + 2), + cat_size)) + goto end; + break; + case 0x0032: + if (ec_slave_fetch_pdo(slave, (uint8_t *) (cat_word + 2), + cat_size, EC_TX_PDO)) + goto end; + break; + case 0x0033: + if (ec_slave_fetch_pdo(slave, (uint8_t *) (cat_word + 2), + cat_size, EC_RX_PDO)) + goto end; + break; + default: + if (fsm->slave->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->state = ec_fsm_slave_state_end; + return; + +end: + EC_ERR("Failed to analyze category data.\n"); + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; +} + +/****************************************************************************** + * slave configuration state machine + *****************************************************************************/ + +/** + Slave configuration state: START. +*/ + +void ec_fsm_slave_conf_state_start(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + if (fsm->slave->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->state = ec_fsm_slave_conf_state_init; +} + +/*****************************************************************************/ + +/** + Slave configuration state: INIT. +*/ + +void ec_fsm_slave_conf_state_init(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_master_t *master = fsm->slave->master; + ec_slave_t *slave = fsm->slave; + ec_datagram_t *datagram = fsm->datagram; + + if (ec_fsm_change_exec(&fsm->fsm_change)) return; + + if (!ec_fsm_change_success(&fsm->fsm_change)) { + slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + return; + } + + slave->self_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_slave_conf_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->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_conf_state_clear_fmmus; +} + +/*****************************************************************************/ + +/** + Slave configuration state: CLEAR FMMU. +*/ + +void ec_fsm_slave_conf_state_clear_fmmus(ec_fsm_slave_t *fsm + /**< slave state machine */) +{ + ec_datagram_t *datagram = fsm->datagram; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed receive FMMU clearing datagram for slave %i.\n", + fsm->slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to clear FMMUs - slave %i did not respond.\n", + fsm->slave->ring_position); + return; + } + + ec_fsm_slave_conf_enter_sync(fsm); +} + +/*****************************************************************************/ + +/** +*/ + +void ec_fsm_slave_conf_enter_sync(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_master_t *master = fsm->slave->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->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 + ec_fsm_slave_conf_enter_preop(fsm); + return; + } + + if (master->debug_level) { + EC_DBG("Configuring sync managers of slave %i.\n", + slave->ring_position); + } + + // configure sync managers + 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); + + 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_slave_sync_config(slave, &mbox_sync, + 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_slave_sync_config(slave, &mbox_sync, + 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_slave_sync_config(slave, sync, + datagram->data + EC_SYNC_SIZE * sync->index); + } + } + + ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_conf_state_sync; +} + +/*****************************************************************************/ + +/** + Slave configuration state: SYNC. +*/ + +void ec_fsm_slave_conf_state_sync(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to receive sync manager configuration datagram for" + " slave %i.\n", slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to set sync managers - slave %i did not respond.\n", + slave->ring_position); + return; + } + + ec_fsm_slave_conf_enter_preop(fsm); +} + +/*****************************************************************************/ + +/** + */ + +void ec_fsm_slave_conf_enter_preop(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + fsm->state = ec_fsm_slave_conf_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_slave_conf_state_preop(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_slave_t *slave = fsm->slave; + ec_master_t *master = fsm->slave->master; + + if (ec_fsm_change_exec(&fsm->fsm_change)) return; + + if (!ec_fsm_change_success(&fsm->fsm_change)) { + slave->error_flag = 1; + fsm->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->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_slave_conf_enter_sync2(fsm); +} + +/*****************************************************************************/ + +/** +*/ + +void ec_fsm_slave_conf_enter_sync2(ec_fsm_slave_t *fsm /**< slave 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_slave_conf_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_slave_sync_config(slave, sync, + datagram->data + EC_SYNC_SIZE * sync->index); + } + + ec_master_queue_datagram(fsm->slave->master, datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_conf_state_sync2; +} + +/*****************************************************************************/ + +/** + Slave configuration state: SYNC2. +*/ + +void ec_fsm_slave_conf_state_sync2(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to receive process data sync manager configuration" + " datagram for slave %i.\n", + slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to set process data sync managers - slave %i did not" + " respond.\n", slave->ring_position); + return; + } + + ec_fsm_slave_conf_enter_fmmu(fsm); +} + +/*****************************************************************************/ + +/** +*/ + +void ec_fsm_slave_conf_enter_fmmu(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_slave_t *slave = fsm->slave; + ec_master_t *master = slave->master; + ec_datagram_t *datagram = fsm->datagram; + unsigned int j; + + if (!slave->base_fmmu_count) { // skip FMMU configuration + ec_fsm_slave_conf_enter_sdoconf(fsm); + return; + } + + // configure FMMUs + 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); + for (j = 0; j < slave->fmmu_count; j++) { + ec_slave_fmmu_config(slave, &slave->fmmus[j], + datagram->data + EC_FMMU_SIZE * j); + } + + ec_master_queue_datagram(master, datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_conf_state_fmmu; +} + +/*****************************************************************************/ + +/** + Slave configuration state: FMMU. +*/ + +void ec_fsm_slave_conf_state_fmmu(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_datagram_t *datagram = fsm->datagram; + ec_slave_t *slave = fsm->slave; + + if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_master_queue_datagram(fsm->slave->master, datagram); + return; + } + + if (datagram->state != EC_DATAGRAM_RECEIVED) { + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to receive FMMUs datagram for slave %i.\n", + fsm->slave->ring_position); + return; + } + + if (datagram->working_counter != 1) { + fsm->slave->error_flag = 1; + fsm->state = ec_fsm_slave_state_error; + EC_ERR("Failed to set FMMUs - slave %i did not respond.\n", + fsm->slave->ring_position); + return; + } + + // No CoE configuration to be applied? Jump to SAVEOP state. + if (list_empty(&slave->sdo_confs)) { // skip SDO configuration + ec_fsm_slave_conf_enter_saveop(fsm); + return; + } + + ec_fsm_slave_conf_enter_sdoconf(fsm); +} + +/*****************************************************************************/ + +/** + */ + +void ec_fsm_slave_conf_enter_sdoconf(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_slave_t *slave = fsm->slave; + + if (list_empty(&slave->sdo_confs)) { // skip SDO configuration + ec_fsm_slave_conf_enter_saveop(fsm); + return; + } + + // start SDO configuration + fsm->state = ec_fsm_slave_conf_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_slave_conf_state_sdoconf(ec_fsm_slave_t *fsm /**< slave 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->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); + 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 + ec_fsm_slave_conf_enter_saveop(fsm); +} + +/*****************************************************************************/ + +/** + */ + +void ec_fsm_slave_conf_enter_saveop(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + fsm->state = ec_fsm_slave_conf_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_slave_conf_state_saveop(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_master_t *master = fsm->slave->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->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->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->state = ec_fsm_slave_conf_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_slave_conf_state_op(ec_fsm_slave_t *fsm /**< slave state machine */) +{ + ec_master_t *master = fsm->slave->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->state = ec_fsm_slave_state_error; + return; + } + + // slave is now in OP + + 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->state = ec_fsm_slave_state_end; // successful +} + +/****************************************************************************** + * Common state functions + *****************************************************************************/ + +/** + State: ERROR. +*/ + +void ec_fsm_slave_state_error(ec_fsm_slave_t *fsm /**< slave state machine */) +{ +} + +/*****************************************************************************/ + +/** + State: END. +*/ + +void ec_fsm_slave_state_end(ec_fsm_slave_t *fsm /**< slave state machine */) +{ +} + +/*****************************************************************************/ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/fsm_slave.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master/fsm_slave.h Tue Feb 13 13:42:37 2007 +0000 @@ -0,0 +1,88 @@ +/****************************************************************************** + * + * $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 finite state machines. +*/ + +/*****************************************************************************/ + +#ifndef __EC_FSM_SLAVE__ +#define __EC_FSM_SLAVE__ + +#include "globals.h" +#include "../include/ecrt.h" +#include "datagram.h" +#include "slave.h" +#include "fsm_sii.h" +#include "fsm_change.h" +#include "fsm_coe.h" + +/*****************************************************************************/ + +typedef struct ec_fsm_slave ec_fsm_slave_t; /**< \see ec_fsm_slave */ + +/** + Finite state machine of an EtherCAT slave. +*/ + +struct ec_fsm_slave +{ + ec_slave_t *slave; /**< slave the FSM runs on */ + ec_datagram_t *datagram; /**< datagram used in the state machine */ + unsigned int retries; /**< retries on datagram timeout. */ + + void (*state)(ec_fsm_slave_t *); /**< state function */ + ec_sdo_data_t *sdodata; /**< SDO configuration data */ + uint16_t sii_offset; + + 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 */ +}; + +/*****************************************************************************/ + +void ec_fsm_slave_init(ec_fsm_slave_t *, ec_datagram_t *); +void ec_fsm_slave_clear(ec_fsm_slave_t *); + +void ec_fsm_slave_start_scan(ec_fsm_slave_t *, ec_slave_t *); +void ec_fsm_slave_start_conf(ec_fsm_slave_t *, ec_slave_t *); + +int ec_fsm_slave_exec(ec_fsm_slave_t *); +int ec_fsm_slave_success(const ec_fsm_slave_t *); + +/*****************************************************************************/ + +#endif diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/globals.h --- a/master/globals.h Tue Feb 13 13:36:31 2007 +0000 +++ b/master/globals.h Tue Feb 13 13:42:37 2007 +0000 @@ -61,6 +61,9 @@ /** datagram timeout in microseconds */ #define EC_IO_TIMEOUT 500 +/** number of state machine retries on datagram timeout */ +#define EC_FSM_RETRIES 3 + /** Seconds to wait before fetching SDO dictionary after slave entered PREOP state. */ #define EC_WAIT_SDO_DICT 3 diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/mailbox.c --- a/master/mailbox.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/mailbox.c Tue Feb 13 13:42:37 2007 +0000 @@ -89,6 +89,7 @@ /** Prepares a datagram for checking the mailbox state. + \todo: Determine sync manager used for receive mailbox \return 0 in case of success, else < 0 */ @@ -96,7 +97,6 @@ ec_datagram_t *datagram /**< datagram */ ) { - // FIXME: second sync manager? if (ec_datagram_nprd(datagram, slave->station_address, 0x808, 8)) return -1; @@ -168,7 +168,8 @@ data_size = EC_READ_U16(datagram->data); if (data_size > slave->sii_tx_mailbox_size - 6) { - EC_ERR("Corrupt mailbox response detected!\n"); + EC_ERR("Corrupt mailbox response received from slave %i!\n", + slave->ring_position); ec_print_data(datagram->data, slave->sii_tx_mailbox_size); return NULL; } @@ -180,7 +181,8 @@ const ec_code_msg_t *mbox_msg; uint16_t code = EC_READ_U16(datagram->data + 8); - EC_ERR("Mailbox error response received - "); + EC_ERR("Mailbox error response received from slave %i - ", + slave->ring_position); for (mbox_msg = mbox_error_messages; mbox_msg->code; mbox_msg++) { if (mbox_msg->code != code) continue; diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/master.c --- a/master/master.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/master.c Tue Feb 13 13:42:37 2007 +0000 @@ -57,7 +57,7 @@ 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 *); +static int ec_master_thread(void *); void ec_master_eoe_run(unsigned long); void ec_master_check_sdo(unsigned long); int ec_master_measure_bus_time(ec_master_t *); @@ -133,8 +133,6 @@ 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; @@ -165,12 +163,6 @@ master->sdo_timer.data = (unsigned long) master; init_completion(&master->sdo_complete); - // create workqueue - if (!(master->workqueue = create_singlethread_workqueue("EtherCAT"))) { - EC_ERR("Failed to create master workqueue.\n"); - goto out_return; - } - // create EoE handlers for (i = 0; i < eoeif_count; i++) { if (!(eoe = (ec_eoe_t *) kmalloc(sizeof(ec_eoe_t), GFP_KERNEL))) { @@ -184,8 +176,15 @@ list_add_tail(&eoe->list, &master->eoe_handlers); } + // init state machine datagram + ec_datagram_init(&master->fsm_datagram); + if (ec_datagram_prealloc(&master->fsm_datagram, EC_MAX_DATA_SIZE)) { + EC_ERR("Failed to allocate FSM datagram.\n"); + goto out_clear_eoe; + } + // create state machine object - if (ec_fsm_init(&master->fsm, master)) goto out_clear_eoe; + ec_fsm_master_init(&master->fsm, master, &master->fsm_datagram); // init kobject and add it to the hierarchy memset(&master->kobj, 0x00, sizeof(struct kobject)); @@ -204,13 +203,12 @@ return 0; - out_clear_eoe: +out_clear_eoe: list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) { list_del(&eoe->list); ec_eoe_clear(eoe); kfree(eoe); } - destroy_workqueue(master->workqueue); out_return: return -1; } @@ -253,8 +251,8 @@ list_del_init(&datagram->queue); } - ec_fsm_clear(&master->fsm); - destroy_workqueue(master->workqueue); + ec_fsm_master_clear(&master->fsm); + ec_datagram_clear(&master->fsm_datagram); // clear EoE objects list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) { @@ -320,57 +318,121 @@ /*****************************************************************************/ /** + Internal locking callback. +*/ + +int ec_master_request_cb(void *master /**< callback data */) +{ + spin_lock(&((ec_master_t *) master)->internal_lock); + return 0; +} + +/*****************************************************************************/ + +/** + Internal unlocking callback. +*/ + +void ec_master_release_cb(void *master /**< callback data */) +{ + spin_unlock(&((ec_master_t *) master)->internal_lock); +} + +/*****************************************************************************/ + +/** + * Starts the master thread. +*/ + +int ec_master_thread_start(ec_master_t *master /**< EtherCAT master */) +{ + init_completion(&master->thread_exit); + + EC_INFO("Starting master thread.\n"); + if (!(master->thread_id = + kernel_thread(ec_master_thread, master, CLONE_KERNEL))) + return -1; + + return 0; +} + +/*****************************************************************************/ + +/** + * Stops the master thread. +*/ + +void ec_master_thread_stop(ec_master_t *master /**< EtherCAT master */) +{ + if (master->thread_id) { + kill_proc(master->thread_id, SIGTERM, 1); + wait_for_completion(&master->thread_exit); + EC_INFO("Master thread exited.\n"); + } +} + +/*****************************************************************************/ + +/** + * Transition function from ORPHANED to IDLE mode. */ int ec_master_enter_idle_mode(ec_master_t *master /**< EtherCAT master */) { + master->request_cb = ec_master_request_cb; + master->release_cb = ec_master_release_cb; + master->cb_data = master; + master->mode = EC_MASTER_MODE_IDLE; - queue_delayed_work(master->workqueue, &master->idle_work, 1); + if (ec_master_thread_start(master)) { + master->mode = EC_MASTER_MODE_ORPHANED; + return -1; + } + return 0; } /*****************************************************************************/ /** + * Transition function from IDLE to ORPHANED mode. */ void ec_master_leave_idle_mode(ec_master_t *master /**< EtherCAT master */) { + master->mode = EC_MASTER_MODE_ORPHANED; + ec_master_eoe_stop(master); - - master->mode = EC_MASTER_MODE_ORPHANED; - while (!cancel_delayed_work(&master->idle_work)) { - flush_workqueue(master->workqueue); - } - + ec_master_thread_stop(master); ec_master_flush_sdo_requests(master); } /*****************************************************************************/ /** + * Transition function from IDLE to OPERATION mode. */ int ec_master_enter_operation_mode(ec_master_t *master /**< EtherCAT master */) { ec_slave_t *slave; - ec_datagram_t *datagram = &master->fsm.datagram; + + ec_master_eoe_stop(master); // stop EoE timer + master->eoe_checked = 1; // prevent from starting again by FSM + ec_master_thread_stop(master); 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 + if (master->fsm_datagram.state == EC_DATAGRAM_SENT) { + while (get_cycles() - master->fsm_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)) { + if (ec_fsm_master_running(&master->fsm)) { + while (ec_fsm_master_exec(&master->fsm)) { ec_master_sync_io(master); } } @@ -392,64 +454,85 @@ } } + master->eoe_checked = 0; // allow starting EoE again + return 0; out_idle: master->mode = EC_MASTER_MODE_IDLE; - queue_delayed_work(master->workqueue, &master->idle_work, 1); + if (ec_master_thread_start(master)) { + EC_WARN("Failed to start master thread again!\n"); + } return -1; } /*****************************************************************************/ /** + * Transition function from OPERATION to IDLE mode. */ 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; + ec_fsm_master_t *fsm = &master->fsm; + ec_fsm_slave_t fsm_slave; + + ec_master_eoe_stop(master); // stop EoE timer + master->eoe_checked = 1; // prevent from starting again by FSM // 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)) {} + if (master->fsm_datagram.state == EC_DATAGRAM_SENT) { + // active waiting + while (get_cycles() - master->fsm_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)) { + if (ec_fsm_master_running(fsm)) { + while (ec_fsm_master_exec(fsm)) { ec_master_sync_io(master); } } + ec_fsm_slave_init(&fsm_slave, &master->fsm_datagram); + // 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); + // don't try to set PREOP for slaves that don't respond, + // because of 3 second timeout. + if (!slave->online) { + if (master->debug_level) + EC_DBG("Skipping to configure offline slave %i.\n", + slave->ring_position); + continue; + } + + ec_fsm_slave_start_conf(&fsm_slave, slave); + while (ec_fsm_slave_exec(&fsm_slave)) { ec_master_sync_io(master); } - while (fsm->slave_state != ec_fsm_slave_state_end - && fsm->slave_state != ec_fsm_slave_state_error); - } + } + + ec_fsm_slave_clear(&fsm_slave); ec_master_destroy_domains(master); - master->request_cb = NULL; - master->release_cb = NULL; - master->cb_data = NULL; + master->request_cb = ec_master_request_cb; + master->release_cb = ec_master_release_cb; + master->cb_data = master; + + master->eoe_checked = 0; // allow EoE again master->mode = EC_MASTER_MODE_IDLE; - queue_delayed_work(master->workqueue, &master->idle_work, 1); + if (ec_master_thread_start(master)) { + EC_WARN("Failed to start master thread!\n"); + } } /*****************************************************************************/ @@ -476,7 +559,6 @@ list_add_tail(&datagram->queue, &master->datagram_queue); datagram->state = EC_DATAGRAM_QUEUED; - datagram->cycles_queued = get_cycles(); } /*****************************************************************************/ @@ -676,8 +758,8 @@ // dequeue the received datagram datagram->state = EC_DATAGRAM_RECEIVED; - datagram->cycles_received = master->device->cycles_isr; - datagram->jiffies_received = master->device->jiffies_isr; + datagram->cycles_received = master->device->cycles_poll; + datagram->jiffies_received = master->device->jiffies_poll; list_del_init(&datagram->queue); } } @@ -721,101 +803,45 @@ /*****************************************************************************/ /** - Idle mode function. -*/ - -void ec_master_idle_run(void *data /**< master pointer */) + Master kernel thread function. +*/ + +static int ec_master_thread(void *data) { ec_master_t *master = (ec_master_t *) data; cycles_t cycles_start, cycles_end; - // aquire master lock - spin_lock_bh(&master->internal_lock); - - cycles_start = get_cycles(); - ecrt_master_receive(master); - - // execute master state machine - ec_fsm_exec(&master->fsm); - - ecrt_master_send(master); - cycles_end = get_cycles(); - - // release master lock - spin_unlock_bh(&master->internal_lock); - - master->idle_cycle_times[master->idle_cycle_time_pos] - = (u32) (cycles_end - cycles_start) * 1000 / cpu_khz; - master->idle_cycle_time_pos++; - master->idle_cycle_time_pos %= HZ; - - if (master->mode == EC_MASTER_MODE_IDLE) - queue_delayed_work(master->workqueue, &master->idle_work, 1); -} - -/*****************************************************************************/ - -/** - Initializes a sync manager configuration page with EEPROM data. - The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes. -*/ - -void ec_sync_config(const ec_sii_sync_t *sync, /**< sync manager */ - const ec_slave_t *slave, /**< EtherCAT slave */ - uint8_t *data /**> configuration memory */ - ) -{ - size_t sync_size; - - sync_size = ec_slave_calc_sync_size(slave, sync); - - if (slave->master->debug_level) { - 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); - EC_WRITE_U16(data + 2, sync_size); - EC_WRITE_U8 (data + 4, sync->control_register); - EC_WRITE_U8 (data + 5, 0x00); // status byte (read only) - EC_WRITE_U16(data + 6, sync->enable ? 0x0001 : 0x0000); // enable -} - -/*****************************************************************************/ - -/** - Initializes an FMMU configuration page. - The referenced memory (\a data) must be at least EC_FMMU_SIZE bytes. -*/ - -void ec_fmmu_config(const ec_fmmu_t *fmmu, /**< FMMU */ - const ec_slave_t *slave, /**< EtherCAT slave */ - uint8_t *data /**> configuration memory */ - ) -{ - size_t sync_size; - - 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 - EC_WRITE_U8 (data + 7, 0x07); // logical end bit - EC_WRITE_U16(data + 8, fmmu->sync->physical_start_address); - EC_WRITE_U8 (data + 10, 0x00); // physical start bit - EC_WRITE_U8 (data + 11, ((fmmu->sync->control_register & 0x04) - ? 0x02 : 0x01)); - EC_WRITE_U16(data + 12, 0x0001); // enable - EC_WRITE_U16(data + 14, 0x0000); // reserved + daemonize("EtherCAT"); + allow_signal(SIGTERM); + + while (!signal_pending(current) && master->mode == EC_MASTER_MODE_IDLE) { + cycles_start = get_cycles(); + + // receive + spin_lock_bh(&master->internal_lock); + ecrt_master_receive(master); + spin_unlock_bh(&master->internal_lock); + + // execute master state machine + ec_fsm_master_exec(&master->fsm); + + // send + spin_lock_bh(&master->internal_lock); + ecrt_master_send(master); + spin_unlock_bh(&master->internal_lock); + + cycles_end = get_cycles(); + master->idle_cycle_times[master->idle_cycle_time_pos] + = (u32) (cycles_end - cycles_start) * 1000 / cpu_khz; + master->idle_cycle_time_pos++; + master->idle_cycle_time_pos %= HZ; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + + master->thread_id = 0; + complete_and_exit(&master->thread_exit, 0); } /*****************************************************************************/ @@ -832,6 +858,7 @@ off_t off = 0; ec_eoe_t *eoe; uint32_t cur, sum, min, max, pos, i; + unsigned int frames_lost; off += sprintf(buffer + off, "\nVersion: %s", ec_master_version_str); off += sprintf(buffer + off, "\nMode: "); @@ -849,6 +876,14 @@ off += sprintf(buffer + off, "\nSlaves: %i\n", master->slave_count); + off += sprintf(buffer + off, "\nDevice:\n"); + off += sprintf(buffer + off, " Frames sent: %u\n", + master->device->tx_count); + off += sprintf(buffer + off, " Frames received: %u\n", + master->device->rx_count); + frames_lost = master->device->tx_count - master->device->rx_count; + if (frames_lost) frames_lost--; + off += sprintf(buffer + off, " Frames lost: %u\n", frames_lost); off += sprintf(buffer + off, "\nTiming (min/avg/max) [us]:\n"); @@ -990,14 +1025,6 @@ master->eoe_checked = 1; - // if the locking callbacks are not set in operation mode, - // the EoE timer my not be started. - if (master->mode == EC_MASTER_MODE_OPERATION - && (!master->request_cb || !master->release_cb)) { - EC_INFO("No EoE processing because of missing locking callbacks.\n"); - return; - } - // decouple all EoE handlers list_for_each_entry(eoe, &master->eoe_handlers, list) eoe->slave = NULL; @@ -1092,36 +1119,22 @@ } if (!active) goto queue_timer; - // aquire master lock... - if (master->mode == EC_MASTER_MODE_OPERATION) { - // request_cb must return 0, if the lock has been aquired! - if (master->request_cb(master->cb_data)) - goto queue_timer; - } - else if (master->mode == EC_MASTER_MODE_IDLE) { - spin_lock(&master->internal_lock); - } - else - goto queue_timer; - - // actual EoE processing + // receive datagrams + if (master->request_cb(master->cb_data)) goto queue_timer; cycles_start = get_cycles(); ecrt_master_receive(master); - + master->release_cb(master->cb_data); + + // actual EoE processing list_for_each_entry(eoe, &master->eoe_handlers, list) { ec_eoe_run(eoe); } + // send datagrams + if (master->request_cb(master->cb_data)) goto queue_timer; ecrt_master_send(master); cycles_end = get_cycles(); - - // release lock... - if (master->mode == EC_MASTER_MODE_OPERATION) { - master->release_cb(master->cb_data); - } - else if (master->mode == EC_MASTER_MODE_IDLE) { - spin_unlock(&master->internal_lock); - } + master->release_cb(master->cb_data); master->eoe_cycle_times[master->eoe_cycle_time_pos] = (u32) (cycles_end - cycles_start) * 1000 / cpu_khz; @@ -1249,6 +1262,36 @@ return -1; } +/*****************************************************************************/ + +/** + Prepares synchronous IO. + Queues all domain datagrams and sends them. Then waits a certain time, so + that ecrt_master_receive() can be called securely. +*/ + +void ec_master_prepare(ec_master_t *master /**< EtherCAT master */) +{ + ec_domain_t *domain; + cycles_t cycles_start, cycles_end, cycles_timeout; + + // queue datagrams of all domains + list_for_each_entry(domain, &master->domains, list) + ecrt_domain_queue(domain); + + ecrt_master_send(master); + + 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; + } +} + /****************************************************************************** * Realtime interface *****************************************************************************/ @@ -1300,7 +1343,7 @@ { uint32_t domain_offset; ec_domain_t *domain; - ec_fsm_t *fsm = &master->fsm; + ec_fsm_slave_t fsm_slave; ec_slave_t *slave; // allocate all domains @@ -1313,66 +1356,57 @@ domain_offset += domain->data_size; } + ec_fsm_slave_init(&fsm_slave, &master->fsm_datagram); + // configure all slaves list_for_each_entry(slave, &master->slaves, list) { - fsm->slave = slave; - fsm->slave_state = ec_fsm_slaveconf_state_start; - - do { - fsm->slave_state(fsm); + ec_fsm_slave_start_conf(&fsm_slave, slave); + while (ec_fsm_slave_exec(&fsm_slave)) { 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) { + + if (!ec_fsm_slave_success(&fsm_slave)) { + ec_fsm_slave_clear(&fsm_slave); EC_ERR("Failed to configure slave %i!\n", slave->ring_position); return -1; } } + ec_fsm_slave_clear(&fsm_slave); + ec_master_prepare(master); // prepare asynchronous IO + return 0; } /*****************************************************************************/ /** - 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 */) -{ -} - -/*****************************************************************************/ - -/** Sends queued datagrams and waits for their reception. */ void ec_master_sync_io(ec_master_t *master /**< EtherCAT master */) { ec_datagram_t *datagram; - unsigned int datagrams_waiting; - - // send datagrams + unsigned int datagrams_sent; + + // send all datagrams ecrt_master_send(master); while (1) { // active waiting + schedule(); // schedule other processes while waiting. ecrt_master_receive(master); // receive and dequeue datagrams // count number of datagrams still waiting for response - datagrams_waiting = 0; + datagrams_sent = 0; list_for_each_entry(datagram, &master->datagram_queue, queue) { - datagrams_waiting++; - } - - // if there are no more datagrams waiting, abort loop. - if (!datagrams_waiting) break; + // there may be another process that queued commands + // in the meantime. + if (datagram->state == EC_DATAGRAM_QUEUED) continue; + datagrams_sent++; + } + + // abort loop if there are no more datagrams marked as sent. + if (!datagrams_sent) break; } } @@ -1395,7 +1429,7 @@ } // query link state - ec_device_call_isr(master->device); + ec_device_poll(master->device); return; } @@ -1416,65 +1450,21 @@ cycles_t cycles_timeout; // receive datagrams - ec_device_call_isr(master->device); + ec_device_poll(master->device); 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: - if (master->device->cycles_isr - - datagram->cycles_sent > cycles_timeout) { - list_del_init(&datagram->queue); - datagram->state = EC_DATAGRAM_TIMED_OUT; - master->stats.timeouts++; - ec_master_output_stats(master); - } - break; - default: - break; - } - } -} - -/*****************************************************************************/ - -/** - Prepares synchronous IO. - Queues all domain datagrams and sends them. Then waits a certain time, so - that ecrt_master_receive() can be called securely. - \ingroup RealtimeInterface -*/ - -void ecrt_master_prepare(ec_master_t *master /**< EtherCAT master */) -{ - ec_domain_t *domain; - cycles_t cycles_start, cycles_end, cycles_timeout; - - // queue datagrams of all domains - list_for_each_entry(domain, &master->domains, list) - ec_domain_queue_datagrams(domain); - - ecrt_master_send(master); - - 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; + if (datagram->state != EC_DATAGRAM_SENT) continue; + + if (master->device->cycles_poll - datagram->cycles_sent + > cycles_timeout) { + list_del_init(&datagram->queue); + datagram->state = EC_DATAGRAM_TIMED_OUT; + master->stats.timeouts++; + ec_master_output_stats(master); + } } } @@ -1491,7 +1481,7 @@ ec_master_output_stats(master); // execute master state machine in a loop - ec_fsm_exec(&master->fsm); + ec_fsm_master_exec(&master->fsm); } /*****************************************************************************/ @@ -1515,9 +1505,12 @@ { unsigned long first, second; char *remainder, *remainder2; + const char *original; unsigned int alias_requested, alias_found; ec_slave_t *alias_slave = NULL, *slave; + original = address; + if (!address || address[0] == 0) return NULL; alias_requested = 0; @@ -1528,7 +1521,7 @@ first = simple_strtoul(address, &remainder, 0); if (remainder == address) { - EC_ERR("Slave address \"%s\" - First number empty!\n", address); + EC_ERR("Slave address \"%s\" - First number empty!\n", original); return NULL; } @@ -1541,7 +1534,7 @@ } } if (!alias_found) { - EC_ERR("Slave address \"%s\" - Alias not found!\n", address); + EC_ERR("Slave address \"%s\" - Alias not found!\n", original); return NULL; } } @@ -1555,7 +1548,7 @@ if (slave->ring_position == first) return slave; } EC_ERR("Slave address \"%s\" - Absolute position invalid!\n", - address); + original); } } else if (remainder[0] == ':') { // field position @@ -1563,19 +1556,19 @@ second = simple_strtoul(remainder, &remainder2, 0); if (remainder2 == remainder) { - EC_ERR("Slave address \"%s\" - Second number empty!\n", address); + EC_ERR("Slave address \"%s\" - Second number empty!\n", original); return NULL; } if (remainder2[0]) { - EC_ERR("Slave address \"%s\" - Invalid trailer!\n", address); + EC_ERR("Slave address \"%s\" - Invalid trailer!\n", original); return NULL; } if (alias_requested) { if (!ec_slave_is_coupler(alias_slave)) { EC_ERR("Slave address \"%s\": Alias slave must be bus coupler" - " in colon mode.\n", address); + " in colon mode.\n", original); return NULL; } list_for_each_entry(slave, &master->slaves, list) { @@ -1584,7 +1577,7 @@ return slave; } EC_ERR("Slave address \"%s\" - Bus coupler %i has no %lu. slave" - " following!\n", address, alias_slave->ring_position, + " following!\n", original, alias_slave->ring_position, second); return NULL; } @@ -1596,7 +1589,7 @@ } } else - EC_ERR("Slave address \"%s\" - Invalid format!\n", address); + EC_ERR("Slave address \"%s\" - Invalid format!\n", original); return NULL; } @@ -1628,8 +1621,6 @@ EXPORT_SYMBOL(ecrt_master_create_domain); EXPORT_SYMBOL(ecrt_master_activate); -EXPORT_SYMBOL(ecrt_master_deactivate); -EXPORT_SYMBOL(ecrt_master_prepare); EXPORT_SYMBOL(ecrt_master_send); EXPORT_SYMBOL(ecrt_master_receive); EXPORT_SYMBOL(ecrt_master_run); diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/master.h --- a/master/master.h Tue Feb 13 13:36:31 2007 +0000 +++ b/master/master.h Tue Feb 13 13:42:37 2007 +0000 @@ -49,7 +49,7 @@ #include "device.h" #include "domain.h" -#include "fsm.h" +#include "fsm_master.h" /*****************************************************************************/ @@ -101,7 +101,8 @@ ec_device_t *device; /**< EtherCAT device */ struct semaphore device_sem; /**< device semaphore */ - ec_fsm_t fsm; /**< master state machine */ + ec_fsm_master_t fsm; /**< master state machine */ + ec_datagram_t fsm_datagram; /**< datagram used for state machines */ ec_master_mode_t mode; /**< master mode */ struct list_head slaves; /**< list of slaves on the bus */ @@ -115,8 +116,8 @@ int debug_level; /**< master debug level */ ec_stats_t stats; /**< cyclic statistics */ - struct workqueue_struct *workqueue; /**< master workqueue */ - struct work_struct idle_work; /**< free run work object */ + int thread_id; /**< master thread PID */ + struct completion thread_exit; /**< thread completion object */ uint32_t idle_cycle_times[HZ]; /**< Idle cycle times ring */ unsigned int idle_cycle_time_pos; /**< time ring buffer position */ @@ -168,10 +169,6 @@ void ec_master_destroy_slaves(ec_master_t *); void ec_master_calc_addressing(ec_master_t *); -// 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 *); - /*****************************************************************************/ #endif diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/module.c --- a/master/module.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/module.c Tue Feb 13 13:42:37 2007 +0000 @@ -274,7 +274,7 @@ ec_device_t *ecdev_register(unsigned int master_index, /**< master index */ struct net_device *net_dev, /**< net_device of the device */ - ec_isr_t isr, /**< interrupt service routine */ + ec_pollfunc_t poll, /**< device poll function */ struct module *module /**< pointer to the module */ ) { @@ -298,7 +298,7 @@ goto out_up; } - if (ec_device_init(master->device, master, net_dev, isr, module)) { + if (ec_device_init(master->device, master, net_dev, poll, module)) { EC_ERR("Failed to init device!\n"); goto out_free; } @@ -352,24 +352,22 @@ /*****************************************************************************/ /** - Starts the master associated with the device. - This function has to be called by the network device driver to tell the - master that the device is ready to send and receive data. The master - will enter the idle mode then. + Opens the network device and makes the master enter IDLE mode. + \return 0 on success, else < 0 \ingroup DeviceInterface */ -int ecdev_start(unsigned int master_index /**< master index */) -{ - ec_master_t *master; - if (!(master = ec_find_master(master_index))) return -1; - - if (ec_device_open(master->device)) { +int ecdev_open(ec_device_t *device /**< EtherCAT device */) +{ + if (ec_device_open(device)) { EC_ERR("Failed to open device!\n"); return -1; } - ec_master_enter_idle_mode(master); + if (ec_master_enter_idle_mode(device->master)) { + EC_ERR("Failed to enter idle mode!\n"); + return -1; + } return 0; } @@ -377,20 +375,16 @@ /*****************************************************************************/ /** - Stops the master associated with the device. - Tells the master to stop using the device for frame IO. Has to be called - before unregistering the device. + Makes the master leave IDLE mode and closes the network device. + \return 0 on success, else < 0 \ingroup DeviceInterface */ -void ecdev_stop(unsigned int master_index /**< master index */) -{ - ec_master_t *master; - if (!(master = ec_find_master(master_index))) return; - - ec_master_leave_idle_mode(master); - - if (ec_device_close(master->device)) +void ecdev_close(ec_device_t *device /**< EtherCAT device */) +{ + ec_master_leave_idle_mode(device->master); + + if (ec_device_close(device)) EC_WARN("Failed to close device!\n"); } @@ -399,6 +393,19 @@ *****************************************************************************/ /** + * Returns the version magic of the realtime interface. + * \return ECRT version magic. + * \ingroup RealtimeInterface + */ + +unsigned int ecrt_version_magic(void) +{ + return ECRT_VERSION_MAGIC; +} + +/*****************************************************************************/ + +/** Reserves an EtherCAT master for realtime operation. \return pointer to reserved master, or NULL on error \ingroup RealtimeInterface @@ -469,13 +476,19 @@ void ecrt_release_master(ec_master_t *master /**< EtherCAT master */) { + EC_INFO("Releasing master %i...\n", master->index); + + if (master->mode != EC_MASTER_MODE_OPERATION) { + EC_WARN("Master %i was was not requested!\n", master->index); + return; + } + ec_master_leave_operation_mode(master); module_put(master->device->module); atomic_inc(&master->available); EC_INFO("Released master %i.\n", master->index); - return; } /*****************************************************************************/ @@ -487,10 +500,11 @@ EXPORT_SYMBOL(ecdev_register); EXPORT_SYMBOL(ecdev_unregister); -EXPORT_SYMBOL(ecdev_start); -EXPORT_SYMBOL(ecdev_stop); +EXPORT_SYMBOL(ecdev_open); +EXPORT_SYMBOL(ecdev_close); EXPORT_SYMBOL(ecrt_request_master); EXPORT_SYMBOL(ecrt_release_master); +EXPORT_SYMBOL(ecrt_version_magic); /** \endcond */ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/slave.c --- a/master/slave.c Tue Feb 13 13:36:31 2007 +0000 +++ b/master/slave.c Tue Feb 13 13:42:37 2007 +0000 @@ -112,7 +112,7 @@ slave->requested_state = EC_SLAVE_STATE_UNKNOWN; slave->current_state = EC_SLAVE_STATE_UNKNOWN; - slave->configured = 0; + slave->self_configured = 0; slave->error_flag = 0; slave->online = 1; slave->fmmu_count = 0; @@ -145,6 +145,7 @@ slave->sii_image = NULL; slave->sii_order = NULL; slave->sii_name = NULL; + slave->sii_current_on_ebus = 0; INIT_LIST_HEAD(&slave->sii_strings); INIT_LIST_HEAD(&slave->sii_syncs); @@ -398,6 +399,8 @@ for (i = 0; i < 4; i++) slave->sii_physical_layer[i] = (data[4] & (0x03 << (i * 2))) >> (i * 2); + + slave->sii_current_on_ebus = EC_READ_S16(data + 0x0C); } /*****************************************************************************/ @@ -629,15 +632,19 @@ off += sprintf(buffer + off, "State: "); off += ec_state_string(slave->current_state, buffer + off); - off += sprintf(buffer + off, "\nFlags: %s, %s\n", + off += sprintf(buffer + off, " ("); + off += ec_state_string(slave->requested_state, buffer + off); + 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); - off += sprintf(buffer + off, "Coupler: %s\n\n", + off += sprintf(buffer + off, "Coupler: %s\n", ec_slave_is_coupler(slave) ? "yes" : "no"); + off += sprintf(buffer + off, "Current consumption: %i mA\n\n", + slave->sii_current_on_ebus); off += sprintf(buffer + off, "Data link status:\n"); for (i = 0; i < 4; i++) { @@ -974,6 +981,71 @@ /*****************************************************************************/ /** + Initializes a sync manager configuration page with EEPROM data. + The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes. +*/ + +void ec_slave_sync_config(const ec_slave_t *slave, /**< EtherCAT slave */ + const ec_sii_sync_t *sync, /**< sync manager */ + uint8_t *data /**> configuration memory */ + ) +{ + size_t sync_size; + + sync_size = ec_slave_calc_sync_size(slave, sync); + + if (slave->master->debug_level) { + 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); + EC_WRITE_U16(data + 2, sync_size); + EC_WRITE_U8 (data + 4, sync->control_register); + EC_WRITE_U8 (data + 5, 0x00); // status byte (read only) + EC_WRITE_U16(data + 6, sync->enable ? 0x0001 : 0x0000); // enable +} + +/*****************************************************************************/ + +/** + Initializes an FMMU configuration page. + The referenced memory (\a data) must be at least EC_FMMU_SIZE bytes. +*/ + +void ec_slave_fmmu_config(const ec_slave_t *slave, /**< EtherCAT slave */ + const ec_fmmu_t *fmmu, /**< FMMU */ + uint8_t *data /**> configuration memory */ + ) +{ + size_t sync_size; + + 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 + EC_WRITE_U8 (data + 7, 0x07); // logical end bit + EC_WRITE_U16(data + 8, fmmu->sync->physical_start_address); + EC_WRITE_U8 (data + 10, 0x00); // physical start bit + EC_WRITE_U8 (data + 11, ((fmmu->sync->control_register & 0x04) + ? 0x02 : 0x01)); + EC_WRITE_U16(data + 12, 0x0001); // enable + EC_WRITE_U16(data + 14, 0x0000); // reserved +} + +/*****************************************************************************/ + +/** \return non-zero if slave is a bus coupler */ @@ -1213,13 +1285,13 @@ /*****************************************************************************/ -/**< \cond */ +/** \cond */ EXPORT_SYMBOL(ecrt_slave_conf_sdo8); EXPORT_SYMBOL(ecrt_slave_conf_sdo16); EXPORT_SYMBOL(ecrt_slave_conf_sdo32); EXPORT_SYMBOL(ecrt_slave_pdo_size); -/**< \endcond */ - -/*****************************************************************************/ +/** \endcond */ + +/*****************************************************************************/ diff -r bc89e3fba1a5 -r 5fcbd29151d2 master/slave.h --- a/master/slave.h Tue Feb 13 13:36:31 2007 +0000 +++ b/master/slave.h Tue Feb 13 13:42:37 2007 +0000 @@ -197,7 +197,7 @@ 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 self_configured; /**< slave was configured by this master */ unsigned int error_flag; /**< stop processing after an error */ unsigned int online; /**< non-zero, if the slave responds. */ @@ -244,6 +244,7 @@ char *sii_image; /**< slave image name acc. to EEPROM */ char *sii_order; /**< slave order number acc. to EEPROM */ char *sii_name; /**< slave name acc. to EEPROM */ + int16_t sii_current_on_ebus; /**< power consumption */ ec_fmmu_t fmmus[EC_MAX_FMMUS]; /**< FMMU configurations */ uint8_t fmmu_count; /**< number of FMMUs used */ @@ -277,8 +278,10 @@ int ec_slave_locate_string(ec_slave_t *, unsigned int, char **); // misc. -uint16_t ec_slave_calc_sync_size(const ec_slave_t *, - const ec_sii_sync_t *); +void ec_slave_sync_config(const ec_slave_t *, const ec_sii_sync_t *, + uint8_t *); +void ec_slave_fmmu_config(const ec_slave_t *, const ec_fmmu_t *, uint8_t *); +uint16_t ec_slave_calc_sync_size(const ec_slave_t *, const ec_sii_sync_t *); int ec_slave_is_coupler(const ec_slave_t *); int ec_slave_has_subbus(const ec_slave_t *); diff -r bc89e3fba1a5 -r 5fcbd29151d2 script/ethercat.sh --- a/script/ethercat.sh Tue Feb 13 13:36:31 2007 +0000 +++ b/script/ethercat.sh Tue Feb 13 13:42:37 2007 +0000 @@ -49,6 +49,12 @@ #------------------------------------------------------------------------------ +IFCONFIG=ifconfig +BRCTL=brctl +ROUTE=route + +#------------------------------------------------------------------------------ + ETHERCAT_CONFIG=/etc/sysconfig/ethercat if [ ! -r $ETHERCAT_CONFIG ]; then @@ -71,11 +77,11 @@ { if [ -z "$EOE_BRIDGE" ]; then return; fi - EOEIF=`/sbin/ifconfig -a | grep -o -E "^eoe[0-9]+ "` + EOEIF=`$IFCONFIG -a | grep -o -E "^eoe[0-9]+ "` # add bridge, if it does not already exist - if ! /sbin/brctl show | grep -E -q "^$EOE_BRIDGE"; then - if ! /sbin/brctl addbr $EOE_BRIDGE; then + if ! $BRCTL show | grep -E -q "^$EOE_BRIDGE"; then + if ! $BRCTL addbr $EOE_BRIDGE; then /bin/false rc_status -v rc_exit @@ -84,18 +90,18 @@ # check if specified interfaces are bridged for interf in $EOEIF $EOE_EXTRA_INTERFACES; do - # interface is already part of the bridge - if /sbin/brctl show $EOE_BRIDGE | grep -E -q $interf + # interface is already part of the bridge (FIXME->show $EOE_BRIDGE) + if $BRCTL show | grep -E -q $interf then continue fi # clear IP address and open interface - if ! /sbin/ifconfig $interf 0.0.0.0 up; then + if ! $IFCONFIG $interf 0.0.0.0 up; then /bin/false rc_status -v rc_exit fi # add interface to the bridge - if ! /sbin/brctl addif $EOE_BRIDGE $interf; then + if ! $BRCTL addif $EOE_BRIDGE $interf; then /bin/false rc_status -v rc_exit @@ -104,7 +110,7 @@ # configure IP on bridge if [ -n "$EOE_IP_ADDRESS" -a -n "$EOE_IP_NETMASK" ]; then - if ! /sbin/ifconfig $EOE_BRIDGE $EOE_IP_ADDRESS \ + if ! $IFCONFIG $EOE_BRIDGE $EOE_IP_ADDRESS \ netmask $EOE_IP_NETMASK; then /bin/false rc_status -v @@ -113,7 +119,7 @@ fi # open bridge - if ! /sbin/ifconfig $EOE_BRIDGE up; then + if ! $IFCONFIG $EOE_BRIDGE up; then /bin/false rc_status -v rc_exit @@ -121,15 +127,15 @@ # install new default gateway if [ -n "$EOE_GATEWAY" ]; then - while /sbin/route -n | grep -E -q "^0.0.0.0"; do - if ! /sbin/route del default; then + while $ROUTE -n | grep -E -q "^0.0.0.0"; do + if ! $ROUTE del default; then echo "Failed to remove route!" 1>&2 /bin/false rc_status -v rc_exit fi done - if ! /sbin/route add default gw $EOE_GATEWAY; then + if ! $ROUTE add default gw $EOE_GATEWAY; then /bin/false rc_status -v rc_exit diff -r bc89e3fba1a5 -r 5fcbd29151d2 script/lsec.pl --- a/script/lsec.pl Tue Feb 13 13:36:31 2007 +0000 +++ b/script/lsec.pl Tue Feb 13 13:42:37 2007 +0000 @@ -100,12 +100,15 @@ elsif ($line =~ /^Advanced position: (\d+:\d+)$/) { $slave->{'advanced_position'} = $1; } - elsif ($line =~ /^State: (.+)$/) { + elsif ($line =~ /^State: (.+) /) { $slave->{'state'} = $1; } elsif ($line =~ /^Coupler: ([a-z]+)$/) { $slave->{'coupler'} = $1; } + elsif ($line =~ /^Current consumption: (-?\d+) mA$/) { + $slave->{'current'} = $1; + } } close INFO; @@ -124,12 +127,32 @@ $cols = length $slave->{'advanced_position'}; $adv_cols = $cols if ($cols > $adv_cols); } - $fmt = sprintf " %%%is %%-%is %%-6s %%s\n", $ring_cols, $adv_cols; - - for $slave (@slaves) { - &print_line if $slave->{'coupler'} eq "yes" and !defined $opt{n}; - printf($fmt, $slave->{'ring_position'}, $slave->{'advanced_position'}, - $slave->{'state'}, $slave->{'name'}); + + if (defined $opt{'c'}) { # display power consumtion + $fmt = sprintf " %%%is %%-%is %%6i %%6i %%s\n", + $ring_cols, $adv_cols; + + my $current_sum = 0; + for $slave (@slaves) { + if ($slave->{'coupler'} eq "yes") { + &print_line if !defined $opt{n}; + $current_sum = 0; # reset current sum + } + $current_sum -= $slave->{'current'}; + printf($fmt, $slave->{'ring_position'}, + $slave->{'advanced_position'}, $slave->{'current'}, + $current_sum, $slave->{'name'}); + } + } + else { + $fmt = sprintf " %%%is %%-%is %%-6s %%s\n", $ring_cols, $adv_cols; + + for $slave (@slaves) { + &print_line if $slave->{'coupler'} eq "yes" and !defined $opt{n}; + printf($fmt, $slave->{'ring_position'}, + $slave->{'advanced_position'}, $slave->{'state'}, + $slave->{'name'}); + } } } @@ -137,7 +160,7 @@ sub get_options { - my $optret = getopts "m:nh", \%opt; + my $optret = getopts "m:cnh", \%opt; &print_usage if defined $opt{h} or $#ARGV > -1 or !$optret; @@ -157,6 +180,8 @@ chomp $cmd; print "Usage: $cmd [OPTIONS]\n"; print " -m Query master .\n"; + print " -c Display current [mA] "; + print "(3: consumption, 4: remaining).\n"; print " -n Display no coupler lines.\n"; print " -h Show this help.\n"; exit 0; diff -r bc89e3fba1a5 -r 5fcbd29151d2 script/sysconfig --- a/script/sysconfig Tue Feb 13 13:36:31 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -#------------------------------------------------------------------------------ -# -# EtherCAT sysconfig file -# -# $Id$ -# -#------------------------------------------------------------------------------ - -# -# PCI index of the (RTL8139-)EtherCAT device -# Setting this is mandatory for the EtherCAT init script! -# -#DEVICE_INDEX=99 - -# -# Number of Ethernet-over-EtherCAT interfaces every master shall create -# on startup. Default is 0. -# -#EOE_INTERFACES=0 - -# -# Bridge all EoE interfaces after master startup -# This variable shall contain the name of the EoE bridge to set up. -# If the variable is empty or undefined, no EoE bridge will be built. -# -#EOE_BRIDGE=eoebr0 - -# -# IP address of the EoE bridge -# Set both EOE_IP_ADDRESS and EOE_IP_NETMASK to let the local host communicate -# with devices on the EoE bridge. -# -#EOE_IP_ADDRESS=192.168.X.X - -# -# IP netmask of the EoE bridge -# See EOE_IP_ADDRESS. -# -#EOE_IP_NETMASK=255.255.255.0 - -# -# Renew default gateway after bridge installation. -# Set this to the new default gateway, if the default route shall -# be renewed after the bridge has been installed. -# -#EOE_GATEWAY=192.168.X.X - -# -# List of extra interfaces to include in the EoE bridge. -# Set this to interconnect the EoE bridge with other local interfaces. -# If EOE_BRIDGE is empty or undefined, setting this variable has no effect. -# Important: The IP address of these interfaces will be cleared. Set -# EOE_IP_ADDRESS and EOE_IP_NETMASK accordingly to enable IP traffic to -# extra interfaces. -# -#EOE_EXTRA_INTERFACES=eth0 - -#------------------------------------------------------------------------------ diff -r bc89e3fba1a5 -r 5fcbd29151d2 script/sysconfig-file --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/script/sysconfig-file Tue Feb 13 13:42:37 2007 +0000 @@ -0,0 +1,58 @@ +#------------------------------------------------------------------------------ +# +# EtherCAT sysconfig file +# +# $Id$ +# +#------------------------------------------------------------------------------ + +# +# PCI index of the (RTL8139-)EtherCAT device +# Setting this is mandatory for the EtherCAT init script! +# +#DEVICE_INDEX=99 + +# +# Number of Ethernet-over-EtherCAT interfaces every master shall create +# on startup. Default is 0. +# +#EOE_INTERFACES=0 + +# +# Bridge all EoE interfaces after master startup +# This variable shall contain the name of the EoE bridge to set up. +# If the variable is empty or undefined, no EoE bridge will be built. +# +#EOE_BRIDGE=eoebr0 + +# +# IP address of the EoE bridge +# Set both EOE_IP_ADDRESS and EOE_IP_NETMASK to let the local host communicate +# with devices on the EoE bridge. +# +#EOE_IP_ADDRESS=192.168.X.X + +# +# IP netmask of the EoE bridge +# See EOE_IP_ADDRESS. +# +#EOE_IP_NETMASK=255.255.255.0 + +# +# Renew default gateway after bridge installation. +# Set this to the new default gateway, if the default route shall +# be renewed after the bridge has been installed. +# +#EOE_GATEWAY=192.168.X.X + +# +# List of extra interfaces to include in the EoE bridge. +# Set this to interconnect the EoE bridge with other local interfaces. +# If EOE_BRIDGE is empty or undefined, setting this variable has no effect. +# Important: The IP address of these interfaces will be cleared. Set +# EOE_IP_ADDRESS and EOE_IP_NETMASK accordingly to enable IP traffic to +# extra interfaces. +# +#EOE_EXTRA_INTERFACES=eth0 + +#------------------------------------------------------------------------------