# HG changeset patch
# User graemef
# Date 1447040939 -46800
#      Mon Nov 09 16:48:59 2015 +1300
# Branch kmc
# Node ID ff6105150f6c46d8abaa7fab07d384d439def1b7
# Parent  3b323c27e2d9f11aab1724e68f3f849f0984986f
CX2100 driver

diff -r 3b323c27e2d9 -r ff6105150f6c configure.ac
--- a/configure.ac	Mon Nov 09 16:48:12 2015 +1300
+++ b/configure.ac	Mon Nov 09 16:48:59 2015 +1300
@@ -393,6 +393,61 @@
 AC_SUBST(E1000E_LAYOUT_3_4, [$e1000elayout34])
 
 #------------------------------------------------------------------------------
+# cx2100 driver
+#------------------------------------------------------------------------------
+
+AC_ARG_ENABLE([cx2100],
+    AS_HELP_STRING([--enable-cx2100],
+                   [Enable cx2100 driver]),
+    [
+        case "${enableval}" in
+            yes) enablecx2100=1
+                ;;
+            no) enablecx2100=0
+                ;;
+            *) AC_MSG_ERROR([Invalid value for --enable-cx2100])
+                ;;
+        esac
+    ],
+    [enablecx2100=0] # disabled by default
+)
+
+AM_CONDITIONAL(ENABLE_CX2100, test "x$enablecx2100" = "x1")
+AC_SUBST(ENABLE_CX2100,[$enablecx2100])
+
+AC_ARG_WITH([cx2100-kernel],
+    AC_HELP_STRING(
+        [--with-cx2100-kernel=<X.Y.Z>],
+        [cx2100 kernel (only if differing)]
+    ),
+    [
+        kernelcx2100=[$withval]
+    ],
+    [
+        kernelcx2100=$linuxversion
+    ]
+)
+
+if test "x${enablecx2100}" = "x1"; then
+    AC_MSG_CHECKING([for kernel for cx2100 driver])
+
+    kernels=`ls -1 ${srcdir}/devices/cx2100/ | grep -oE "^cx2100-.*" | cut -d "-" -f 2 | uniq`
+    found=0
+    for k in $kernels; do
+        if test "$kernelcx2100" = "$k"; then
+            found=1
+        fi
+    done
+    if test $found -ne 1; then
+        AC_MSG_ERROR([kernel $kernelcx2100 not available for cx2100 driver!])
+    fi
+
+    AC_MSG_RESULT([$kernelcx2100])
+fi
+
+AC_SUBST(KERNEL_CX2100,[$kernelcx2100])
+
+#------------------------------------------------------------------------------
 # r8169 driver
 #------------------------------------------------------------------------------
 
@@ -943,6 +998,8 @@
         devices/e1000/Makefile
         devices/e1000e/Kbuild
         devices/e1000e/Makefile
+        devices/cx2100/Kbuild
+        devices/cx2100/Makefile
         ethercat.spec
         examples/Kbuild
         examples/Makefile
diff -r 3b323c27e2d9 -r ff6105150f6c devices/Kbuild.in
--- a/devices/Kbuild.in	Mon Nov 09 16:48:12 2015 +1300
+++ b/devices/Kbuild.in	Mon Nov 09 16:48:59 2015 +1300
@@ -66,6 +66,10 @@
 	obj-m += e1000e/
 endif
 
+ifeq (@ENABLE_CX2100@,1)
+	obj-m += cx2100/
+endif
+
 ifeq (@ENABLE_R8169@,1)
 	EC_R8169_OBJ := r8169-@KERNEL_R8169@-ethercat.o
 	obj-m += ec_r8169.o
diff -r 3b323c27e2d9 -r ff6105150f6c devices/Makefile.am
--- a/devices/Makefile.am	Mon Nov 09 16:48:12 2015 +1300
+++ b/devices/Makefile.am	Mon Nov 09 16:48:59 2015 +1300
@@ -29,11 +29,13 @@
 
 SUBDIRS = \
 	e1000 \
-	e1000e
+	e1000e \
+	cx2100
 
 DIST_SUBDIRS = \
 	e1000 \
-	e1000e
+	e1000e \
+	cx2100
 
 # using HEADERS to enable tags target
 noinst_HEADERS = \
diff -r 3b323c27e2d9 -r ff6105150f6c devices/cx2100/Kbuild.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/devices/cx2100/Kbuild.in	Mon Nov 09 16:48:59 2015 +1300
@@ -0,0 +1,61 @@
+#------------------------------------------------------------------------------
+#
+#  Copyright (C) 2013  Graeme Foot, Kinetic Engineering Design Ltd.
+#                                   <graeme@touchcut.com>
+#
+#  This file is subject to the terms and conditions of the GNU General Public
+#  License version 2, as published by the Free Software Foundation.
+# 
+#  This file 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.
+#
+#  This file has been added to 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 version 2, as
+#  published by the Free Software Foundation.
+#
+#  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 license mentioned above concerns the source code only. Using the
+#  EtherCAT technology and brand is only permitted in compliance with the
+#  industrial property and similar rights of Beckhoff Automation GmbH.
+#
+#  ---
+#
+#  vim: syntax=make
+#
+#------------------------------------------------------------------------------
+
+TOPDIR := $(src)/../..
+
+REV := $(shell if test -s $(TOPDIR)/revision; then \
+		cat $(TOPDIR)/revision; \
+	else \
+		hg id -i $(TOPDIR) 2>/dev/null || echo "unknown"; \
+	fi)
+
+ifeq (@ENABLE_CX2100@,1)
+	EC_CX2100_OBJ := \
+		cx2100-@KERNEL_CX2100@-ethercat.o
+	obj-m += ec_cx2100.o
+	ec_cx2100-objs := $(EC_CX2100_OBJ)
+	CFLAGS_cx2100-@KERNEL_CX2100@-ethercat.o = -DREV=$(REV)
+endif
+
+KBUILD_EXTRA_SYMBOLS := \
+	@abs_top_builddir@/$(LINUX_SYMVERS) \
+	@abs_top_builddir@/master/$(LINUX_SYMVERS)
+
+#------------------------------------------------------------------------------
diff -r 3b323c27e2d9 -r ff6105150f6c devices/cx2100/LICENSE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/devices/cx2100/LICENSE	Mon Nov 09 16:48:59 2015 +1300
@@ -0,0 +1,339 @@
+
+"This software program is licensed subject to the GNU General Public License 
+(GPL). Version 2, June 1991, available at 
+<http://www.fsf.org/copyleft/gpl.html>"
+
+GNU General Public License 
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to 
+share and change it. By contrast, the GNU General Public License is intended
+to guarantee your freedom to share and change free software--to make sure 
+the software is free for all its users. This General Public License applies 
+to most of the Free Software Foundation's software and to any other program 
+whose authors commit to using it. (Some other Free Software Foundation 
+software is covered by the GNU Library General Public License instead.) You 
+can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom 
+to distribute copies of free software (and charge for this service if you 
+wish), that you receive source code or can get it if you want it, that you 
+can change the software or use pieces of it in new free programs; and that 
+you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to 
+deny you these rights or to ask you to surrender the rights. These 
+restrictions translate to certain responsibilities for you if you distribute
+copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or 
+for a fee, you must give the recipients all the rights that you have. You 
+must make sure that they, too, receive or can get the source code. And you 
+must show them these terms so they know their rights.
+ 
+We protect your rights with two steps: (1) copyright the software, and (2) 
+offer you this license which gives you legal permission to copy, distribute 
+and/or modify the software. 
+
+Also, for each author's protection and ours, we want to make certain that 
+everyone understands that there is no warranty for this free software. If 
+the software is modified by someone else and passed on, we want its 
+recipients to know that what they have is not the original, so that any 
+problems introduced by others will not reflect on the original authors' 
+reputations. 
+
+Finally, any free program is threatened constantly by software patents. We 
+wish to avoid the danger that redistributors of a free program will 
+individually obtain patent licenses, in effect making the program 
+proprietary. To prevent this, we have made it clear that any patent must be 
+licensed for everyone's free use or not licensed at all. 
+
+The precise terms and conditions for copying, distribution and modification 
+follow. 
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+   placed by the copyright holder saying it may be distributed under the 
+   terms of this General Public License. The "Program", below, refers to any
+   such program or work, and a "work based on the Program" means either the 
+   Program or any derivative work under copyright law: that is to say, a 
+   work containing the Program or a portion of it, either verbatim or with 
+   modifications and/or translated into another language. (Hereinafter, 
+   translation is included without limitation in the term "modification".) 
+   Each licensee is addressed as "you". 
+
+   Activities other than copying, distribution and modification are not 
+   covered by this License; they are outside its scope. The act of running 
+   the Program is not restricted, and the output from the Program is covered 
+   only if its contents constitute a work based on the Program (independent 
+   of having been made by running the Program). Whether that is true depends
+   on what the Program does. 
+
+1. You may copy and distribute verbatim copies of the Program's source code 
+   as you receive it, in any medium, provided that you conspicuously and 
+   appropriately publish on each copy an appropriate copyright notice and 
+   disclaimer of warranty; keep intact all the notices that refer to this 
+   License and to the absence of any warranty; and give any other recipients 
+   of the Program a copy of this License along with the Program. 
+
+   You may charge a fee for the physical act of transferring a copy, and you 
+   may at your option offer warranty protection in exchange for a fee. 
+
+2. You may modify your copy or copies of the Program or any portion of it, 
+   thus forming a work based on the Program, and copy and distribute such 
+   modifications or work under the terms of Section 1 above, provided that 
+   you also meet all of these conditions: 
+
+   * a) You must cause the modified files to carry prominent notices stating 
+        that you changed the files and the date of any change. 
+
+   * b) You must cause any work that you distribute or publish, that in 
+        whole or in part contains or is derived from the Program or any part 
+        thereof, to be licensed as a whole at no charge to all third parties
+        under the terms of this License. 
+
+   * c) If the modified program normally reads commands interactively when 
+        run, you must cause it, when started running for such interactive 
+        use in the most ordinary way, to print or display an announcement 
+        including an appropriate copyright notice and a notice that there is
+        no warranty (or else, saying that you provide a warranty) and that 
+        users may redistribute the program under these conditions, and 
+        telling the user how to view a copy of this License. (Exception: if 
+        the Program itself is interactive but does not normally print such 
+        an announcement, your work based on the Program is not required to 
+        print an announcement.) 
+
+   These requirements apply to the modified work as a whole. If identifiable 
+   sections of that work are not derived from the Program, and can be 
+   reasonably considered independent and separate works in themselves, then 
+   this License, and its terms, do not apply to those sections when you 
+   distribute them as separate works. But when you distribute the same 
+   sections as part of a whole which is a work based on the Program, the 
+   distribution of the whole must be on the terms of this License, whose 
+   permissions for other licensees extend to the entire whole, and thus to 
+   each and every part regardless of who wrote it. 
+
+   Thus, it is not the intent of this section to claim rights or contest 
+   your rights to work written entirely by you; rather, the intent is to 
+   exercise the right to control the distribution of derivative or 
+   collective works based on the Program. 
+
+   In addition, mere aggregation of another work not based on the Program 
+   with the Program (or with a work based on the Program) on a volume of a 
+   storage or distribution medium does not bring the other work under the 
+   scope of this License. 
+
+3. You may copy and distribute the Program (or a work based on it, under 
+   Section 2) in object code or executable form under the terms of Sections 
+   1 and 2 above provided that you also do one of the following: 
+
+   * a) Accompany it with the complete corresponding machine-readable source 
+        code, which must be distributed under the terms of Sections 1 and 2 
+        above on a medium customarily used for software interchange; or, 
+
+   * b) Accompany it with a written offer, valid for at least three years, 
+        to give any third party, for a charge no more than your cost of 
+        physically performing source distribution, a complete machine-
+        readable copy of the corresponding source code, to be distributed 
+        under the terms of Sections 1 and 2 above on a medium customarily 
+        used for software interchange; or, 
+
+   * c) Accompany it with the information you received as to the offer to 
+        distribute corresponding source code. (This alternative is allowed 
+        only for noncommercial distribution and only if you received the 
+        program in object code or executable form with such an offer, in 
+        accord with Subsection b above.) 
+
+   The source code for a work means the preferred form of the work for 
+   making modifications to it. For an executable work, complete source code 
+   means all the source code for all modules it contains, plus any 
+   associated interface definition files, plus the scripts used to control 
+   compilation and installation of the executable. However, as a special 
+   exception, the source code distributed need not include anything that is 
+   normally distributed (in either source or binary form) with the major 
+   components (compiler, kernel, and so on) of the operating system on which
+   the executable runs, unless that component itself accompanies the 
+   executable. 
+
+   If distribution of executable or object code is made by offering access 
+   to copy from a designated place, then offering equivalent access to copy 
+   the source code from the same place counts as distribution of the source 
+   code, even though third parties are not compelled to copy the source 
+   along with the object code. 
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+   expressly provided under this License. Any attempt otherwise to copy, 
+   modify, sublicense or distribute the Program is void, and will 
+   automatically terminate your rights under this License. However, parties 
+   who have received copies, or rights, from you under this License will not
+   have their licenses terminated so long as such parties remain in full 
+   compliance. 
+
+5. You are not required to accept this License, since you have not signed 
+   it. However, nothing else grants you permission to modify or distribute 
+   the Program or its derivative works. These actions are prohibited by law 
+   if you do not accept this License. Therefore, by modifying or 
+   distributing the Program (or any work based on the Program), you 
+   indicate your acceptance of this License to do so, and all its terms and
+   conditions for copying, distributing or modifying the Program or works 
+   based on it. 
+
+6. Each time you redistribute the Program (or any work based on the 
+   Program), the recipient automatically receives a license from the 
+   original licensor to copy, distribute or modify the Program subject to 
+   these terms and conditions. You may not impose any further restrictions 
+   on the recipients' exercise of the rights granted herein. You are not 
+   responsible for enforcing compliance by third parties to this License. 
+
+7. If, as a consequence of a court judgment or allegation of patent 
+   infringement or for any other reason (not limited to patent issues), 
+   conditions are imposed on you (whether by court order, agreement or 
+   otherwise) that contradict the conditions of this License, they do not 
+   excuse you from the conditions of this License. If you cannot distribute 
+   so as to satisfy simultaneously your obligations under this License and 
+   any other pertinent obligations, then as a consequence you may not 
+   distribute the Program at all. For example, if a patent license would 
+   not permit royalty-free redistribution of the Program by all those who 
+   receive copies directly or indirectly through you, then the only way you 
+   could satisfy both it and this License would be to refrain entirely from 
+   distribution of the Program. 
+
+   If any portion of this section is held invalid or unenforceable under any
+   particular circumstance, the balance of the section is intended to apply
+   and the section as a whole is intended to apply in other circumstances. 
+
+   It is not the purpose of this section to induce you to infringe any 
+   patents or other property right claims or to contest validity of any 
+   such claims; this section has the sole purpose of protecting the 
+   integrity of the free software distribution system, which is implemented 
+   by public license practices. Many people have made generous contributions
+   to the wide range of software distributed through that system in 
+   reliance on consistent application of that system; it is up to the 
+   author/donor to decide if he or she is willing to distribute software 
+   through any other system and a licensee cannot impose that choice. 
+
+   This section is intended to make thoroughly clear what is believed to be 
+   a consequence of the rest of this License. 
+
+8. If the distribution and/or use of the Program is restricted in certain 
+   countries either by patents or by copyrighted interfaces, the original 
+   copyright holder who places the Program under this License may add an 
+   explicit geographical distribution limitation excluding those countries, 
+   so that distribution is permitted only in or among countries not thus 
+   excluded. In such case, this License incorporates the limitation as if 
+   written in the body of this License. 
+
+9. The Free Software Foundation may publish revised and/or new versions of 
+   the General Public License from time to time. Such new versions will be 
+   similar in spirit to the present version, but may differ in detail to 
+   address new problems or concerns. 
+
+   Each version is given a distinguishing version number. If the Program 
+   specifies a version number of this License which applies to it and "any 
+   later version", you have the option of following the terms and 
+   conditions either of that version or of any later version published by 
+   the Free Software Foundation. If the Program does not specify a version 
+   number of this License, you may choose any version ever published by the 
+   Free Software Foundation. 
+
+10. If you wish to incorporate parts of the Program into other free programs
+    whose distribution conditions are different, write to the author to ask 
+    for permission. For software which is copyrighted by the Free Software 
+    Foundation, write to the Free Software Foundation; we sometimes make 
+    exceptions for this. Our decision will be guided by the two goals of 
+    preserving the free status of all derivatives of our free software and 
+    of promoting the sharing and reuse of software generally. 
+
+   NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 
+    FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 
+    OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 
+    PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER 
+    EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 
+    ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH 
+    YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL 
+    NECESSARY SERVICING, REPAIR OR CORRECTION. 
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 
+    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 
+    REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR 
+    DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL 
+    DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM 
+    (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED 
+    INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF 
+    THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR 
+    OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest 
+possible use to the public, the best way to achieve this is to make it free 
+software which everyone can redistribute and change under these terms. 
+
+To do so, attach the following notices to the program. It is safest to 
+attach them to the start of each source file to most effectively convey the
+exclusion of warranty; and each file should have at least the "copyright" 
+line and a pointer to where the full notice is found. 
+
+one line to give the program's name and an idea of what it does.
+Copyright (C) yyyy  name of author
+
+This program is free software; you can redistribute it and/or modify it 
+under the terms of the GNU General Public License as published by the Free 
+Software Foundation; either version 2 of the License, or (at your option) 
+any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT 
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 
+Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Also add information on how to contact you by electronic and paper mail. 
+
+If the program is interactive, make it output a short notice like this when 
+it starts in an interactive mode: 
+
+Gnomovision version 69, Copyright (C) year name of author Gnomovision comes 
+with ABSOLUTELY NO WARRANTY; for details type 'show w'.  This is free 
+software, and you are welcome to redistribute it under certain conditions; 
+type 'show c' for details.
+
+The hypothetical commands 'show w' and 'show c' should show the appropriate 
+parts of the General Public License. Of course, the commands you use may be 
+called something other than 'show w' and 'show c'; they could even be 
+mouse-clicks or menu items--whatever suits your program. 
+
+You should also get your employer (if you work as a programmer) or your 
+school, if any, to sign a "copyright disclaimer" for the program, if 
+necessary. Here is a sample; alter the names: 
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program 
+'Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+signature of Ty Coon, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into 
+proprietary programs. If your program is a subroutine library, you may 
+consider it more useful to permit linking proprietary applications with the 
+library. If this is what you want to do, use the GNU Library General Public 
+License instead of this License.
diff -r 3b323c27e2d9 -r ff6105150f6c devices/cx2100/Makefile.am
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/devices/cx2100/Makefile.am	Mon Nov 09 16:48:59 2015 +1300
@@ -0,0 +1,55 @@
+#------------------------------------------------------------------------------
+#
+#  Copyright (C) 2013  Graeme Foot, Kinetic Engineering Design Ltd.
+#                                   <graeme@touchcut.com>
+#
+#  This file is subject to the terms and conditions of the GNU General Public
+#  License version 2, as published by the Free Software Foundation.
+# 
+#  This file 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.
+#
+#  This file has been added to 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 version 2, as
+#  published by the Free Software Foundation.
+#
+#  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 license mentioned above concerns the source code only. Using the
+#  EtherCAT technology and brand is only permitted in compliance with the
+#  industrial property and similar rights of Beckhoff Automation GmbH.
+#
+#------------------------------------------------------------------------------
+
+EXTRA_DIST = \
+	Kbuild.in \
+	LICENSE \
+	cx2100-2.6.32-ethercat.c
+
+BUILT_SOURCES = \
+	Kbuild
+
+modules:
+	$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" modules
+
+modules_install:
+	$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" \
+		INSTALL_MOD_DIR="$(INSTALL_MOD_DIR)" modules_install
+
+clean-local:
+	$(MAKE) -C "$(LINUX_SOURCE_DIR)" M="@abs_srcdir@" clean
+
+#------------------------------------------------------------------------------
diff -r 3b323c27e2d9 -r ff6105150f6c devices/cx2100/cx2100-2.6.32-ethercat.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/devices/cx2100/cx2100-2.6.32-ethercat.c	Mon Nov 09 16:48:59 2015 +1300
@@ -0,0 +1,1740 @@
+/******************************************************************************
+ *
+ *  $Id$
+ *
+ *  Copyright (C) 2013  Graeme Foot, Kinetic Engineering Design Ltd.
+ *                                   <graeme@touchcut.com>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License version 2, as published by the Free Software Foundation.
+ * 
+ *  This file 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.
+ *
+ *  This file has been added to 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 version 2, as
+ *  published by the Free Software Foundation.
+ *
+ *  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 license mentioned above concerns the source code only. Using the
+ *  EtherCAT technology and brand is only permitted in compliance with the
+ *  industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+/* 
+ * Notes:
+ * 
+ * To transmit data the EtherCAT Master passes the data using reusable skb's
+ * (socket data buffers).  Usually the skb's are allocated by the client and
+ * released by the driver, but because they are reused, they are not freed.
+ * 
+ * In a general Ethernet situation skb's are also used to send received data
+ * data back to the client, but instead we can send the data directly back
+ * to the EtherCAT master and avoid extra memory copies.
+ * 
+ * This device is primarily for use with the IgH EtherCAT Master.  If it
+ * is combined with an EK1110 module then it can also function as a general
+ * Ethernet port.  (The EK1110 needs to be configured into forward mode.)
+ * 
+ * However, as this is an unusual use case for this hardware I have not enabled 
+ * general Ethernet port functionality, so if it is not claimed by the EtherCAT 
+ * master it will remain idle.
+ * 
+ * The following is the data for a frame that can be sent to the EK1110 to
+ * set it into forward mode (untested):
+ * 
+ *  UINT8 frameForwardEthernetFrames[] = 
+ *          { 0x01, 0x01, 0x05, 0x01, 0x00, 0x00, 
+ *            0x00, 0x1b, 0x21, 0x36, 0x1b, 0xce, 
+ *            0x88, 0xa4, 0x0e, 0x10,
+ *            0x08,    
+ *            0x00,  
+ *            0x00, 0x00,
+ *            0x00, 0x01,
+ *            0x02, 0x00,
+ *            0x00, 0x00,
+ *            0x00, 0x00,
+ *            0x00, 0x00 };
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/mman.h>
+
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/pci_ids.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/completion.h>
+#include <linux/crc32.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/irq.h>
+
+#include "../../globals.h"
+#include "../ecdev.h"
+
+
+MODULE_AUTHOR("Graeme Foot <graemef@touchcut.com>");
+MODULE_DESCRIPTION("Beckhoff CX2100-0004 PCI Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(EC_MASTER_VERSION);
+
+static int verbosityLevel = 0;
+static int verbosityCalls = -1;
+module_param(verbosityLevel, int, S_IRUGO);
+module_param(verbosityCalls, int, S_IRUGO);
+
+
+
+#define VENDOR_ID   0x15ec
+#define DEVICE_ID   0x5000
+#define DRIVER_NAME "ec_cx2100"
+
+
+#define FRAME_PAGE_SIZE 0x1000
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT      (6*HZ)
+#define TX_BUF_TOT_LEN  
+
+
+/* Note: the max Ethernet frame length is approx 1514 (ETH_FRAME_LEN)
+ * the is the MTU (max transmission unit) + 10 (header) + 4 (CRC)
+ * 0x800 = 2048 so we are home and hosed to cover the frame + DMA header
+ *         and then also have a nice alignment */
+#define MAX_FRAME_SIZE      0x800
+#define TX_FRAME_DATA_SIZE  MAX_FRAME_SIZE - sizeof(listItem_s) - sizeof(txHeader_s)
+#define RX_FRAME_DATA_SIZE  MAX_FRAME_SIZE - sizeof(rxDMAHeader_s) - sizeof(rxHeader_s)
+
+
+/* verbosity levels
+ * Level 0: no verbosity messages
+ * Level 1: major verbosity messages
+ *   ..
+ * Level 9: detail verbosity messages
+ */
+#define VERBOSITY_MAJOR  1
+#define VERBOSITY_DETAIL 9
+
+#define VERBOSITY_DEVICEINFO   1
+#define VERBOSITY_FUNCTIONCALL 5
+#define VERBOSITY_RETVALUE     7
+
+
+#define MSG_INFO(fmt, args...) printk(KERN_INFO DRIVER_NAME ": " fmt, ##args)
+#define MSG_ERR(fmt, args...)  printk(KERN_ERR DRIVER_NAME " ERROR: " fmt, ##args)
+#define MSG_WARN(fmt, args...) printk(KERN_WARNING DRIVER_NAME " WARNING: " fmt, ##args)
+#define MSG_VERBOSE(lvl, fmt, args...) if (lvl <= verbosityLevel) \
+{ \
+  printk(KERN_INFO DRIVER_NAME ": " fmt, ##args); \
+  if      (verbosityCalls > 0) verbosityCalls--; \
+  else if (verbosityCalls == 0) verbosityLevel = 0; \
+}
+
+
+
+/** list item and helpers for tx frames
+ */
+typedef struct _listItem_s listItem_s;
+struct _listItem_s
+{
+  listItem_s *prev;             /**< link to previous list item */
+  listItem_s *next;             /**< link to next list item */
+};
+
+
+/** initialize a list
+ */
+static inline void initList(listItem_s *head)
+{
+  head->prev = head;
+  head->next = head;
+}
+
+
+/** check if a list is empty (ie the list only points to itself)
+ */
+static inline int isListEmpty(listItem_s *head)
+{
+  return (head->next == head);
+}
+
+
+/** return this first item in the list
+ */
+static inline listItem_s *listFirstItem(listItem_s *head)
+{
+  return head != head->next ? head->next : NULL;
+}
+
+
+/** remove an item from its list
+ */
+static inline listItem_s *listRemoveItem(listItem_s *item)
+{
+  listItem_s* prevItem = item->prev;
+  listItem_s* nextItem = item->next;
+  
+  prevItem->next = nextItem;
+  nextItem->prev = prevItem;
+  
+  item->prev = NULL;
+  item->next = NULL;
+
+  return item;
+}
+
+
+/** remove the first item from the list and return it
+ */
+static inline listItem_s *listRemoveFirstItem(listItem_s *head)
+{
+  return listRemoveItem(head->next);
+}
+
+
+/** insert an item at the end of the list
+ */
+static inline void listAddTail(listItem_s *head, listItem_s *item)
+{
+  // insert between the old last item and the head
+  listItem_s *prev = head->prev;
+  listItem_s *next = head;
+  
+  prev->next = item;
+  next->prev = item;
+
+  item->prev = prev;
+  item->next = next;
+}
+
+
+
+/** CX2100 base function information block */
+typedef struct _infoBlock_s {
+  u16   fnType;
+  u16   fnRevision;
+  union
+  {
+    struct
+    {
+      u32   fnParam;
+      u32   fnAddrOffset;
+      u32   fnSize;
+    } _base;
+    struct
+    {
+      u8    fnBlockCount;
+      u8    creationDay;
+      u8    creationMonth;
+      u8    creationYear;
+      u32   id1;
+      u32   id2;
+    } _info;
+    struct
+    {
+      u8    txDMAChannel;
+      u8    rxDMAChannel;
+      u16   reserved1;
+      u32   fnAddrOffset;
+      u32   fnSize;
+    } _ecMasterDMA;
+  };
+} infoBlock_s;
+
+#define CCAT_ID1 0x000088a4
+#define CCAT_ID2 0x54414343  /* "CCAT" */
+
+
+
+/** EtherCAT master function block */
+typedef struct _ecMasterFnBlock_s 
+{
+  u32   infoBlock;
+  u32   miiOffset;
+  u32   txFifoOffset;
+  u32   macRegisterOffset;
+  u32   rxWindowOffset;
+  u32   txMemoryOffset;
+  u8    physicalConnectionActive : 1;
+  u8    reserved1                : 7;
+  u8    defaultFieldbus;
+  u16   supportedFieldbus;
+} ecMasterFnBlock_s;
+
+
+
+/** EtherCAT master MII */
+typedef struct _mii_s 
+{
+  u16   miCycle       : 1;
+  u16   reserved1     : 6;
+  u16   cmdValid      : 1;
+  u16   cmdRegister   : 2;
+  u16   reserved2     : 6;
+  
+  u16   phyAddress    : 5;
+  u16   reserved3     : 3;
+  u16   addressOfReg  : 5;
+  u16   reserved4     : 3;
+  
+  u16   phyWriteData;
+  u16   phyReadData;
+} mii_s;
+
+
+
+/** EtherCAT master MAC address filter */
+typedef struct _macFilter_s 
+{
+  u8    macAddr[6];
+  
+  u8    macFilterEnable   : 1;
+  u8    reserved1         : 7;
+  
+  u8    linkStatus        : 1;
+  u8    reserved2         : 7;
+} macFilter_s;
+
+
+/** EtherCAT master Management Functions */
+typedef struct _ecManagement_s 
+{
+  mii_s        mii;
+  macFilter_s  macFilter;
+} ecManagement_s;
+
+
+
+/** EtherCAT master tx header 
+ */
+typedef struct _txHeader_s 
+{
+  u16   length;           /**< not used for dma */
+  
+  u8    port0     : 1;    /**< sending port 0, one-hot coded */
+  u8    port1     : 1;    /**< sending port 0, one-hot coded */
+  u8    reserved1 : 6;
+  
+  u8    tsEnable  : 1;    /**< sending if timestamp is reached */
+  u8    sync0     : 1;    /**< sending on sync 0 event */
+  u8    sync1     : 1;    /**< sending on sync 1 event (doc says sync 0???) */
+  u8    reserved2 : 5;
+  
+  u32   readAck   : 1;    /**< read acknowledge (1 if read by CX2100, ie sent) */
+  u32   reserved3 : 31;
+  
+  u64   timestamp;        /**< frame timestamp */
+} txHeader_s;
+
+
+
+/** EtherCAT master dma tx frame 
+ */
+typedef struct _txDMAFrame_s 
+{
+  listItem_s   listItem;                 /**< user defined data, must be 64bit */
+  txHeader_s   header;                   /**< frame header */
+  u8           data[TX_FRAME_DATA_SIZE]; /**< frame data */
+} txDMAFrame_s;
+
+
+
+/* forward declaration */
+typedef struct _rxDMAFrame_s rxDMAFrame_s;
+
+
+/** EtherCAT master dma rx header 
+ */
+typedef struct _rxDMAHeader_s 
+{
+  rxDMAFrame_s *nextFrame;
+
+  u32   received  : 1;
+  u32   reserved1 : 7;
+  u32   nextValid : 1;
+  u32   reserved2 : 23;
+} rxDMAHeader_s;
+  
+
+/** EtherCAT master dma rx data header 
+ */
+typedef struct _rxHeader_s 
+{
+  u16   length      : 12;
+  u16   reserved3   : 4;
+  
+  u16   port        : 8;
+  u16   reserved4   : 8;
+  
+  u32   reserved5;
+  
+  u64   timestamp;
+} rxHeader_s;
+
+
+/** EtherCAT master dma rx frame 
+ */
+struct _rxDMAFrame_s 
+{
+  rxDMAHeader_s   dmaHeader;                /**< frame DMA header */
+  rxHeader_s      header;                   /**< frame data header */
+  u8              data[RX_FRAME_DATA_SIZE]; /**< frame data */
+};
+
+
+
+/** tx fifo */
+typedef struct _txFifo_s
+{
+  union
+  {
+    struct
+    {
+      u32   queueAddr   : 24;         /**< offset addr of current frame relative to DMA mem 
+                                        *  set to address of the frame to add to the fifo */
+      u32   queueLen64  : 8;          /**< length of frame in 64bit words (ie (len + 8) / 8) */
+    };
+    u32   queueFrame;                 /**< use queueFrame to set the queueAddr and queueLen64
+                                       *   at the same time */
+  };
+  u32   reserved1;
+  u32   fifoReset   : 8;              /**< reset fifo data (by writing any value) */
+  u32   reserved2   : 24;
+  u32   reserved3;
+} txFifo_s;
+
+
+
+/** rx fifo */
+typedef struct _rxFifo_s
+{
+  union
+  {
+    struct
+    {
+      u32   queueAddr   : 24;         /**< offset addr of current frame relative to DMA mem
+                                        *  set to address of the frame to add to the fifo */
+      u32   reserved1   : 7;
+      u32   queueValid  : 1;          /**< is the queue addr valid? */
+    };
+    u32   queueFrame;                 /**< use queueFrame to set the queueAddr and queueValid
+                                       *   at the same time */
+  };
+  union
+  {
+    struct
+    {
+      u32   lastAddr    : 24;         /**< ??? doc says this is reserved */
+      u32   reserved2   : 8;
+    };
+    u32   reserved2;
+  };
+  u32   fifoLevel   : 24;             /**< ??? no doc? fifo reset? */
+  u32   bufferLevel : 8;              /**< ??? no doc? */
+  u32   nextAddr;                     /**< ??? next address used for received frame */
+}  rxFifo_s;
+
+
+
+/** private data allocated and stored by the netDev */
+typedef struct _cx2100_private_s 
+{
+  void __iomem       *bar0_addr;
+  void __iomem       *bar2_addr;
+  struct pci_dev     *pci_dev;
+  struct napi_struct  napi;
+  struct net_device  *netDev;
+
+  unsigned char      *rxVirtMem;            /**< was rx_ring, rx dma virtual memory */
+  dma_addr_t          rxPhysAddr;           /**< device tx dma physical address */
+  size_t              rxDMAMemSize;         /**< size of the dma virtual memory allocated */
+  int                 rxFrameCnt;           /**< number of available tx frames */
+  rxDMAFrame_s       *rxFrames;             /**< first rx frame, can be used as array of frames */
+  rxFifo_s           *rxFifo;
+  rxDMAFrame_s       *rxHead;
+  rxDMAFrame_s       *rxTail;
+
+  unsigned char      *txVirtMem;            /**< was tx_buff, tx dma virtual memory */
+  dma_addr_t          txPhysAddr;           /**< device tx dma physical address */
+  size_t              txDMAMemSize;         /**< size of the dma virtual memory allocated */
+  int                 txFrameCnt;           /**< number of available tx frames */
+  txDMAFrame_s       *txFrames;             /**< first tx frame, can be used as array of frames */
+  txFifo_s           *txFifo;
+  listItem_s          txListFree;
+  listItem_s          txListPend;
+  int                 txOK;                 /**< are the transmit frames available */
+  
+  int                 linkOK;
+
+  infoBlock_s        *ecMasterInfoBlock;    /**< The EtherCAT Master Information Block (in BAR0 function list) */
+  ecMasterFnBlock_s  *ecMasterFnBlock;      /**< The EtherCAT Master Function Block (in BAR0) */
+  ecManagement_s     *ecManagement;         /**< The ec Master Management interface and information */
+
+  ec_device_t        *ecdev;
+} cx2100_private_s;
+
+
+
+
+/* forward declarations */
+static int cx2100_recycleRxFrame(cx2100_private_s *tp, rxDMAFrame_s *rxFrame);
+static int cx2100_getLinkStatus(cx2100_private_s *tp, int forceUpdate);
+
+
+
+/** read bar memory at the given offset 
+ */
+static int readBarMem(
+        void __iomem *in_barAddr,
+        size_t in_offset,
+        size_t in_size,
+        void *out_data
+        )
+{
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "readBarMem");
+  
+  if (!in_barAddr) return -ENOMEM;
+  
+  mb();
+
+  switch (in_size)
+  {
+    case 1 : { *((u8 *)out_data)  = (u8)ioread8(in_barAddr + in_offset); } break;
+    case 2 : { *((u16 *)out_data) = (u16)ioread16(in_barAddr + in_offset); } break;
+    case 4 : { *((u32 *)out_data) = (u32)ioread32(in_barAddr + in_offset); } break;
+    default :
+    {
+      memcpy(out_data, (void *)(in_barAddr + in_offset), in_size);
+    }
+  }
+  
+  return 0;
+}
+
+
+/** write bar memory at the given offset 
+ */
+static int writeBarMem(
+        void __iomem *in_barAddr,
+        size_t in_offset,
+        size_t in_size,
+        void *in_data
+        )
+{
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "writeBarMem");
+  
+  if (!in_barAddr) return -ENOMEM;
+  
+  switch (in_size)
+  {
+    case 1 : { iowrite8(*(u8 *)in_data, in_barAddr + in_offset); } break;
+    case 2 : { iowrite16(*(u16 *)in_data, in_barAddr + in_offset); } break;
+    case 4 : { iowrite32(*(u32 *)in_data, in_barAddr + in_offset); } break;
+    default :
+    {
+      memcpy((void *)(in_barAddr + in_offset), in_data, in_size);
+    }
+  }
+  
+  mb();
+
+  return 0;
+}
+
+
+
+
+/** initialise dma memory
+ * 
+ * This allocates consistent virtual memory mapped to the DMA memory.
+ * First we query the size of the DMA so we know how much memory needs to be 
+ * allocated
+ */
+static int cx2100_initDmaMem(
+        cx2100_private_s *tp,         /**< in: private data */
+        u8 dmaChannel,                /**< in: the dma channel number */
+        unsigned char **dmaVirtMem,   /**< out: pc side virtual memory address */
+        dma_addr_t *dmaPhysAddr,      /**< out: hardware side physical address */
+        size_t *dmaMemSize,           /**< out: size of coherent memory allocated */
+        int *frameCnt,                /**< out: the number of frames that will fit in the memory */
+        void **frameHead              /**< out: pointer to first frame in virtual mem */
+        )
+{
+  u32 data   = 0xFFFFFFFF;
+  u32 offset = 0x1000 + sizeof(u64) * dmaChannel;
+  
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_initDmaMem");
+  
+  // calculate available memory size by writing/reading bar2 dma physical address
+  if ( (writeBarMem(tp->bar2_addr, offset, sizeof(data), &data) == 0) &&
+       (readBarMem(tp->bar2_addr, offset, sizeof(data), &data) == 0) )
+  {
+    // calc mem size available
+    u32 memTranslate = data & 0xfffffffc;
+    u32 memSize      = (~memTranslate) + 1;
+    *dmaMemSize      = 2*memSize - FRAME_PAGE_SIZE;
+
+    if (memSize > 0)
+    {
+      *dmaVirtMem = dma_alloc_coherent(&tp->pci_dev->dev, *dmaMemSize,
+                                       dmaPhysAddr, GFP_KERNEL);
+    }
+    else
+    {
+      *dmaVirtMem = NULL;
+    }
+    
+    mb();
+
+    if ( (*dmaVirtMem != NULL) && (*dmaPhysAddr != 0) )
+    {
+      // success, translate memory address
+      u32 translateAddr = (*dmaPhysAddr + memSize - FRAME_PAGE_SIZE) & memTranslate;
+      u64 addr          = translateAddr;
+
+      // clear dma memory
+      memset(*dmaVirtMem, 0, *dmaMemSize);
+
+      // write translated physical address to bar2
+      writeBarMem(tp->bar2_addr, offset, sizeof(addr), &addr);
+
+      // calc number of available tx frames
+      *frameCnt = memSize / sizeof(txDMAFrame_s);
+      
+      // set pointer to first frame;
+      *frameHead = (void *)(((u32)*dmaVirtMem) + translateAddr - *dmaPhysAddr);
+      
+      // all good
+      return 0;
+    }
+  }
+  
+  return -ENOMEM;
+}
+
+
+
+/** initialise the rx and tx lists
+ * 
+ * We keep a list of tx frames and send them to the txFifo when we want to 
+ * send something.  Once the data is sent we recycle them back into the free list
+ * 
+ * The rx frames are all sent to the rxFifo so that it can send them to us when
+ * there is data to be read.  Once we have read it we send it straight back
+ * to the rxFifo
+ */
+static int cx2100_initRxTxLists(
+        cx2100_private_s *tp            /**< in: private data */
+        )
+{
+  int i;
+  
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_initRxTxLists");
+  
+  // init ok flags
+  tp->txOK = 1;
+  
+  // calc fifo pointers
+  tp->txFifo = ((void *)tp->ecMasterFnBlock) + tp->ecMasterFnBlock->txFifoOffset;
+  tp->rxFifo = ((void *)tp->ecMasterFnBlock) + tp->ecMasterFnBlock->txFifoOffset + 0x10;
+       
+  // reset tx fifo
+  tp->txFifo->fifoReset  = 0;
+  tp->txFifo->fifoReset  = 1;
+  
+  // reset rx fifo
+  tp->rxFifo->queueFrame = 0;
+  tp->rxFifo->fifoLevel  = 0;
+
+  
+  // init tx frame lists
+  initList(&tp->txListFree);
+  initList(&tp->txListPend);
+
+  
+  // add all tx frames to the free list 
+  for (i = 0; i < tp->txFrameCnt; i++)
+  {
+    listAddTail(&tp->txListFree, (listItem_s *)&tp->txFrames[i]);
+  }
+  
+  
+  // add all rx frames to the rx ring
+  for (i = 0; i < tp->rxFrameCnt; i++)
+  {
+    cx2100_recycleRxFrame(tp, &tp->rxFrames[i]);
+  }
+
+  
+  return 0;
+}
+
+
+
+/** open the port, allocate the DMA memory and set up the frames
+ */
+static int cx2100_open(
+        struct net_device *netDev
+        )
+{
+  cx2100_private_s *tp = netdev_priv(netDev);
+  int               res;
+
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_open");
+  
+  
+  // init tx dma mem
+  res = cx2100_initDmaMem(tp, tp->ecMasterInfoBlock->_ecMasterDMA.txDMAChannel,
+                          &tp->txVirtMem, &tp->txPhysAddr, &tp->txDMAMemSize, 
+                          &tp->txFrameCnt, (void **)&tp->txFrames);
+  if (res) return res;
+
+  
+  // init rx dma mem
+  res = cx2100_initDmaMem(tp, tp->ecMasterInfoBlock->_ecMasterDMA.rxDMAChannel,
+                          &tp->rxVirtMem, &tp->rxPhysAddr, &tp->rxDMAMemSize,
+                          &tp->rxFrameCnt, (void **)&tp->rxFrames);
+  if (res) return res;
+  
+  
+  // init the rx and tx list
+  res = cx2100_initRxTxLists(tp);
+  if (res) return res;
+  
+  
+  // disable the mac filter to ensure it isn't writable
+  tp->ecManagement->macFilter.macFilterEnable = 0;
+  
+  
+  // get initial link status
+  cx2100_getLinkStatus(tp, 1);
+
+  
+  return 0;
+}
+
+
+
+/** close the port
+ */
+static int cx2100_close(
+        struct net_device *netDev
+        )
+{
+  cx2100_private_s *tp = netdev_priv(netDev);
+  
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_close");
+  
+
+  // free the dma memory
+  if (tp->rxVirtMem)
+  {
+    dma_free_coherent(&tp->pci_dev->dev, tp->rxDMAMemSize,
+                      tp->rxVirtMem, tp->rxPhysAddr);
+    tp->rxVirtMem = NULL;
+  }
+  if (tp->txVirtMem)
+  {
+    dma_free_coherent(&tp->pci_dev->dev, tp->txDMAMemSize,
+                      tp->txVirtMem, tp->txPhysAddr);
+    tp->txVirtMem = NULL;
+  }
+  
+  
+  // reset tx fifo
+  if (tp->txFifo)
+  {
+    tp->txFifo->fifoReset  = 0;
+    tp->txFifo->fifoReset  = 1;
+  }
+  
+  // reset rx fifo
+  if (tp->rxFifo)
+  {
+    tp->rxFifo->queueFrame = 0;
+    tp->rxFifo->fifoLevel  = 0;
+  }
+  
+  
+  // reset the lists
+  tp->txListFree.next = NULL;
+  tp->txListFree.prev = NULL;
+  
+  tp->rxHead = NULL;
+  tp->rxTail = NULL;
+
+
+  return 0;
+}
+
+
+  
+/** request a tx frame from the free tx frames list
+ * 
+ * We send the pointer to it back in terms of the data section
+ */
+static int cx2100_requestTxFrame(
+        cx2100_private_s *tp,
+        unsigned int len,
+        txDMAFrame_s **txFrame
+        )
+{
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_requestTxFrame");
+  
+  if ( (len <= TX_FRAME_DATA_SIZE) && !isListEmpty(&tp->txListFree) )
+  {
+    // get a free frame
+    *txFrame = (txDMAFrame_s *)listRemoveFirstItem(&tp->txListFree);
+
+    MSG_VERBOSE(VERBOSITY_RETVALUE, "Get a free txFrame: 0x%08x", (u32)(*txFrame));
+  
+    
+    // set port 0 (don't know why)
+    (*txFrame)->header.port0 = 1;
+    
+    // txFrame available
+    tp->txOK = 1;
+    
+    return 0;
+  }
+  else
+  {
+    if (tp->txOK)
+    {
+      // only show message on first failure
+      tp->txOK = 0;
+      
+      if (len <= TX_FRAME_DATA_SIZE)
+      {
+        MSG_ERR("Communication Error: No more transmit frames are available");
+      }
+    }
+  
+    *txFrame = NULL;
+    
+    return -ENOMEM;
+  }
+}
+
+
+
+// Not currently used
+///** recycle an unused tx frame (re-adds to free list)
+// */
+//static int cx2100_recycleTxFrame(
+//        cx2100_private_s *tp,
+//        void **data
+//        )
+//{
+//  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_recycleTxFrame");
+//  
+//  // offset from data pointer back to frame pointer and
+//  // fill in the fram length
+//  txDMAFrame_s *txFrame = (txDMAFrame_s *)((void *)data - offsetof(txDMAFrame_s, data));
+//
+//  // readd to the free list
+//  listAddTail(&tp->txListFree, &txFrame->listItem);
+//
+//
+//  return 0;
+//}
+
+
+
+/** send a tx frame, queues it to the txFifo and adds it to the pending list
+ */
+static int cx2100_sendTxFrame(
+        cx2100_private_s *tp,
+        unsigned int len,
+        txDMAFrame_s *txFrame
+        )
+{
+  // calc addr offset to start of frame header (ignoring dma header)
+  u32 frameAddr = (u32)&txFrame->header - (u32)tp->txFrames;
+  
+  // calc the frame length (including header) in 64bit words
+  // shift it to the top byte
+  // Note: blindly add 8 bits to ensure we are rounding up
+  u32 len64Word = ((len + sizeof(txHeader_s) + 8) / 8) << 24;
+  
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_sendTxFrame");
+
+  MSG_VERBOSE(VERBOSITY_DETAIL, "TX Frame info: txFrame: 0x%08x, frameAddr: %u, frameLen: %u, frameLen64: %u", 
+              (u32)txFrame, frameAddr, len, len64Word);
+  
+  
+  mb();
+
+  // fill in the frame length
+  txFrame->header.length = (u16)len;
+
+  mb();
+
+  // set start address of frame-header and length in 64 bit words
+  // note: using the above memory barrier to ensure these params
+  //       are not out of order with this cmd
+  tp->txFifo->queueFrame = frameAddr + len64Word;
+  
+  
+  // set some stats
+  tp->netDev->stats.tx_bytes   += len;
+  tp->netDev->stats.tx_packets++;
+
+
+  // add the frame to the pending list
+  listAddTail(&tp->txListPend, &txFrame->listItem);
+
+  
+  return 0;
+}
+
+
+/** clean up any frames that have been sent and are still in the pending list
+ * 
+ * If the link is down then the fifo will be auto reset, so clear out any
+ * pend items regardless of being read
+ * 
+ * We reset the readAck flag to let the txFifo know that we know the data has
+ * been read, and to get it ready for the next use
+ */
+static void cx2100_cleanupSentTxFrames(
+        cx2100_private_s *tp
+        )
+{
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_cleanupSentTxFrames");
+  
+  while (!isListEmpty(&tp->txListPend))
+  {
+    // get first item
+    txDMAFrame_s *txFrame = (txDMAFrame_s *)listFirstItem(&tp->txListPend);
+    
+    MSG_VERBOSE(VERBOSITY_DETAIL, "Check cleanup frame: txFrame: 0x%08x", (u32)txFrame);
+
+    // is it ready to be recycled? (frame has been read or link error)
+    if (txFrame->header.readAck || !tp->linkOK)
+    {
+      MSG_VERBOSE(VERBOSITY_DETAIL, "Recycle frame: txFrame: 0x%08x", (u32)txFrame);
+  
+      // clear readAck bit
+      txFrame->header.readAck = 0;
+
+      // add back to the free list
+      listRemoveItem(&txFrame->listItem);
+      listAddTail(&tp->txListFree, &txFrame->listItem);
+    }
+    else
+    {
+      // frame not sent yet
+      MSG_VERBOSE(VERBOSITY_DETAIL, "Frame not sent yet: txFrame: 0x%08x", (u32)txFrame);
+
+      break;
+    }
+  }
+}
+
+
+
+/** driver callback function to transmit a frame
+ * 
+ * The data is passed via a socket buffer.  In the normal course of events
+ * the client allocates the buffer and we free it.  However, the ethercat master
+ * allocated the buffers once and reuses them, so don't free them.
+ */
+static netdev_tx_t cx2100_start_xmit(struct sk_buff *skb,
+        struct net_device *netDev
+        )
+{
+  cx2100_private_s *tp  = netdev_priv(netDev);
+  unsigned int      len = skb->len;
+  int               res;
+  txDMAFrame_s     *txFrame;
+  
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_start_xmit");
+  
+
+  // get a frame, not if the len is too long then the frame is not returned
+  res = cx2100_requestTxFrame(tp, len, &txFrame);
+  if (unlikely(res)) 
+  {
+    MSG_VERBOSE(VERBOSITY_DETAIL, "txFrame not available");
+  
+    netDev->stats.tx_dropped++;
+    return NETDEV_TX_OK;
+  }
+  
+  
+  // if the data is less then the min frame len then pre-zero the mem
+  if (len < ETH_ZLEN)
+  {
+    memset(&txFrame->data, 0, ETH_ZLEN);
+  }
+  
+  // copy from skb to frame data
+  skb_copy_and_csum_dev(skb, (void *)&txFrame->data);
+    
+  
+  // send the frame
+  res = cx2100_sendTxFrame(tp, len, txFrame);
+  
+  
+  netDev->trans_start = jiffies;
+
+
+  return NETDEV_TX_OK;
+}
+
+
+
+/** return the received rx frame back to the rxFifo for reuse
+ * 
+ * We need to set the received flag to let the fifo know we have read the data
+ */
+static int cx2100_recycleRxFrame(
+        cx2100_private_s *tp, 
+        rxDMAFrame_s *rxFrame
+        )
+{  
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_recycleRxFrame");
+  
+  // update last frame to point to this frame and set that it is a valid pointer
+  if (tp->rxTail)
+  {
+    tp->rxTail->dmaHeader.nextFrame = rxFrame;
+    tp->rxTail->dmaHeader.nextValid = 1;
+  }
+  
+  // reset the received flag
+  rxFrame->dmaHeader.received = 0;
+  
+  mb();
+  
+  // tell the fifo to use this frame
+  // Note: we are setting the frame offset addr and valid flag in one go
+  tp->rxFifo->queueFrame = 0x80000000 | ((u32)rxFrame - (u32)tp->rxFrames);
+  
+  if (tp->rxHead == NULL)
+  {
+    // init head
+    tp->rxHead = rxFrame;
+  }
+  
+  // set new tail
+  tp->rxTail = rxFrame;
+  
+  
+  return 0;
+}
+
+
+
+/** check for received frames
+ */
+static int cx2100_rx(
+        cx2100_private_s *tp
+        )
+{ 
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_rx");
+  
+  // process received frames
+  while (tp->rxHead && tp->rxHead->dmaHeader.received)
+  {
+    rxDMAFrame_s *rxFrame = tp->rxHead;
+
+    // calc frames data length (frame length - data header - CRC)
+    // Note: the frame length value does not include the DMA user header
+    void   *data = (u8 *)&rxFrame->data;
+    size_t  len  = rxFrame->header.length - sizeof(rxHeader_s) - 4;
+    
+    rmb();
+    
+    // update the rx head to the next list item
+    if (tp->rxHead->dmaHeader.nextValid) tp->rxHead = tp->rxHead->dmaHeader.nextFrame;
+    else                                 tp->rxHead = NULL;
+    rxFrame->dmaHeader.nextValid = 0;
+    
+
+    // let the ec master know we have data
+    ecdev_receive(tp->ecdev, data, len);
+    
+    // update stats
+    tp->netDev->last_rx          = jiffies;
+    tp->netDev->stats.rx_bytes  += len;
+    tp->netDev->stats.rx_packets++;
+    
+    
+    // recycle the frame to the free list
+    cx2100_recycleRxFrame(tp, rxFrame);
+  }
+  
+  
+  return 0;
+}
+
+
+
+/** updates whether the link status is connected and return the status
+ * 
+ * also informs the ec master of any change
+ */
+static int cx2100_getLinkStatus(
+        cx2100_private_s *tp,
+        int forceUpdate
+        )
+{
+  int linkOK;
+  
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_getLinkStatus");
+  
+
+  linkOK = (tp->ecManagement->macFilter.linkStatus != 0);
+  if ( (tp->linkOK != linkOK) || (forceUpdate) )
+  {
+    MSG_INFO("Link Status change: %s", linkOK ? "Connected" : "Disconnected");
+    tp->linkOK = linkOK;
+
+    // inform the ec master of the new status
+    ecdev_set_link(tp->ecdev, linkOK ? 1 : 0);
+  }
+  
+  return linkOK;
+}
+
+
+
+/** The poller is called by the ec master and does all of the rx fifo work 
+ * and cleans up after the tx fifo. 
+ */
+static void cx2100_poll(struct net_device *netDev)
+{
+  cx2100_private_s *tp = netdev_priv(netDev);
+
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_poll");
+  
+
+  if (cx2100_getLinkStatus(tp, 0))
+  {
+    // check for received frames
+    cx2100_rx(tp); 
+  }
+
+  // always clean up after any sent tx frames
+  // Note: if link is down then the fifo is auto reset and we need to clean 
+  // house anyway
+  cx2100_cleanupSentTxFrames(tp);
+}
+
+
+
+/** ensure we have a Beckhoff device with an EtherCAT Master with DMA
+ */
+static void __devinit cx2100_listBlockInfo(
+        infoBlock_s *baseInfoBlock 
+        )
+{
+  int          i;
+  infoBlock_s *ecMasterInfoBlock;
+  
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_listBlockInfo");
+  
+  
+  ecMasterInfoBlock = baseInfoBlock+1;
+  for (i = 0; i < baseInfoBlock->_info.fnBlockCount-1; i++)
+  {
+    switch (ecMasterInfoBlock->fnType)
+    {
+      case 0x0001 :
+      {
+        MSG_INFO("  %d: 0x%04x - Base Information Block (unexpected), Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0002 :
+      {
+        MSG_INFO("  %d: 0x%04x - EtherCAT Slave, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0003 :
+      {
+        MSG_INFO("  %d: 0x%04x - EtherCAT Master without DMA, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0004 :
+      {
+        MSG_INFO("  %d: 0x%04x - Ethernet MAC without DMA, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0005 :
+      {
+        MSG_INFO("  %d: 0x%04x - Ethernet Switch, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0006 :
+      {
+        MSG_INFO("  %d: 0x%04x - Sercos III, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0007 :
+      {
+        MSG_INFO("  %d: 0x%04x - Profibus, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0008 :
+      {
+        MSG_INFO("  %d: 0x%04x - CAN Controller, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0009 :
+      {
+        MSG_INFO("  %d: 0x%04x - KBUS Master, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x000a :
+      {
+        MSG_INFO("  %d: 0x%04x - IP-Link Master, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x000b :
+      {
+        MSG_INFO("  %d: 0x%04x - SPI Master, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x000c :
+      {
+        MSG_INFO("  %d: 0x%04x - I2C Master, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x000d :
+      {
+        MSG_INFO("  %d: 0x%04x - GPIO, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x000e :
+      {
+        MSG_INFO("  %d: 0x%04x - Drive, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x000f :
+      {
+        MSG_INFO("  %d: 0x%04x - CCAT Update, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0010 :
+      {
+        MSG_INFO("  %d: 0x%04x - System time, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0011 :
+      {
+        MSG_INFO("  %d: 0x%04x - Interrupt Controller, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0012 :
+      {
+        MSG_INFO("  %d: 0x%04x - EEPROM Controller, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0013 :
+      {
+        MSG_INFO("  %d: 0x%04x - DMA Controller, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0014 :
+      {
+        MSG_INFO("  %d: 0x%04x - EtherCAT Master with DMA, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0015 :
+      {
+        MSG_INFO("  %d: 0x%04x - Ethernet MAC with DMA, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0016 :
+      {
+        MSG_INFO("  %d: 0x%04x - SRAM Interface, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      case 0x0017 :
+      {
+        MSG_INFO("  %d: 0x%04x - Internal Copy block, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      } break;
+      
+      default :
+      {
+        MSG_INFO("  %d: 0x%04x - Unknown function type, Revision: 0x%04x\n", 
+                 i, ecMasterInfoBlock->fnType, ecMasterInfoBlock->fnRevision);
+      }
+    }
+
+    
+    // next
+    ecMasterInfoBlock++;
+  }
+}
+
+
+/** ensure we have a Beckhoff device with an EtherCAT Master with DMA
+ */
+static int __devinit cx2100_matchDevice(
+        infoBlock_s *baseInfoBlock, 
+        infoBlock_s **ecMasterInfoBlock
+        )
+{
+  int i;
+
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_matchDevice");
+  
+
+  // ensure BAR0 type is 0x0001 and that the ID information is correct
+  if ( (baseInfoBlock->fnType != 0x0001) ||
+       (baseInfoBlock->_info.id1 != 0x000088a4) ||
+       (baseInfoBlock->_info.id2 != 0x54414343) )
+  {
+    // invalid information
+    if (baseInfoBlock->fnType != 0x0001)
+    {
+      MSG_ERR("Invalid base information type (Type: 0x%04x, ID1: 0x%08x, ID2: 0x%08x)\n",
+              baseInfoBlock->fnType, baseInfoBlock->_info.id1, baseInfoBlock->_info.id2);
+    }
+    else if (baseInfoBlock->_info.id1 != 0x000088a4)
+    {
+      MSG_ERR("Invalid base information id 1 (Type: 0x%04x, ID1: 0x%08x, ID2: 0x%08x)\n",
+              baseInfoBlock->fnType, baseInfoBlock->_info.id1, baseInfoBlock->_info.id2);
+    }
+    else if (baseInfoBlock->_info.id2 != 0x54414343)
+    {
+      MSG_ERR("Invalid base information id 2 (Type: 0x%04x, ID1: 0x%08x, ID2: 0x%08x)\n",
+              baseInfoBlock->fnType, baseInfoBlock->_info.id1, baseInfoBlock->_info.id2);
+    }
+    
+    if (baseInfoBlock->fnType == 0xffff)
+    {
+      uint8_t *data = (uint8_t *)baseInfoBlock;
+      
+      MSG_ERR("Invalid base information, Raw data:\n");
+      MSG_ERR("  0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+              data[0],
+              data[1],
+              data[2],
+              data[3],
+              data[4],
+              data[5],
+              data[6],
+              data[7]);
+      MSG_ERR("  0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+              data[8],
+              data[9],
+              data[10],
+              data[11],
+              data[12],
+              data[13],
+              data[14],
+              data[15]);
+    }
+    
+    return -ENODEV;
+  }
+
+  
+  // check the available functions, we are looking for an EtherCAT Master with DMA function
+  *ecMasterInfoBlock = baseInfoBlock+1;
+  for (i = 0; i < baseInfoBlock->_info.fnBlockCount-1; i++)
+  {
+    // is it an EtherCAT Master with DMA?
+    if ((*ecMasterInfoBlock)->fnType == 0x0014)
+    {
+      MSG_INFO("Found EtherCAT Master (0x0014), Revision: 0x%04x, txDMA: 0x%02x, rxDMA: 0x%02x\n", 
+               (*ecMasterInfoBlock)->fnRevision,
+               (*ecMasterInfoBlock)->_ecMasterDMA.txDMAChannel,
+               (*ecMasterInfoBlock)->_ecMasterDMA.rxDMAChannel);
+      
+      if (verbosityLevel >= VERBOSITY_DEVICEINFO)
+      {
+        cx2100_listBlockInfo(baseInfoBlock);
+      }
+      
+      return 0;
+    }
+    
+    // next
+    (*ecMasterInfoBlock)++;
+  }
+  
+  
+  // no EtherCAT Master found, list supported functions
+  MSG_ERR("EtherCAT master function not found.  This device supports the following functions:\n");
+  
+  cx2100_listBlockInfo(baseInfoBlock);
+  
+  
+  return -ENODEV;
+}
+  
+  
+
+/** initialise the device and setup the net device and private info etc 
+ */
+static __devinit struct net_device * cx2100_initNetDev (
+        struct pci_dev *dev 
+        )
+{
+  struct net_device *netDev;
+  cx2100_private_s  *tp;
+
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_initNetDev");
+  
+
+  /* netDev and priv zeroed in alloc_etherdev */
+  netDev = alloc_etherdev(sizeof (*tp));
+  if (netDev == NULL)
+  {
+    dev_err(&dev->dev, "Unable to alloc new net device\n");
+    return ERR_PTR(-ENOMEM);
+  }
+  SET_NETDEV_DEV(netDev, &dev->dev);
+
+  
+  // initialise some values
+  netDev->base_addr = pci_resource_start(dev, 0);
+
+  tp = netdev_priv(netDev);
+  tp->pci_dev = dev;
+  tp->netDev  = netDev;
+  
+
+  return netDev;
+}
+
+
+/** netDev operation callback functions */
+static const struct net_device_ops cx2100_netdev_ops = {
+  .ndo_open          = cx2100_open,
+  .ndo_stop          = cx2100_close,
+  .ndo_start_xmit    = cx2100_start_xmit,
+};
+
+static const struct ethtool_ops cx2100_ethtool_ops = {};
+
+
+
+/** probe (install) the device
+ * 
+ * We check that we have the correct hardware because there are a few Beckhoff
+ * devices with the same pci device ids
+ */
+static int __devinit cx2100_probe(
+        struct pci_dev *dev, 
+        const struct pci_device_id *id
+        )
+{
+  cx2100_private_s  *tp;
+  infoBlock_s       *ecMasterInfoBlock;
+  void __iomem      *ioaddr;
+  struct net_device *netDev;
+  int                wasEnabled;
+  int                res;
+  int                i;
+  res = 0;
+  
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_probe");
+  
+  
+  // enable the device so we can talk to it
+  // check if the device was already enabled
+  wasEnabled = pci_is_enabled(dev);
+  if (!wasEnabled)
+  {
+    res = pci_enable_device(dev);
+    if (res) return res;
+  }
+  
+  // request information from the device
+  res = pci_request_regions(dev, DRIVER_NAME);
+  if (res) goto err_disable;
+  
+  
+  // check bar 0 that we have the correct hardware
+  ioaddr = pci_iomap(dev, 0, 0);
+  if (!ioaddr)
+  {
+    dev_err(&dev->dev, "cannot map BAR0, aborting\n");
+    res = -EIO;
+    goto err_regions;
+  }
+  
+  
+  // enable PCI bus-mastering (dma)
+  pci_set_master(dev);
+  
+  
+  // check that we have the correct device type (ie contains an EC Master)
+  // and return its information block
+  res = cx2100_matchDevice(ioaddr, &ecMasterInfoBlock);
+  if (res) goto err_bar0;
+  
+  
+  // initialise the net device structure and private data
+  netDev = cx2100_initNetDev(dev);
+  if (IS_ERR(netDev))
+  {
+    res = PTR_ERR(netDev);
+    goto err_bar0;
+  }
+
+  // fill in netdev info
+  // Note: we will be using skb_copy_and_csum_dev to calc the checksum
+  netDev->netdev_ops      = &cx2100_netdev_ops;
+  netDev->ethtool_ops     = &cx2100_ethtool_ops;
+  netDev->watchdog_timeo  = TX_TIMEOUT;
+  netDev->features       |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;
+  netDev->irq             = dev->irq;
+
+  
+  // set up the EtherCAT master
+  tp = netdev_priv(netDev);
+  tp->bar0_addr         = ioaddr;
+  tp->ecMasterInfoBlock = ecMasterInfoBlock;
+  tp->ecMasterFnBlock   = tp->bar0_addr + ecMasterInfoBlock->_ecMasterDMA.fnAddrOffset;
+  tp->ecManagement      = ((void *)tp->ecMasterFnBlock) + tp->ecMasterFnBlock->miiOffset;
+  tp->linkOK            = (tp->ecManagement->macFilter.linkStatus != 0);
+
+  
+  // fill in netDev mac address
+  for (i = 0; i < 6; i++)
+  {
+    ((u8 *)(netDev->dev_addr))[i] = tp->ecManagement->macFilter.macAddr[i];
+  }
+  memcpy(netDev->perm_addr, netDev->dev_addr, netDev->addr_len);
+  MSG_INFO("EtherCAT Master, MAC address: %02x:%02x:%02x:%02x:%02x:%02x, Link: %s\n", 
+           tp->ecManagement->macFilter.macAddr[0],
+           tp->ecManagement->macFilter.macAddr[1],
+           tp->ecManagement->macFilter.macAddr[2],
+           tp->ecManagement->macFilter.macAddr[3],
+           tp->ecManagement->macFilter.macAddr[4],
+           tp->ecManagement->macFilter.macAddr[5],
+           tp->linkOK ? "Connected" : "Disconnected");
+  
+  
+  // map bar2 IO
+  tp->bar2_addr = pci_iomap(dev, 2, 0);
+  if (!tp->bar2_addr)
+  {
+    dev_err(&dev->dev, "cannot map BAR2, aborting\n");
+    res = -EIO;
+    goto err_netDev;
+  }
+  
+  
+  // offer device to EtherCAT master module
+  tp->ecdev = ecdev_offer(netDev, cx2100_poll, THIS_MODULE);
+  if (!tp->ecdev)
+  {
+    MSG_WARN("EtherCAT Master device %s (%p) not utilized as an EtherCAT master\n", netDev->name, netDev);
+    res = -EBUSY;
+    goto err_bar2;
+  }
+  
+  
+  // open the device
+  if (ecdev_open(tp->ecdev))
+  {
+    goto err_noMaster;
+  }
+  
+
+  // set netDev ref in pci device
+  pci_set_drvdata(dev, netDev);
+
+
+  MSG_INFO("module inited\n");
+  
+  
+  // success
+  return 0;
+  
+
+err_noMaster :
+  // not opened by the ec master
+  ecdev_withdraw(tp->ecdev);
+  tp->ecdev = NULL;
+             
+err_bar2 :
+  // unmap bar2
+  pci_iounmap(dev, tp->bar2_addr);
+  tp->bar2_addr = NULL;
+  tp->bar0_addr = NULL;
+
+err_netDev :
+  free_netdev(netDev);
+
+err_bar0 :
+  // release dma master
+  pci_clear_master(dev); 
+  // unmap bar0
+  pci_iounmap(dev, ioaddr);
+  
+err_regions :
+  // release regions
+  pci_release_regions(dev);
+  
+err_disable :
+  // failure, re-disable the device
+  if (!wasEnabled) pci_disable_device(dev);
+
+  
+  // return error
+  return res;
+}
+
+
+
+/** remove (shutdown) the device
+ */
+static void __devexit cx2100_remove(
+        struct pci_dev *dev
+        )
+{
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_remove");
+  
+  if (dev)
+  {
+    struct net_device *netDev = pci_get_drvdata(dev);
+    
+    if (netDev)
+    {
+      cx2100_private_s *tp = netdev_priv(netDev);
+      
+    	flush_scheduled_work();
+
+      if (tp)
+      {
+        if (tp->ecdev)
+        {
+          ecdev_close(tp->ecdev);
+          ecdev_withdraw(tp->ecdev);
+        }
+        if (tp->bar0_addr)
+        {
+          pci_iounmap(dev, tp->bar0_addr);
+        }
+      }
+      
+      free_netdev(netDev);
+    	pci_set_drvdata(dev, NULL);
+
+      // clean up and disable
+      pci_release_regions(dev);
+      pci_clear_master(dev);  
+      pci_disable_device(dev);
+    }
+  }
+  
+  MSG_INFO("module removed\n");
+}
+
+
+
+
+/** devices the driver handles
+ */
+struct pci_device_id cx2100_ids[] =
+{
+  {VENDOR_ID, DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+  {}  // end of list
+};
+
+
+
+/** driver struct
+ */
+struct pci_driver cx2100 = 
+{
+  .name     = DRIVER_NAME,
+  .id_table = cx2100_ids,
+  .probe    = cx2100_probe,
+  .remove   = __devexit_p(cx2100_remove)
+};
+
+
+
+/** Initialize and register the driver 
+ */
+static int __init cx2100_init_module(void)
+{
+  MSG_INFO("EtherCAT-capable Beckhoff CX2100 EtherCAT Network Driver (Revision 2)\n");
+  
+  
+  // check out if we should be in verbose mode
+  if (verbosityLevel <= 0)
+  {
+    MSG_INFO("CX2100 Verbose Mode Off\n");
+  }
+  else
+  {
+    MSG_INFO("CX2100 Verbose Mode On, Level %d\n", verbosityLevel);
+  }
+
+  
+  return pci_register_driver(&cx2100);
+}
+
+
+
+/** Unregister the driver
+ */
+static void __exit cx2100_cleanup_module(void)
+{
+  MSG_VERBOSE(VERBOSITY_FUNCTIONCALL, "Function Call: %s", "cx2100_cleanup_module");
+  
+  pci_unregister_driver(&cx2100);
+}
+
+
+
+module_init(cx2100_init_module);
+module_exit(cx2100_cleanup_module);
+
