Initial revision
authorheiko
Wed, 16 Jan 2002 14:01:55 +0100
changeset 0 c9b8efdb5369
child 1 14e484c7b1bf
child 2 3345048104fc
Initial revision
GNU_GPL
Makefile
README
me8100-driver
me8100.c
me8100.h
me8100_test_counter/Makefile
me8100_test_counter/me8100_test_counter.c
me8100_test_dio/Makefile
me8100_test_dio/me8100_test_dio.c
me8100_test_int/Makefile
me8100_test_int/me8100_test_int.c
pci-compat.h
sysdep.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GNU_GPL	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,348 @@
+NOTE!
+	The copyright for the file me2600_firmware.c is different
+	and is stated in the file. Please read this copyright
+	information too!
+
+
+----------------------------------------
+
+		    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.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <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) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,25 @@
+# Makefile for the Meilhaus me8100 driver module
+# If you are in the directory /me8100 where the Makefile and the sources
+# resides, you can use the make command with following parmeters:
+# $make           	generates me8100.o which is the driver module
+# $make test8100   	generates the executable test8100
+# $make test8100i  	generates the executable test8100i
+# $make clean     	deletes all files including *.o and *~ 
+
+me8100.o:me8100.c me8100.h
+	gcc -c me8100.c -Wall -O
+
+test8100:test8100.o
+	gcc -o test8100 test8100.o 
+
+test8100.o:test8100.c me8100.h
+	gcc -c test8100.c -Wall -O
+
+test8100i:test8100i.o
+	gcc -o test8100i test8100i.o 
+
+test8100i.o:test8100i.c me8100.h
+	gcc -c test8100i.c -Wall -O
+
+clean:
+	rm -f *.o *~
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,180 @@
+README file for the Meilhaus ME8100 driver.
+
+
+1) What you should have got:
+============================
+These files should be in your directory:
+	- me8100.c		the source code of the device driver
+	- me8100.h		a header file included from me8100.c
+	- Makefile		the makefile to compile the driver
+	- me8100-driver		a shell script to load and unload the driver
+	- sysdep.h		compatibility file down to kernel 2.0
+	- pci-compat.h		compatibility file for pci stuff down to 2.0
+	- me8100_test_counter.c	test program for counters
+	- me8100_test_dio.c	test program for digital I/O
+	- me8100_test_int.c	test program for interrupts
+	- GNU_GPL		a file containing the General Public License
+	- README		this file :-)
+
+2) How to compile the driver
+===========================
+Be sure to have installed the kernel sources on your system.
+Normally you simply type
+	make
+at your command prompt. If you have got a file me8100.o 
+the make process worked. :-)
+
+
+3) How to install the driver:
+=============================
+3.1) Automatic installation
+---------------------------
+Make sure you are root and run 
+./me8100-driver start
+
+To get rid of the driver use 
+./me8100-driver stop
+
+A look at the me8100-driver shell script will give you
+closer informations about using it.
+
+3.2) Do it yourself installation
+--------------------------------
+Make sure you are root and type
+	insmod me8100.o
+A
+	lsmod
+should print the modules loaded, including me8100.
+
+You must make a node for your driver e.g.
+mknod /dev/me8100 c <major> <minor>
+
+The major number has to be unique! The standard configuration is
+to automatically choose the major number. Type 
+  cat /proc/devices | grep me8100  
+you will get a line like this:
+254 me8100 
+
+In this example 254 is your major number.
+
+Now make a node in your file system:
+mknod /dev/me8100 c <major> <minor>
+
+The major number you already know. <minor> is the board number.
+Your first board is minor=0, second board minor=1 ....
+If you have only one board and the major number given in the example
+you would type:
+(make sure you are root !)
+mknod /dev/me8100 c 254 0
+
+
+That's it! --> Enjoy
+
+
+4) How to use the driver
+========================
+4.1) Open the device
+--------------------
+If you have used the install script described in 3.1), you have four nodes
+for your me8100 board in the /dev directroy, named me8100_0 to me8100_3.
+Before you can use the board, you have to open a path to the board:
+
+int file_handle;
+
+.
+.
+
+main(void){
+
+.
+.
+
+file_handle = open("/dev/me8100_0", 0_RDWR, 0);
+
+.
+.
+
+}
+
+When the device is opend, the board is reset. This means, that the outputs are
+set to "0" and the interrupt logic is disabled.
+
+4.2) IOCTL's of the board
+------------------------
+Now the board is ready to use. In order to work with the board, you have 
+to use the ioctl systemcall. You can find the defined ioctl's in the end of
+the headerfile me8100.h.
+
+In order to use this definitions you have to include the file me8100.h in
+your program. The ioctl systemcall will look something like this:
+
+
+#include "me8100.h"
+
+unsigned short mask;
+
+.
+.
+
+main(void){
+
+.
+.
+
+ioctl(file_handle, ME8100_WRITE_MASK_A, &mask);	
+
+.
+.
+
+}
+
+
+You have to pass the address of a variable to the driver. What type is
+necessary, you can get from the definitions of the ioctl's in
+the headerfile me8100.h. Please look at the source code to get closer 
+information about the purpose of the diffrent ioctl's.
+
+4.3) Interrupt
+--------------
+In order to get information about, how to handle the interrupt 
+functionality of the driver refer to the source code of the test 
+program.
+
+
+4.3) Close the device
+---------------------
+If the work is done, you have to close the device. You have to use the
+systemcall close for this purpose:
+
+close(file_handle);
+
+When the device is closed, the board is reset.
+
+
+===============================================================================
+Because live is never as simple as this you can read on if you got any problem.
+===============================================================================
+
+The driver has been developed and tested under SuSE-Linux 7.1 kernel 2.4.0.
+If you want to know what your kernel version is, type
+	cat /proc/version
+It will not run on older Linux versions then 2.4 down to 2.0.
+It should work with newer kernels, 
+provided Linus did not change the interface for modules again.
+
+If you want to get closer informations about how to use the driver, 
+please refer to the source code of the test programs.
+
+The handbook is also a good source to get informations, about the behavior
+of the board.
+
+- Meilhaus Electronic
+
+
+
+
+ 
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100-driver	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,79 @@
+#! /bin/sh
+# You can use this script to install the device driver
+# with automatically assigned major number. 
+
+# Please modify these lines according to your needs
+PATH=/sbin:/bin:/usr/bin
+module="me8100"
+device="me8100"
+group="users"
+mode="664"
+
+
+### I hope you don't have to modify anything below this line ###
+rc_done="\033[71G\033[32mdone\033[m"
+rc_failed="\033[71G\033[31m\033[1mfailed\033[m"   
+
+return=$rc_done
+
+case "$1" in
+    start)
+	echo -n "Starting me8100 driver "
+	/sbin/insmod -f $module.o || return=$rc_failed
+	rm -f /dev/${device}
+	rm -f /dev/${device}_[0-3]
+	major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"`
+
+	echo -n "with major number $major"
+
+	# Make the device nodes in the dev. file system for 4 boards
+	mknod /dev/${device}_0 c $major 0
+	mknod /dev/${device}_1 c $major 1
+	mknod /dev/${device}_2 c $major 2
+	mknod /dev/${device}_3 c $major 3
+	
+	# Set a default link to the first dev
+	ln -s /dev/${device}_0 /dev/${device}
+
+	# Give appropriate group permissions
+	chgrp $group /dev/${device}_?
+	chmod $mode  /dev/${device}_?
+
+	;;
+    stop)
+	echo -n "Removing me8100 driver "
+	/sbin/rmmod $module
+
+	# Remove the default link
+	rm -f /dev/${device}
+	# Remove the nodes
+	rm -f /dev/${device}_[0-3]
+
+	;;
+    restart)
+	## If first returns OK call the second, if first or
+	## second command fails, set echo return value.
+	$0 stop  &&  $0 start  ||  return=$rc_failed
+	;;
+    status)
+	echo -e "Enty in /proc/modules is:"
+	cat /proc/modules | grep $module
+	echo -e "Entry in /proc/devices is:"
+	cat /proc/devices | grep $module
+	echo -e "Entry in /proc/interrupts is:"
+	cat /proc/interrupts | grep $module
+
+	;;
+    *)
+	echo "Usage: $0 {start|stop|restart|status}"
+	exit 1
+	;;
+esac
+
+# Inform the caller not only verbosely and set an exit status.
+echo -e "$return"
+test "$return" = "$rc_done" || exit 1
+exit 0
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100.c	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,2079 @@
+/* me8100.c */
+/* Device driver for Meilhaus me8100 board.
+ * ========================================
+ *
+ *  Copyright (C) 2001 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *  
+ *  This file 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+      
+/*
+ * Source File : me8100.c
+ * Destination : me8100.o
+ * Author      : GG (Guenter Gebhardt)
+ *
+ * File History: Version   Date       Editor   Action
+ *---------------------------------------------------------------------
+ *               1.00.00   01.01.10   GG       first release
+ *                                                          
+ *               1.00.01   01.02.14   GG       Add new ioctls:
+ *                                              get_board_info
+ *                                              get_int count
+ *
+ *               1.01.00   01.10.08   GG       Port to Kernel 2.4
+ *---------------------------------------------------------------------
+ *
+ * Description:
+ *
+ *   Contains the entire driver-module code for the
+ *   Meilhaus ME-8100 board. However, the definitions and type
+ *   declarations are kept in the headerfile called me8100.h.
+ *
+ */
+
+
+/*
+ * User application could also include the kernel header files. But the
+ * real kernel functions are protected by #ifdef __KERNEL__.
+ */
+#ifndef __KERNEL__
+#  define __KERNEL__
+#endif
+
+
+/* 
+ * This must be defined before module.h is included. Not needed, when
+ * it is a built in driver.
+ */
+#define MODULE
+
+
+/*
+ * If we are compiling for a multiprocessor system, 
+ * we have to define this.
+ */
+#include <linux/config.h>
+#ifdef CONFIG_SMP
+#  define __SMP__
+#endif
+
+
+/* 
+ * Basic facilities for modules.
+ * Defines __module_kernel_version.
+ * Includes <linux/version.h> (UTS_RELEASE, LINUX_VERSION_CODE, ...)
+ */
+#include <linux/module.h>
+
+/*
+ * Needed for the registration of I/O and MEMORY regions.
+ * (request_region, ...)
+ */
+#include <linux/ioport.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/unistd.h>
+
+
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+
+
+/* Compatibility file for kernels from 2.0 up to 2.4 */
+#include "sysdep.h"
+
+
+/* Include-File for the Meilhaus ME-8100 I/O board */
+#include "me8100.h"
+
+
+/* Board specific data are kept global */
+static me8100_info_type info_vec[SORT_COUNT * ME8100_MAX_DEVICES];
+  
+
+/* Number of boards, detected from the BIOS */
+static int me8100_board_count;
+
+
+/* Major Device Number. 0 means to get it automatically from the System */
+static unsigned int major = 0;
+
+
+/* Prototypes */
+static int me8100_open(struct inode *, struct file *);
+static int me8100_release(struct inode *, struct file *);
+static int me8100_ioctl(struct inode *, struct file *, 
+			unsigned int , unsigned long ); 
+static int me8100_fasync(int, struct file *, int);
+static void me8100_isr(int, void *, struct pt_regs *);
+
+static int me8100_init_board(me8100_info_type *, struct pci_dev *);
+static int me8100_reset_board(me8100_info_type *);
+
+static int me8100_read_id_a(unsigned short *, me8100_info_type *);
+static int me8100_write_ctrl_a(unsigned short *, me8100_info_type *);
+static int me8100_res_int_a(me8100_info_type *);
+static int me8100_read_di_a(unsigned short *, me8100_info_type *);
+static int me8100_write_do_a(unsigned short *, me8100_info_type *);
+static int me8100_write_pattern_a(unsigned short *, me8100_info_type *);
+static int me8100_write_mask_a(unsigned short *, me8100_info_type *);
+static int me8100_read_int_di_a(unsigned short *, me8100_info_type *);
+
+static int me8100_read_id_b(unsigned short *, me8100_info_type *);
+static int me8100_write_ctrl_b(unsigned short *, me8100_info_type *);
+static int me8100_res_int_b(me8100_info_type *);
+static int me8100_read_di_b(unsigned short *, me8100_info_type *);
+static int me8100_write_do_b(unsigned short *, me8100_info_type *);
+static int me8100_write_pattern_b(unsigned short *, me8100_info_type *);
+static int me8100_write_mask_b(unsigned short *, me8100_info_type *);
+static int me8100_read_int_di_b(unsigned short *, me8100_info_type *);
+
+static int me8100_write_counter_0(unsigned char *, me8100_info_type *);
+static int me8100_write_counter_1(unsigned char *, me8100_info_type *);
+static int me8100_write_counter_2(unsigned char *, me8100_info_type *);
+static int me8100_read_counter_0(unsigned char *, me8100_info_type *);
+static int me8100_read_counter_1(unsigned char *, me8100_info_type *);
+static int me8100_read_counter_2(unsigned char *, me8100_info_type *);
+static int me8100_setup_counter(unsigned char *, me8100_info_type *);
+
+static int me8100_get_serial(unsigned int *, me8100_info_type *);
+static int me8100_get_name(me8100_version_enum_type *, me8100_info_type *);
+static int me8100_int_occur(me8100_int_occur_type *, me8100_info_type *);
+static int me8100_setup_icsr(unsigned char *, me8100_info_type *);
+static int me8100_read_icsr(unsigned char *, me8100_info_type *);
+static int me8100_get_board_info(me8100_info_type *, me8100_info_type *);
+static int me8100_get_int_count(me8100_int_occur_type *, me8100_info_type *);
+
+
+/* File operations provided by the driver */
+static struct file_operations me8100_file_operations = {
+#ifdef LINUX_24
+  THIS_MODULE,	        /* owner               */
+#endif
+  NULL,		        /* lseek()             */
+  NULL,   		/* read()              */
+  NULL,    		/* write()             */
+  NULL,		        /* readdir()           */
+  NULL,		        /* poll()              */
+  me8100_ioctl,         /* ioctl()             */
+  NULL,   		/* mmap()              */
+  me8100_open,          /* open()              */
+  NULL,                 /* flush()             */
+  me8100_release,       /* release()           */
+  NULL,		        /* fsync()             */
+  me8100_fasync,        /* fasync()            */
+  NULL,		        /* check_media_change()*/
+  NULL,		        /* revalidate()        */
+  NULL                  /* lock()              */
+};
+
+
+
+/*
+ * Routine:                                                                 
+ *   init_module                                                               
+ *                                                                          
+ * Description: 
+ *   This function is executed from the system, when the driver is loaded.
+ *   Actions performed:
+ *     - Searches for PCI hardware.
+ *     - Initializes detected ME8100 boards with me8100_init_board().
+ *     - Installs the driver in the system with register_chrdev().
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name           Type          Access             Description            
+ *--------------------------------------------------------------------------
+ *
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure. 
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+#ifdef CONFIG_PCI
+int init_module(void){
+  int result;
+  unsigned short board_count = 0;
+  unsigned short sort_count;
+  struct pci_dev *pci_dev_ptr = NULL;
+
+  PDEBUG("init_module() is executed\n");
+  
+  /* Set the board context to 0 */
+  memset(&info_vec, 0, sizeof(info_vec));
+
+  if (pci_present()){
+    /* Search for ME8100_A boards */
+    for(sort_count = 0; 
+	sort_count < ME8100_MAX_DEVICES; 
+	sort_count++, board_count++){
+      pci_dev_ptr = pci_find_device(PCI_VENDOR_ID_MEILHAUS,
+				    PCI_DEVICE_ID_MEILHAUS_ME8100_A,
+				    pci_dev_ptr);
+      if(!pci_dev_ptr)
+	break;
+
+      PDEBUG("init_module():ME8100_A found\n");
+      info_vec[board_count].version = ME8100_A;
+      info_vec[board_count].board_count = board_count;
+
+      result = me8100_init_board(&info_vec[board_count], pci_dev_ptr);
+      if (result){
+	printk(KERN_ERR"ME8100:init_module():Can't init board\n");
+	return result;
+      }
+    }
+
+    /* Search for ME8100_B boards */
+    for(sort_count = 0; 
+	sort_count < ME8100_MAX_DEVICES; 
+	sort_count++, board_count++){
+      pci_dev_ptr = pci_find_device(PCI_VENDOR_ID_MEILHAUS,
+				    PCI_DEVICE_ID_MEILHAUS_ME8100_B,
+				    pci_dev_ptr);
+      if(!pci_dev_ptr)
+	break;
+
+      PDEBUG("init_module():ME8100_B found\n");
+      info_vec[board_count].version = ME8100_B;
+      info_vec[board_count].board_count = board_count;
+
+      result = me8100_init_board(&info_vec[board_count], pci_dev_ptr);
+      if (result){
+	printk(KERN_ERR"ME8100:init_module():Can't init board\n");
+	return result;
+      }
+    }
+
+    if (board_count == 0){
+      printk(KERN_ERR"ME8100:init_module():No PCI-Devices found\n");
+      return -ENODEV;
+    }
+
+    me8100_board_count = board_count;
+    PDEBUG("init_module(): %d Boards found\n", me8100_board_count);
+    
+    /* 
+     * Register the driver in the system with major number = 0.
+     * This means that the major number is automatically assigned
+     * from the kernel and returned as result from register_chrdev().
+     */
+    result = register_chrdev(major, ME8100_NAME, &me8100_file_operations);
+    if (result < 0){
+      printk(KERN_ERR"ME8100:init_module():Can't get major no\n");
+      return result;
+    }
+    else{
+      major = result;
+      PDEBUG("init_module():Major = %d\n", major);
+    }
+  } 
+  else{ 
+    printk(KERN_ERR"ME8100:init_module():No PCI-BIOS present !\n");
+    return -ENODEV;
+  }
+  return 0;
+}  
+#else
+return -ENODEV
+#endif
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_init_board                                                        
+ *                                                                          
+ * Description:                                                             
+ *   This function initializes the detected me8100 boards. 
+ *   Actions performed:
+ *     - Get the baseaddresses of the PLX and the ME8100.
+ *     - If PLX bug detected, start workaround.
+ *     - Initializes the device info structure.
+ *     - Resets the board with me8100_reset_board().        
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name           Type                 Access Description                 
+ *--------------------------------------------------------------------------
+ *   *pci_dev_ptr   struct pci_dev       read   List with all pci devices.
+ *   *info          me8100_info_type     read   Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else failure.                          
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * 
+ * Modification:                                                            
+ *   01.09.14  Don't get baseaddresses from struct pci_dev, 
+ *             for compatibility. (GG)
+ *
+ */
+static int me8100_init_board(me8100_info_type *info, 
+			     struct pci_dev *pci_dev_ptr){
+  int result = 0;
+  unsigned int plx_regbase_tmp;
+  unsigned int me8100_regbase_tmp;
+  unsigned int swap_regbase_tmp;
+  unsigned int regbase_tmp;
+
+  PDEBUG("me8100_init_board() is executed\n");
+
+
+  /*--------------------------- plx regbase ---------------------------------*/
+
+  result = pci_read_config_dword(pci_dev_ptr, 
+				 PCI_BASE_ADDRESS_1, 
+				 &plx_regbase_tmp);
+  if(result != PCIBIOS_SUCCESSFUL){
+    printk(KERN_ERR"ME8100:Can't get PCI_BASE_ADDRESS_1\n");
+    return result;
+  }
+  PDEBUG("me8100_init_board():PCI base 0 = 0x%04X\n", plx_regbase_tmp);
+
+  result = pci_read_config_dword(pci_dev_ptr, 
+				 PCI_BASE_ADDRESS_5,
+				 &swap_regbase_tmp);
+  if(result != PCIBIOS_SUCCESSFUL){
+    printk(KERN_ERR"ME8100:Can't get PCI_BASE_ADDRESS_5\n");
+    return result;
+  }
+  PDEBUG("me8100_init_board():PCI base 5 = 0x%04X\n", swap_regbase_tmp);
+
+  if(!swap_regbase_tmp){
+    printk(KERN_WARNING"ME8100:me8100_init_board:Swap not present\n");
+  }
+
+  /* 
+   * This is the PLX bug workaround.
+   * If bit 7 is set in the plx_regbase, 
+   * the plx registers maybe not readable.
+   */
+  if(plx_regbase_tmp & 0x0080){
+    printk(KERN_WARNING"ME8100:me8100_init_board():PLX-BUG detected\n");
+
+    if(PLX_WORKAROUND_ENABLE){
+      if(swap_regbase_tmp){
+	regbase_tmp = plx_regbase_tmp;
+	plx_regbase_tmp = swap_regbase_tmp;
+	swap_regbase_tmp = regbase_tmp;
+	result = pci_write_config_dword(pci_dev_ptr, 
+					PCI_BASE_ADDRESS_1, 
+					plx_regbase_tmp);
+	if(result != PCIBIOS_SUCCESSFUL)
+	  return result;
+
+	result = pci_write_config_dword(pci_dev_ptr, 
+					PCI_BASE_ADDRESS_5, 
+					swap_regbase_tmp);
+	if(result != PCIBIOS_SUCCESSFUL)
+	  return result;
+      }
+      else{
+	plx_regbase_tmp -= 0x80;
+	result = pci_write_config_dword(pci_dev_ptr, 
+					PCI_BASE_ADDRESS_1, 
+					plx_regbase_tmp);
+	if(result != PCIBIOS_SUCCESSFUL)
+	  return result;
+      }
+    }
+  }
+
+  if(!(plx_regbase_tmp & PCI_BASE_ADDRESS_SPACE)){
+    printk(KERN_ERR"ME8100:me8100_init_board():PLX space is not MEM\n");
+    return -EIO;
+  }
+  info->plx_regbase_size = PLX_BASE_SIZE;
+  info->plx_regbase = plx_regbase_tmp & PCI_BASE_ADDRESS_IO_MASK;
+  PDEBUG("me8100_init_board():IO at 0x%04X\n", info->plx_regbase);
+  
+
+  /*--------------------------- me8100 regbase ------------------------------*/
+
+  result = pci_read_config_dword(pci_dev_ptr, 
+				 PCI_BASE_ADDRESS_2,
+				 &me8100_regbase_tmp);
+  if(result != PCIBIOS_SUCCESSFUL){
+    printk(KERN_ERR"ME8100:Can't get PCI_BASE_ADDRESS_2\n");
+    return result;
+  }
+  PDEBUG("me8100_init_board():PCI base 2 = 0x%04X\n", me8100_regbase_tmp);
+
+  if(!(me8100_regbase_tmp & PCI_BASE_ADDRESS_SPACE)){
+    printk(KERN_ERR"ME8100:me8100_init_board():ME8100 space is not IO\n");
+    return -EIO;
+  }
+  info->me8100_regbase_size = ME8100_BASE_SIZE;
+  info->me8100_regbase = me8100_regbase_tmp & PCI_BASE_ADDRESS_IO_MASK;
+  PDEBUG("me8100_init_board():IO at 0x%04X\n", info->me8100_regbase);
+  
+
+  /*--------------------------- init device info ----------------------------*/
+
+  result = pci_read_config_dword(pci_dev_ptr, 0x2C, &info->serial_no);
+  if(result != PCIBIOS_SUCCESSFUL){
+    printk(KERN_ERR"ME8100:me8100_init_board:Can't get serial_no\n");
+    return result;
+  }
+  PDEBUG("me8100_init_board():serial_no = 0x%08X\n", info->serial_no);
+
+  result = pci_read_config_byte(pci_dev_ptr, 0x08, &info->hw_revision);
+  if(result != PCIBIOS_SUCCESSFUL){
+    printk(KERN_ERR"ME8100:me8100_init_board():Can't get hw_revision\n");
+    return result;
+  }
+  PDEBUG("me8100_init_board():hw_revision = 0x%02X\n", info->hw_revision);
+
+  info->vendor_id = pci_dev_ptr->vendor;
+  PDEBUG("me8100_init_board():vendor_id = 0x%04X\n", info->vendor_id);
+
+  info->device_id = pci_dev_ptr->device;
+  PDEBUG("me8100_init_board():device_id = 0x%04X\n", info->device_id);
+
+  info->pci_dev_no = PCI_SLOT(pci_dev_ptr->devfn);
+  PDEBUG("me8100_init_board():pci_dev_no = 0x%02X\n", info->pci_dev_no);
+
+  info->pci_func_no = PCI_FUNC(pci_dev_ptr->devfn);
+  PDEBUG("me8100_init_board():pci_func_no = 0x%02X\n", info->pci_func_no);
+
+  info->pci_bus_no = pci_dev_ptr->bus->number;
+  PDEBUG("me8100_init_board():pci_bus_no = 0x%02X\n", info->pci_bus_no);
+
+  info->int_line = pci_dev_ptr->irq;
+  PDEBUG("me8100_init_board():int_line = %d\n", info->int_line);
+
+  info->int_count_1 = 0;
+  info->int_count_2 = 0;
+  info->int1 = 0;
+  info->int2 = 0;
+  info->file_ptr = NULL;
+  info->board_in_use = 0;
+  spin_lock_init(&info->use_lock);
+
+  
+  /*--------------------------- Reset the board -----------------------------*/
+
+  result = me8100_reset_board(info);
+  if(result){
+    printk(KERN_ERR"ME8100:me8100_init_board():Can't reset board\n");
+    return result;
+  }
+
+  return 0;
+}
+
+
+
+/*
+ * Routine:
+ *   me8100_reset_board                                                     
+ *                                                                          
+ * Description:                                                             
+ *   This function resets the ME-81000 board. 
+ *   Actions performed:
+ *     - Disables the interruptlogic of the plx.
+ *     - Disables the interrupts on the ME-8100.
+ *     - Sets the digital I/O to high impedance.
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name           Type          Access    Description                     
+ *--------------------------------------------------------------------------
+ *   board_count    int           read      Index of the detected board     
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.                     
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_reset_board(me8100_info_type *info){
+  unsigned char icsr = 0x12;
+
+  PDEBUG("me8100_reset_board() is executed\n");
+
+  /* Disable the Interrupt logic of the plx */
+  PDEBUG("me8100_reset_board(): plx_mode = 0x%X\n", icsr);
+  outb(icsr, info->plx_regbase + PLX_ICSR);
+  
+  /* Ports to high impedance, interrupts deactivated */
+  outw(0x00, info->me8100_regbase + ME8100_CTRL_REG_A);
+  outw(0x0000, info->me8100_regbase + ME8100_CTRL_REG_B);
+
+  /* Reset any pending interrupt */
+  inw(info->me8100_regbase + ME8100_RES_INT_REG_A);
+  inw(info->me8100_regbase + ME8100_RES_INT_REG_B);
+
+  return 0;
+}
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_open                                                              
+ *                                                                          
+ * Description:                                                             
+ *   Function, which is executed, when a userprogramm makes the syscall     
+ *   open. 
+ *   Actions performed:
+ *     - It installs the drivers interrupt service routine 
+ *       me8100_isr in the system.
+ *     - It remarks the board as used in the global data structure.
+ *                                                                          
+ * Parameter list:
+ *   Name       Type             Access    Description
+ *--------------------------------------------------------------------------
+ *   *inode_ptr struct inode     read      Pointer to device inode.
+ *   *file_ptr  struct file      read      Ponnter to file structure.
+ *
+ *--------------------------------------------------------------------------
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0 else failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ *   01.10.04  Guard board_in_use with spin_lock, cause of race condition
+ *             when compiling for SMP.
+ */
+static int me8100_open(struct inode *inode_ptr, struct file *file_ptr){
+  int minor = 0;
+  int err = 0;
+
+  PDEBUG("me8100_open() is executed\n");
+
+  minor = MINOR(inode_ptr->i_rdev);
+
+  if(minor >= me8100_board_count){
+    printk(KERN_ERR"ME8100:me8100_open():Board %d doesn't exist\n", minor);
+    return -ENODEV;
+  }
+
+  spin_lock(&info_vec[minor].use_lock);
+  if(info_vec[minor].board_in_use){
+    printk(KERN_ERR"ME8100:me8100_open():Board %d already in use\n", minor);
+    spin_unlock(&info_vec[minor].use_lock);
+    return -EBUSY;
+  }
+  info_vec[minor].board_in_use = 1;
+  spin_unlock(&info_vec[minor].use_lock);
+
+  info_vec[minor].file_ptr = file_ptr;
+
+  err = request_irq(info_vec[minor].int_line,
+		    me8100_isr,
+		    SA_INTERRUPT | SA_SHIRQ,
+		    ME8100_NAME,
+		    (void *)&info_vec[minor]);
+  if(err){
+    printk(KERN_ERR"ME8100:me8100_open():Can't get interrupt line");
+    return err;
+  }
+
+  MOD_INC_USE_COUNT;
+
+  return 0;
+}
+
+
+
+/*
+ * Routine:
+ *   me8100_release
+ *
+ * Description:
+ *   Function, which is executed, when the userprogramm makes the syscall
+ *   close. First it resets the board and marks the board as unused in the
+ *   global info_vec. Then it frees the Interrupt requested
+ *   in me8100_open. After that the fasync queue probably installed is
+ *   deleted.
+ *   At last the usecount of the path is decremented.
+ *
+ * Parameter list:
+ *   Name       Type            Access    Description
+ *--------------------------------------------------------------------------
+ *   inode_ptr  struct inode *  read      pointer to the inode structure of 
+ *                                        the system                        
+ *   file_ptr   struct file *   read      pointer to the file structure of  
+ *                                        the system                        
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure                      
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_release(struct inode *inode_ptr, struct file *file_ptr){
+  int minor = 0;
+  int err   = 0;
+  
+  PDEBUG("me8100_release() is executed\n");
+
+  minor = MINOR(inode_ptr->i_rdev);
+
+  err = me8100_reset_board(&info_vec[minor]);
+  if(err){
+    printk(KERN_ERR"ME8100:me8100_release():Can't reset");
+    return err;
+  }
+
+  free_irq(info_vec[minor].int_line, (void *) &info_vec[minor]);
+
+  /* Delete the fasync structure and free memory */
+  me8100_fasync(0, file_ptr, 0);
+
+  info_vec[minor].board_in_use = 0;
+
+  MOD_DEC_USE_COUNT;
+  PDEBUG("me8100_release() is leaved\n");
+  return 0;
+}
+
+
+
+/*
+ * Routine:
+ *   me8100_ioctl
+ *
+ * Description:
+ *   Function, which prvides the functionality of the ME8100 board. This
+ *   function is executed, when a user program executes the systemcall
+ *   ioctl. It checks if the service requested is valid and calls the
+ *   appropriate routine to handle the request.
+ *
+ * Parameter list:
+ *   Name       Type            Access    Description
+ *--------------------------------------------------------------------------
+ *   inode_ptr  struct inode *  read      pointer to the inode structure of
+ *                                        the system
+ *   file_ptr   struct file *   read      pointer to the file structure of
+ *                                        the system
+ *   service    unsigned int    read      requested service
+ *   arg        unsigned long   r/w       address of the structure with
+ *                                        user data and parameters
+ *
+ * Result:
+ *   On success the return value is 0, else is failure
+ *--------------------------------------------------------------------------
+ * Author: GG
+ * Modification:
+ *   01.02.14   GG       Add new ioctls:
+ *                        get_board_info
+ *                        get_int count
+ */
+static int me8100_ioctl(struct inode * inode_ptr, 
+			struct file *file_ptr, 
+			unsigned int service, 
+			unsigned long arg){
+
+  int minor = 0;
+
+  PDEBUG("me8100_ioctl() is executed\n");
+
+  minor = MINOR(inode_ptr->i_rdev);      
+
+  if(_IOC_TYPE(service) != ME8100_MAGIC){
+    printk(KERN_ERR"ME8100:Invalid ME8100_MAGIC\n");
+    return -EINVAL;
+  }
+  if(_IOC_NR(service) > ME8100_IOCTL_MAXNR){
+    printk(KERN_ERR"ME8100:Service number ME8100 to high\n");
+    return -EINVAL;
+  }
+
+  switch(service){
+  case ME8100_READ_ID_A:
+    return me8100_read_id_a((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_WRITE_CTRL_A:
+    return me8100_write_ctrl_a((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_RES_INT_A:
+    return me8100_res_int_a(&info_vec[minor]);
+  case ME8100_READ_DI_A:
+    return me8100_read_di_a((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_WRITE_DO_A:
+    return me8100_write_do_a((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_WRITE_PATTERN_A:
+    return me8100_write_pattern_a((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_WRITE_MASK_A:
+    return me8100_write_mask_a((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_READ_INT_DI_A:
+    return me8100_read_int_di_a((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_READ_ID_B:
+    return me8100_read_id_b((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_WRITE_CTRL_B:
+    return me8100_write_ctrl_b((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_RES_INT_B:
+    return me8100_res_int_b(&info_vec[minor]);
+  case ME8100_READ_DI_B:
+    return me8100_read_di_b((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_WRITE_DO_B:
+    return me8100_write_do_b((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_WRITE_PATTERN_B:
+    return me8100_write_pattern_b((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_WRITE_MASK_B:
+    return me8100_write_mask_b((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_READ_INT_DI_B:
+    return me8100_read_int_di_b((unsigned short *) arg, &info_vec[minor]);
+  case ME8100_WRITE_COUNTER_0:
+    return me8100_write_counter_0((unsigned char *) arg, &info_vec[minor]);
+  case ME8100_WRITE_COUNTER_1:
+    return me8100_write_counter_1((unsigned char *) arg, &info_vec[minor]);
+  case ME8100_WRITE_COUNTER_2:
+    return me8100_write_counter_2((unsigned char *) arg, &info_vec[minor]);
+  case ME8100_READ_COUNTER_0:
+    return me8100_read_counter_0((unsigned char *) arg, &info_vec[minor]);
+  case ME8100_READ_COUNTER_1:
+    return me8100_read_counter_1((unsigned char *) arg, &info_vec[minor]);
+  case ME8100_READ_COUNTER_2:
+    return me8100_read_counter_2((unsigned char *) arg, &info_vec[minor]);
+  case ME8100_SETUP_COUNTER:
+    return me8100_setup_counter((unsigned char *) arg, &info_vec[minor]);
+  case ME8100_GET_SERIAL:
+    return me8100_get_serial((unsigned int *) arg, &info_vec[minor]);
+  case ME8100_GET_NAME:
+    return me8100_get_name((me8100_version_enum_type *) arg, &info_vec[minor]);
+  case ME8100_INT_OCCUR:    
+    return me8100_int_occur((me8100_int_occur_type *) arg, &info_vec[minor]);
+  case ME8100_SETUP_ICSR:
+    return me8100_setup_icsr((unsigned char *) arg, &info_vec[minor]);
+  case ME8100_READ_ICSR:
+    return me8100_read_icsr((unsigned char *) arg, &info_vec[minor]);
+  case ME8100_GET_BOARD_INFO:
+    return me8100_get_board_info((me8100_info_type *)arg, &info_vec[minor]);
+  case ME8100_GET_INT_COUNT:
+    return me8100_get_int_count((me8100_int_occur_type *)arg,&info_vec[minor]);
+  default:
+    return -EINVAL;
+  }
+  
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_fasync                                                           
+ *                                                                          
+ * Description:                                                             
+ *   This function is executed, when a user program executes the systemcall 
+ *   fcntl. It remarks the processes who want to be informed asynchronous   
+ *   in a fasync structure and saves the pointer to this structure in the   
+ *   file structure of the path.                                           
+ *
+ * Parameter list:                                                          
+ *   Name       Type            Access    Description                       
+ *--------------------------------------------------------------------------
+ *   fd         int             read      File descriptor of the open path.
+ *   file_ptr   struct file *   read      Pointer to the file structure of  
+ *                                        the system.
+ *   mode       int             read      Requested operation,
+ *                                        passed to the fasync_helper.
+ *                                                                          
+ * Result:                                                                  
+ *   All < 0 marks a failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modufication:                                                            
+ */
+static int me8100_fasync(int fd, struct file *file_ptr, int mode){
+  int val = 0;
+  struct fasync_struct *fasync_ptr;
+
+  fasync_ptr = file_ptr->private_data;
+
+  PDEBUG("me8100_fasync() is executed\n");
+ 
+  val = fasync_helper(fd, file_ptr, mode, &fasync_ptr);
+  file_ptr->private_data = fasync_ptr;
+  return val;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_get_board_info                                                  
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by me8100_ioctl, in order to get the global    
+ *   variables for a specific board from the info_vec.                      
+ * Parameter list:                                                          
+ *   Name    Type                      Access  Description                  
+ *--------------------------------------------------------------------------
+ *   *arg    me8100_info_type          w       Carries the value to user.
+ *   *info   me8100_info_type          r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_get_board_info(me8100_info_type *arg, 
+				 me8100_info_type *info){
+
+  PDEBUG("me8100_get_board_info() is executed\n");
+  
+  if(copy_to_user(arg, info, sizeof(me8100_info_type)))
+    return -EFAULT;
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_id_a                                                       
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read the      
+ *   function id register a.                                                
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                      Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short            w       Carries the value to user.
+ *   *info   me8100_info_type          r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_id_a(unsigned short *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned short id_a;  
+
+  PDEBUG("me8100_read_id_a() is executed\n");
+  
+  id_a = inw(info->me8100_regbase + ME8100_ID_REG_A);    
+  err = copy_to_user(arg, &id_a, sizeof(id_a));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_ctrl_a                                                    
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to setup the     
+ *   CTRL register a.                                                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                      Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short            r       Carries the value from user.
+ *   *info   me8100_info_type          r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_ctrl_a(unsigned short *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned short ctrl_a = 0;  
+
+  PDEBUG("me8100_write_ctrl_a() is executed\n");
+  
+  err = copy_from_user(&ctrl_a, arg, sizeof(ctrl_a));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_ctrl_a:ctrl_a=0x%04X\n", ctrl_a);
+  PDEBUG("me8100_write_ctrl_a:To offset=0x%02X\n", ME8100_CTRL_REG_A);
+  outw(ctrl_a, info->me8100_regbase + ME8100_CTRL_REG_A);    
+ 
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_res_int_a                                                       
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to reset the     
+ *   INTR bit a.                                                            
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                      Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *info   me8100_info_type          r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_res_int_a(me8100_info_type *info){
+
+  PDEBUG("me8100_res_int_a() is executed\n");
+  
+  inw(info->me8100_regbase + ME8100_RES_INT_REG_A);    
+
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_di_a                                                       
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read a value  
+ *   from the digital input a.                                              
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             w       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_di_a(unsigned short *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned short di_a;  
+
+  PDEBUG("me8100_read_di_a() is executed\n");
+  
+  di_a = inw(info->me8100_regbase + ME8100_DI_REG_A);    
+  err = copy_to_user(arg, &di_a, sizeof(di_a));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_do_a                                                      
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to write a value 
+ *   to the digital output a.                                               
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure                      
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_do_a(unsigned short *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned short do_a = 0;  
+
+  PDEBUG("me8100_write_do_a() is executed\n");
+  
+  err = copy_from_user(&do_a, arg, sizeof(do_a));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_do_a:do_a=0x%04X\n", do_a);
+  PDEBUG("me8100_write_do_a:To offset=0x%02X\n", ME8100_DO_REG_A);
+  outw(do_a, info->me8100_regbase + ME8100_DO_REG_A);    
+ 
+  return 0;
+}
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_pattern_a                                                 
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to setup the     
+ *   pattern register a.                                                    
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_pattern_a(unsigned short *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned short pattern_a = 0;  
+
+  PDEBUG("me8100_write_pattern_a() is executed\n");
+  
+  err = copy_from_user(&pattern_a, arg, sizeof(pattern_a));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_pattern_a:pattern_a=0x%04X\n", pattern_a);
+  PDEBUG("me8100_write_pattern_a:To offset=0x%02X\n", 
+	 ME8100_PATTERN_REG_A);
+  outw(pattern_a, info->me8100_regbase + ME8100_PATTERN_REG_A);    
+ 
+  return 0;
+}
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_mask_a                                                    
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to setup the     
+ *   mask register a.                                                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_mask_a(unsigned short *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned short mask_a = 0;  
+
+  PDEBUG("me8100_write_mask_a() is executed\n");
+  
+  err = copy_from_user(&mask_a, arg, sizeof(mask_a));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_mask_a:mask_a=0x%04X\n", mask_a);
+  PDEBUG("me8100_write_mask_a:To offset=0x%02X\n", ME8100_MASK_REG_A);
+  outw(mask_a, info->me8100_regbase + ME8100_MASK_REG_A);    
+ 
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_int_di_a                                                   
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read the      
+ *   word from the digital input a, beeing actual at this moment the        
+ *   Interrupt rises.                                                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             w       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_int_di_a(unsigned short *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned short int_di_a;  
+
+  PDEBUG("me8100_read_int_di_a() is executed\n");
+  
+  int_di_a = inw(info->me8100_regbase + ME8100_INT_DI_REG_A);    
+  err = copy_to_user(arg, &int_di_a, sizeof(int_di_a));
+  if(err){
+    PDEBUG("me8100_read_int_di_a:Failed to copy data\n");
+    return err;
+  }
+  return 0;
+}
+
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_id_b                                                       
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read the      
+ *   function id register b.                                                
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             w       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_id_b(unsigned short *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned short id_b;  
+
+  PDEBUG("me8100_read_id_b() is executed\n");
+  
+  id_b = inw(info->me8100_regbase + ME8100_ID_REG_B);    
+  err = copy_to_user(arg, &id_b, sizeof(id_b));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_ctrl_b                                                    
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to setup the     
+ *   CTRL register b.                                                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   arg     unsigned short *           r       carries the value from user 
+ *   minor   int                        r       specifies the board         
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_ctrl_b(unsigned short *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned short ctrl_b = 0;  
+
+  PDEBUG("me8100_write_ctrl_b() is executed\n");
+  
+  err = copy_from_user(&ctrl_b, arg, sizeof(ctrl_b));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_ctrl_b:ctrl_b=0x%04X\n", ctrl_b);
+  PDEBUG("me8100_write_ctrl_b:To offset=0x%02X\n", ME8100_CTRL_REG_B);
+  outw(ctrl_b, info->me8100_regbase + ME8100_CTRL_REG_B);    
+ 
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_res_int_b                                                       
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to reset the     
+ *   INTR bit b.                                                            
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_res_int_b(me8100_info_type *info){
+
+  PDEBUG("me8100_res_int_b() is executed\n");
+  inw(info->me8100_regbase + ME8100_RES_INT_REG_B);    
+
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_di_b                                                       
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read a value  
+ *   from the digital input b.                                              
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             w       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_di_b(unsigned short *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned short di_b;  
+
+  PDEBUG("me8100_read_di_b() is executed\n");
+  
+  di_b = inw(info->me8100_regbase + ME8100_DI_REG_B);    
+  err = copy_to_user(arg, &di_b, sizeof(di_b));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_do_b                                                      
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to write a value 
+ *   to the digital output b.                                               
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_do_b(unsigned short *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned short do_b = 0;  
+
+  PDEBUG("me8100_write_do_b() is executed\n");
+  
+  err = copy_from_user(&do_b, arg, sizeof(do_b));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_do_b:do_b=0x%04X\n", do_b);
+  PDEBUG("me8100_write_do_b:To offset=0x%02X\n", ME8100_DO_REG_B);
+  outw(do_b, info->me8100_regbase + ME8100_DO_REG_B);    
+ 
+  return 0;
+}
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_pattern_b                                                 
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to setup the     
+ *   pattern register b.                                                    
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_pattern_b(unsigned short *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned short pattern_b = 0;  
+
+  PDEBUG("me8100_write_pattern_b() is executed\n");
+  
+  err = copy_from_user(&pattern_b, arg, sizeof(pattern_b));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_pattern_b:pattern_b=0x%04X\n", pattern_b);
+  PDEBUG("me8100_write_pattern_b:To offset=0x%02X\n", 
+	 ME8100_PATTERN_REG_B);
+  outw(pattern_b, info->me8100_regbase + ME8100_PATTERN_REG_B);    
+ 
+  return 0;
+}
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_mask_b                                                    
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to setup the     
+ *   mask register b.                                                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_mask_b(unsigned short *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned short mask_b = 0;  
+
+  PDEBUG("me8100_write_mask_b() is executed\n");
+  
+  err = copy_from_user(&mask_b, arg, sizeof(mask_b));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_mask_b:mask_b=0x%04X\n", mask_b);
+  PDEBUG("me8100_write_mask_b:To offset=0x%02X\n", ME8100_MASK_REG_B);
+  outw(mask_b, info->me8100_regbase + ME8100_MASK_REG_B);    
+ 
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_int_di_b                                                   
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read the      
+ *   word from the digital input b, beeing actual at the moment the         
+ *   Interrupt rises.                                                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned short             w       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_int_di_b(unsigned short *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned short int_di_b;  
+
+  PDEBUG("me8100_read_int_di_b() is executed\n");
+  
+  int_di_b = inw(info->me8100_regbase + ME8100_INT_DI_REG_B);    
+  err = copy_to_user(arg, &int_di_b, sizeof(int_di_b));
+  if(err){
+    PDEBUG("me8100_read_int_di_b:Failed to copy data\n");   
+    return err;
+  }
+  return 0;
+}
+
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_counter_0                                                 
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to write a byte  
+ *   to the counter 0                                                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned char              r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_counter_0(unsigned char *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned char value = 0;  
+
+  PDEBUG("me8100_write_counter_0() is executed\n");
+  
+  err = copy_from_user(&value, arg, sizeof(value));
+  if(err)
+    return err;
+ 
+  PDEBUG("me8100_write_counter_0:counter_0=0x%02X\n", value);
+  PDEBUG("me8100_write_counter_0:To offset=0x%02X\n", 
+	 ME8100_COUNTER_REG_0);
+  outb(value, info->me8100_regbase + ME8100_COUNTER_REG_0);    
+ 
+  return 0;
+}
+
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_counter_1                                                 
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to write a byte  
+ *   to the counter 1                                                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned char              r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_counter_1(unsigned char *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned char value = 0;  
+
+  PDEBUG("me8100_write_counter_1() is executed\n");
+  
+  err = copy_from_user(&value, arg, sizeof(value));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_counter_1:counter_1=0x%02X\n", value);
+  PDEBUG("me8100_write_counter_1:To offset=0x%02X\n", 
+	 ME8100_COUNTER_REG_1);
+  outb(value, info->me8100_regbase + ME8100_COUNTER_REG_1);    
+ 
+  return 0;
+}
+
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_write_counter_2                                                 
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to write a byte  
+ *   to the counter 2                                                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned char              r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_write_counter_2(unsigned char *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned char value = 0;  
+
+  PDEBUG("me8100_write_counter_2() is executed\n");
+  
+  err = copy_from_user(&value, arg, sizeof(value));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_write_counter_2:counter_2=0x%02X\n", value);
+  PDEBUG("me8100_write_counter_2:To offset=0x%02X\n", 
+	 ME8100_COUNTER_REG_2);
+  outb(value, info->me8100_regbase + ME8100_COUNTER_REG_2);    
+ 
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_counter_0                                                  
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read a byte   
+ *   from the counter 0                                                     
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned char              r       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_counter_0(unsigned char *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned char value;  
+
+  PDEBUG("me8100_read_counter_0() is executed\n");
+  
+  value = inb(info->me8100_regbase + ME8100_COUNTER_REG_0);    
+  err = copy_to_user(arg, &value, sizeof(value));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_counter_1                                                  
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read a byte   
+ *   from the counter 1                                                     
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned char              r       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_counter_1(unsigned char *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned char value;  
+
+  PDEBUG("me8100_read_counter_1() is executed\n");
+  
+  value = inb(info->me8100_regbase + ME8100_COUNTER_REG_1);    
+  err = copy_to_user(arg, &value, sizeof(value));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_counter_2                                                  
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read a byte   
+ *   from the counter 2                                                     
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned char              r       Carries the value to user.
+ *   minor   int                        r       specifies the board         
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_counter_2(unsigned char *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned char value;  
+
+  PDEBUG("me8100_read_counter_2() is executed\n");
+  
+  value = inb(info->me8100_regbase + ME8100_COUNTER_REG_2);    
+  err = copy_to_user(arg, &value, sizeof(value));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+/*
+ * Routine:                                                                  
+ *   me8100_setup_counter                                                    
+ *                                                                           
+ * Description:                                                              
+ *   This function is called by the me8100_ioctl, in order to setup the      
+ *   the counter.                                                            
+ *                                                                           
+ * Parameter list:                                                           
+ *   Name    Type                       Access  Description                  
+ *-------------------------------------------------------------------------- 
+ *   *arg    unsigned char              r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                           
+ * Result:                                                                   
+ *   On success the return value is 0, else is failure.
+ *-------------------------------------------------------------------------- 
+ * Author: GG                                                                
+ * Modification:                                                             
+ */
+static int me8100_setup_counter(unsigned char *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned char value = 0;  
+
+  PDEBUG("me8100_setup_counter() is executed\n");
+  
+  err = copy_from_user(&value, arg, sizeof(value));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_setup_counter_0:ctrl=0x%02X\n", value);
+  PDEBUG("me8100_setup_counter_0():To offset=0x%02X\n", 
+	 ME8100_COUNTER_CTRL_REG);
+  outb(value, info->me8100_regbase + ME8100_COUNTER_CTRL_REG);    
+ 
+  return 0;
+}
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_get_serial                                                      
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to get the       
+ *   serial number of the board.                                            
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned int               w       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_get_serial(unsigned int *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned int value;  
+
+  PDEBUG("me8100_get_serial() is executed\n");
+  
+  value = info->serial_no;
+  err = copy_to_user(arg, &value, sizeof(value));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_get_name                                                        
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to get the       
+ *   name of the board.                                                     
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned int               w       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_get_name(me8100_version_enum_type *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned int value;  
+
+  PDEBUG("me8100_get_name() is executed\n");
+  
+  value = info->version;
+  err = copy_to_user(arg, &value, sizeof(value));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_int_occur                                                       
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to find out,     
+ *   which interrupt input rised the last interrupt on a me8100 board.      
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    me8100_int_occur_type      w       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_int_occur(me8100_int_occur_type *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned long flags;
+  me8100_int_occur_type int_occur;
+
+  PDEBUG("me8100_read_int_occur() is executed\n");
+
+  save_flags(flags);
+  cli();
+  int_occur.int1 = info->int1;
+  int_occur.int2 = info->int2;
+  restore_flags(flags);
+
+  err = copy_to_user(arg, &int_occur, sizeof(int_occur));
+  if(err)
+    return err;
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_setup_icsr                                                      
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to write a value 
+ *   to the plx icsr (offset 0x4C)                                          
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned char              r       Carries the value from user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_setup_icsr(unsigned char *arg, me8100_info_type *info){
+  int err = 0;
+  unsigned char value = 0;  
+
+  PDEBUG("me8100_setup_icsr() is executed\n");
+  
+  err = copy_from_user(&value, arg, sizeof(value));
+  if(err)
+    return err;
+
+  PDEBUG("me8100_setup_icsr:icsr=0x%02X\n", value);
+  PDEBUG("me8100_setup_icsr:To offset=0x%02X\n", 
+	 PLX_ICSR);
+  outb(value, info->plx_regbase + PLX_ICSR);    
+ 
+  return 0;
+}
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_read_icsr                                                       
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by the me8100_ioctl, in order to read the      
+ *   the plx icsr (offset 0x4C)                                             
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                       Access  Description                 
+ *--------------------------------------------------------------------------
+ *   *arg    unsigned char              w       Carries the value to user.
+ *   *info   me8100_info_type           r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_read_icsr(unsigned char *arg, me8100_info_type *info){
+  int err = 0;  
+  unsigned char value;  
+
+  PDEBUG("me8100_read_icsr() is executed\n");
+  
+  value = inb(info->plx_regbase + PLX_ICSR);    
+  err = copy_to_user(arg, &value, sizeof(value));
+  if(err)
+    return err;
+
+  return 0;
+}
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_get_int_count                                                   
+ *                                                                          
+ * Description:                                                             
+ *   This function is called by me8100_ioctl, in order to get the count of  
+ *   interrupts occoured since the module was loaded.                       
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name    Type                      Access  Description                  
+ *--------------------------------------------------------------------------
+ *   *arg    int                       w       Carries the value to user.
+ *   *info   me8100_info_type          r       Global board context.
+ *                                                                          
+ * Result:                                                                  
+ *   On success the return value is 0, else is failure.
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static int me8100_get_int_count(me8100_int_occur_type *arg, 
+				me8100_info_type *info){
+  me8100_int_occur_type int_count;
+  unsigned long flags;
+
+  PDEBUG("me8100_get_int_count() is executed\n");
+
+  save_flags(flags);
+  cli();
+  int_count.int1 = info->int_count_1;
+  int_count.int2 = info->int_count_2;
+  restore_flags(flags);
+
+  if(copy_to_user(arg, &int_count, sizeof(int_count)))
+    return -EFAULT;
+  return 0;
+}
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   me8100_isr                                                             
+ *                                                                          
+ * Description:                                                             
+ *   This is the interrupt service routine of the ME8100 board. This        
+ *   function is called, when the interrupt logic of the plx and the board  
+ *   is enabled and an extarnal interrupt occures. First it checks if the   
+ *   interrupt number is right and if this board rises the interrupt by     
+ *   reading the interrupt status register of the PLX. It remarks the input 
+ *   (Int1 or Int2) which rises the interrupt in the global                 
+ *   info_vec. Then it informs the process, probably remarked in   
+ *   the fasync structure.                                                  
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name       Type              Access    Description                     
+ *--------------------------------------------------------------------------
+ *   irq        int               read      number of interrupt occured     
+ *   dev_id     void*             read      pointer to board specific       
+ *                                          informations                    
+ *   regs       struct pt_regs *  read      pointer to cpu register         
+ *                                                                          
+ * Result:                                                                  
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+static void me8100_isr(int irq, void *dev_id, struct pt_regs *regs){
+  unsigned char icsr = 0;
+  unsigned short dummy = 0;
+  me8100_info_type *board_context;
+  struct fasync_struct *fasync_ptr;
+
+  PDEBUG("me8100_isr() is executed\n");
+
+  board_context = (me8100_info_type *) dev_id;
+
+  fasync_ptr = board_context->file_ptr->private_data;
+
+  if(irq != board_context->int_line){
+    PDEBUG("me8100_isr():incorrect interrupt num: %d\n", irq);
+    return;
+  }
+
+  board_context->int1 = 0;
+  board_context->int2 = 0;
+
+  icsr = inb(board_context->plx_regbase + PLX_ICSR);
+
+  if((icsr & 0x04)&&(icsr & 0x40)&&(icsr & 0x01)){
+    PDEBUG("me8100_isr():Int1 occured\n");
+    board_context->int1 = 1;
+    board_context->int_count_1++;
+    dummy = inw(board_context->me8100_regbase + ME8100_RES_INT_REG_A); 
+  }
+
+  if((icsr & 0x20)&&(icsr & 0x40)&&(icsr & 0x08)){
+    PDEBUG("me8100_isr():Int2 occured\n");
+    board_context->int2 = 1;
+    board_context->int_count_2++;
+    dummy = inw(board_context->me8100_regbase + ME8100_RES_INT_REG_B); 
+  }
+
+  if(!(board_context->int1 || board_context->int2)){
+    PDEBUG("me8100_isr():Not this Board\n");
+    return;
+  }
+
+  if(fasync_ptr){
+    PDEBUG("me8100_isr():send signal to process\n");
+    kill_fasync(&fasync_ptr, SIGIO, POLL_IN);
+  }
+} 
+
+
+
+
+/*
+ * Routine:                                                                 
+ *   cleanup_module                                                         
+ *                                                                          
+ * Description:                                                             
+ *   This routine is called, when the module is removed from the kernel.    
+ *   It unregisters the module on the system.                               
+ *                                                                          
+ * Parameter list:                                                          
+ *   Name       Type              Access    Description                     
+ *--------------------------------------------------------------------------
+ *                                                                          
+ * Result:                                                                  
+ *--------------------------------------------------------------------------
+ * Author: GG                                                               
+ * Modification:                                                            
+ */
+void cleanup_module(void){
+  extern unsigned int major;
+  int err;
+
+  PDEBUG("cleanup_module() is executed\n");
+  
+  if(major){
+    err = unregister_chrdev(major, ME8100_NAME);
+    if(err)
+      printk(KERN_WARNING"ME8100:cleanup_module():cannot unregister major\n");
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100.h	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,219 @@
+/* me8100.h */
+/* Device driver for Meilhaus me1000 board.
+ * ========================================
+ *
+ *  Copyright (C) 2001 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *  
+ *  This file 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+      
+/*
+ * Source File : me8100.h
+ * Destination : me8100.o
+ * Author      : GG (Guenter Gebhardt)
+ *
+ * File History: Version   Date       Editor   Action
+ *---------------------------------------------------------------------
+ *               1.00.00   01.01.10   GG       first release
+ *                                                          
+ *               1.00.01   01.02.14   GG       Add new ioctls:
+ *                                              get_board_info
+ *                                              get_int count
+ *
+ *               1.01.00   01.10.08   GG       Port to Kernel 2.4
+ *---------------------------------------------------------------------
+ *
+ * Description:
+ *
+ *   Contains declarations and type definitions for the ME-8100
+ *   driver module.
+ *
+ */
+
+
+/* Please define this to enable debug mode */
+#undef ME8100_DEBUG
+
+#undef PDEBUG // only to be sure
+
+#ifdef ME8100_DEBUG
+# define PDEBUG(fmt, args...) printk(KERN_DEBUG"ME8100:" fmt, ##args)
+#else 
+# define PDEBUG(fmt, args...) // no debugging, do nothing
+#endif
+
+
+/* Meilhaus PCI vendor id */
+#define PCI_VENDOR_ID_MEILHAUS 0x1402
+
+/* ME-8100 device IDs */
+#define PCI_DEVICE_ID_MEILHAUS_ME8100_A  0x0810A  // Meilhaus ME8100 A
+#define PCI_DEVICE_ID_MEILHAUS_ME8100_B  0x0810B  // Meilhaus ME8100 B
+
+
+/* Count of different ME-8100 sorts = 2 */
+#define SORT_COUNT 2
+
+
+/* Device name, for entries in /proc/.. */
+#define ME8100_NAME "me8100"
+
+
+/* Maximum count of ME-8100 devices */
+#define ME8100_MAX_DEVICES 16
+
+
+/* Here you can enable the workaround for the plx bug */
+#define PLX_WORKAROUND_ENABLE 1
+
+
+/* Offset Interrupt Control Status Register of the PLX */
+#define PLX_ICSR 0x4C
+
+
+/* Size of register bases */
+#define ME8100_BASE_SIZE 0xFF
+#define PLX_BASE_SIZE 0x80
+
+
+/**************************************************/
+/* Bit definition for the ME8100_CTRL_REG_X       */
+/* it differs from the ISA VERSION !!!            */
+/* The other registers are similar to ISA VERSION */
+/**************************************************/
+// Bit 0-3 dont care
+// Bit 4   SRC/SINK    1 = SRC  0 = SINK
+// Bit 5   INTB 1      ISA has here INTB 0
+// Bit 6   INTB 0      ISA has here INTB 1
+// Bit 7   ENIO        1 = Enable  0 = high resistance
+// Bit 8-15 dont care
+/*********************************************************/
+/* The ME8100_ID_REG_X contains on PCI boards static 81h */
+/*********************************************************/
+
+/* ME8100 Register Set A */
+#define ME8100_ID_REG_A			0x00      //(r, )
+#define ME8100_CTRL_REG_A		0x00      //( ,w)
+#define ME8100_RES_INT_REG_A		0x02      //(r, )
+#define ME8100_DI_REG_A			0x04      //(r, )
+#define ME8100_DO_REG_A                 0x06      //( ,w)
+#define ME8100_PATTERN_REG_A		0x08      //( ,w)
+#define ME8100_MASK_REG_A		0x0A      //( ,w)
+#define ME8100_INT_DI_REG_A		0x0A      //(r, )
+									
+/* ME8100 Register Set B */		
+#define ME8100_ID_REG_B			0x0C      //(r, )
+#define ME8100_CTRL_REG_B		0x0C      //( ,w)
+#define ME8100_RES_INT_REG_B		0x0E      //(r, )
+#define ME8100_DI_REG_B			0x10      //(r, )
+#define ME8100_DO_REG_B			0x12      //( ,w)
+#define ME8100_PATTERN_REG_B	        0x14      //( ,w)
+#define ME8100_MASK_REG_B		0x16      //( ,w)
+#define ME8100_INT_DI_REG_B		0x16      //(r, )
+									
+/* ME8100 82C54 Counter Registers                          */
+/* 82C54 registers are adressed as 8-bit registers, so the */
+/* offset is in bytes.                                     */
+#define ME8100_COUNTER_REG_0		0x18      //(r,w)
+#define ME8100_COUNTER_REG_1		0x1A      //(r,w)
+#define ME8100_COUNTER_REG_2		0x1C      //(r,w)
+#define ME8100_COUNTER_CTRL_REG	        0x1E      //(r,w)
+
+/* Bitmasks for the PLX_ICSR register */
+#define LOCAL_INT1_EN             0x01  // local interrupt 1 enabled  (r,w)
+#define LOCAL_INT1_POL            0x02  // local interrupt 1 polarity (r,w)
+#define LOCAL_INT1_STATE          0x04  // local interrupt 1 state    (r, )
+#define LOCAL_INT2_EN             0x08  // local interrupt 2 enabled  (r,w)
+#define LOCAL_INT2_POL            0x10  // local interrupt 2 polarity (r,w)
+#define LOCAL_INT2_STATE          0x20  // local interrupt 2 state    (r, )
+#define PCI_INT_EN                0x40  // PCI interrupt enable       (r,w)
+#define SOFT_INT                  0x80  // Software interrupt         (r,w)
+
+
+typedef enum {  
+  ME8100_A, 
+  ME8100_B
+} me8100_version_enum_type; 
+
+
+typedef struct{
+  int int1;
+  int int2;
+} me8100_int_occur_type;
+
+
+typedef struct{
+  int board_count;                  /* index of the board after detection    */
+  me8100_version_enum_type version; /* sort of board                         */
+  unsigned int plx_regbase;         /* PLX configuration space base address  */
+  unsigned int me8100_regbase;      /* Base address of the ME8100/2000       */
+  unsigned int plx_regbase_size;    /* Size of PLX space                     */
+  unsigned int me8100_regbase_size; /* Size of ME8100 base address           */
+  unsigned int serial_no;           /* Serial number of the board            */
+  unsigned char hw_revision;        /* Hardware revision of the board        */
+  unsigned short vendor_id;         /* Meilhaus vendor id (0x1402)           */
+  unsigned short device_id;         /* Device ID                             */
+  int pci_bus_no;                   /* PCI bus number                        */
+  int pci_dev_no;                   /* PCI device number                     */
+  int pci_func_no;                  /* PCI function number                   */
+  char int_line;                    /* IRQ assigned from the PCI BIOS        */
+  int int1;                         /* Marks witch interrupt occured         */
+  int int2;                         /* Marks witch interrupt occured         */
+  int int_count_1;                  /* Count of interrupt 1                  */
+  int int_count_2;                  /* Count of interrupt 2                  */
+  int board_in_use;                 /* Indicates if board is already in use  */
+  spinlock_t use_lock;              /* Guards board in use                   */
+  struct file *file_ptr;            /* Pointer to file structure of path     */
+} me8100_info_type;
+
+
+/* ME8100 IOCTL's */
+#define ME8100_IOCTL_MAXNR 29
+#define ME8100_MAGIC 'o'
+#define ME8100_READ_ID_A        _IOR(ME8100_MAGIC, 0,  unsigned short)
+#define ME8100_WRITE_CTRL_A     _IOW(ME8100_MAGIC, 1,  unsigned short)
+#define ME8100_RES_INT_A        _IOR(ME8100_MAGIC, 2,  unsigned short)
+#define ME8100_READ_DI_A        _IOR(ME8100_MAGIC, 3,  unsigned short)
+#define ME8100_WRITE_DO_A       _IOW(ME8100_MAGIC, 4,  unsigned short)
+#define ME8100_WRITE_PATTERN_A  _IOW(ME8100_MAGIC, 5,  unsigned short)
+#define ME8100_WRITE_MASK_A     _IOW(ME8100_MAGIC, 6,  unsigned short)
+#define ME8100_READ_INT_DI_A    _IOR(ME8100_MAGIC, 7,  unsigned short)
+
+#define ME8100_READ_ID_B        _IOR(ME8100_MAGIC, 8,  unsigned short)
+#define ME8100_WRITE_CTRL_B     _IOW(ME8100_MAGIC, 9,  unsigned short)
+#define ME8100_RES_INT_B        _IOR(ME8100_MAGIC, 10, unsigned short)
+#define ME8100_READ_DI_B        _IOR(ME8100_MAGIC, 11, unsigned short)
+#define ME8100_WRITE_DO_B       _IOW(ME8100_MAGIC, 12, unsigned short)
+#define ME8100_WRITE_PATTERN_B  _IOW(ME8100_MAGIC, 13, unsigned short)
+#define ME8100_WRITE_MASK_B     _IOW(ME8100_MAGIC, 14, unsigned short)
+#define ME8100_READ_INT_DI_B    _IOR(ME8100_MAGIC, 15, unsigned short)
+
+#define ME8100_WRITE_COUNTER_0  _IOW(ME8100_MAGIC, 16, unsigned char)
+#define ME8100_WRITE_COUNTER_1  _IOW(ME8100_MAGIC, 17, unsigned char)
+#define ME8100_WRITE_COUNTER_2  _IOW(ME8100_MAGIC, 18, unsigned char)
+#define ME8100_READ_COUNTER_0   _IOR(ME8100_MAGIC, 19, unsigned char)
+#define ME8100_READ_COUNTER_1   _IOR(ME8100_MAGIC, 20, unsigned char)
+#define ME8100_READ_COUNTER_2   _IOR(ME8100_MAGIC, 21, unsigned char)
+#define ME8100_SETUP_COUNTER    _IOW(ME8100_MAGIC, 22, unsigned char)
+
+#define ME8100_GET_SERIAL       _IOR(ME8100_MAGIC, 23, unsigned int)
+#define ME8100_GET_NAME         _IOR(ME8100_MAGIC, 24,me8100_version_enum_type)
+#define ME8100_INT_OCCUR        _IOR(ME8100_MAGIC, 25, me8100_int_occur_type)
+#define ME8100_SETUP_ICSR       _IOW(ME8100_MAGIC, 26, unsigned char)
+#define ME8100_READ_ICSR        _IOR(ME8100_MAGIC, 27, unsigned char)
+#define ME8100_GET_BOARD_INFO   _IOR(ME8100_MAGIC, 28, me8100_info_type)
+#define ME8100_GET_INT_COUNT    _IOR(ME8100_MAGIC, 29, me8100_int_occur_type)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_counter/Makefile	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,15 @@
+# Makefile for the Meilhaus me8100 driver test program
+# If you are in the directory ./me8100_test_counter where the Makefile 
+# and the sources resides, you can use the make command with 
+# following parmeters:
+# $make 	  generates the executable me8100_test_counter
+# $make clean     deletes all files including *.o and *~ 
+
+me8100_test_counter:me8100_test_counter.o
+	gcc -o me8100_test_counter me8100_test_counter.o 
+
+me8100_test_counter.o:me8100_test_counter.c ../me8100.h
+	gcc -c me8100_test_counter.c -Wall -O
+
+clean:
+	rm -f *.o *~
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_counter/me8100_test_counter.c	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,199 @@
+/*
+ * Source File : me8100_test_counter.c
+ * Destination : me8100_test_counter
+ * Author      : GG (Guenter Gebhardt)                                 
+ *    
+ *                                                                     
+ * File History: Version   Date       Editor   Action                  
+ *---------------------------------------------------------------------
+ *               1.00.00   01.07.12   GG       first release           
+ *                                                                     
+ *---------------------------------------------------------------------
+ *                                                                     
+ * Description:
+ *   This program shows the use of the driver and the counter part 
+ *   of the me8100 board. It configures the counters in mode 3 (symetric 
+ *   devisor) with a counter value of 4. So each frequency of a signal 
+ *   put in at CLK is devided by 4.
+ */
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <linux/spinlock.h>
+
+#include "../me8100.h"
+
+
+int main(void){
+  int err = 0;
+  int minor = 0;
+  int count = 0;
+  static int file_handle = -1;
+
+  unsigned char cctrl_0;
+  unsigned char cctrl_1;
+  unsigned char cctrl_2;
+  unsigned char clvalue_0;
+  unsigned char chvalue_0;
+  unsigned char clvalue_1;
+  unsigned char chvalue_1;
+  unsigned char clvalue_2;
+  unsigned char chvalue_2;
+
+  printf("%c%3s", 27, "[2J");
+  printf("<<<--- ME8100 TESTPROGRAM FOR COUNTER --->>>\n\n");
+
+  /*
+   * You can select up to four me8100 baords, if installed.
+   * 0 is the first board.
+   */
+  printf("Please type in the Minor Device Number of Board to open : ");
+  count = scanf("%d", &minor);
+  if(!count){
+    printf("Invalid Input !\n");
+    return 1;
+  }
+  printf("Open path /dev/me8100_%d !\n\n", minor);
+  
+  switch(minor){
+  case 0:
+    file_handle = open("/dev/me8100_0", O_RDWR, 0);
+    break;
+  case 1:
+    file_handle = open("/dev/me8100_1", O_RDWR, 0);
+    break;
+  case 2:
+    file_handle = open("/dev/me8100_2", O_RDWR, 0);
+    break;
+  case 3:
+    file_handle = open("/dev/me8100_3", O_RDWR, 0);
+    break;
+  default:
+    printf("Invalid Input !\n");
+    return 1;
+  }
+
+  if(file_handle < 0){
+    printf("Cannot open path !\n");
+    return 1;
+  }
+
+  /*
+   * Counter 
+   *
+   * Now that you have access to the me8100 you have to configurate
+   * the board according to your needs. 
+   * The ME8100 has got 3 counters with 16 bit each. First you have
+   * to configure the counters control register according to your needs.
+   * Then you can write the proper value to the counter.
+   */
+
+
+  /*--------------- ALL COUNTER IN MODE 3 AND BINARY ------------------------*/
+
+  printf("Please press return to test counter in mode 3/binary :\n");
+  getchar();
+  getchar();
+  
+  /* 
+   * Configure COUNTER_0 as asynchronous devisor,
+   * as binary counter and LSB/MSB.
+   */
+  cctrl_0 = 0x36; 
+  err = ioctl(file_handle , ME8100_SETUP_COUNTER, &cctrl_0);
+  if(err){
+    printf("Cannot setup counter 0\n");
+    return 1;
+  }
+
+  /* 
+   * Configure COUNTER_1 as asynchronous devisor,
+   * as binary counter and LSB/MSB.
+   */
+  cctrl_1 = 0x76;
+  err = ioctl(file_handle , ME8100_SETUP_COUNTER, &cctrl_1);
+  if(err){
+    printf("Cannot setup counter 1\n");
+    return 1;
+  }
+
+  /* 
+   * Configure COUNTER_2 as asynchronous devisor,
+   * as binary counter and LSB/MSB.
+   */
+  cctrl_2 = 0xB6;
+  err = ioctl(file_handle , ME8100_SETUP_COUNTER, &cctrl_2);
+  if(err){
+    printf("Cannot setup counter 2\n");
+    return 1;
+  }
+
+
+  /*---------------------------- LOAD COUNTER -------------------------------*/
+  
+  /* Counter 0 */
+  clvalue_0 = 0x4;
+  chvalue_0 = 0x0;
+  /* Write lower byte */
+  err = ioctl(file_handle , ME8100_WRITE_COUNTER_0, &clvalue_0);
+  if(err){
+    printf("Cannot write to counter 0\n");
+    return 1;
+  }
+  /* Write higher byte */
+  err = ioctl(file_handle , ME8100_WRITE_COUNTER_0, &chvalue_0);
+  if(err){
+    printf("Cannot write to counter 0\n");
+    return 1;
+  }
+
+  /* Counter 1 */
+  clvalue_1 = 0x4;
+  chvalue_1 = 0x0;
+  /* Write lower byte */
+  err = ioctl(file_handle , ME8100_WRITE_COUNTER_1, &clvalue_1);
+  if(err){
+    printf("Cannot write to counter 1\n");
+    return 1;
+  }
+  /* Write higher byte */
+  err = ioctl(file_handle , ME8100_WRITE_COUNTER_1, &chvalue_1);
+  if(err){
+    printf("Cannot write to counter 1\n");
+    return 1;
+  }
+
+  /* Counter 2 */
+  clvalue_2 = 0x4;
+  chvalue_2 = 0x0;
+  /* Write lower byte */
+  err = ioctl(file_handle , ME8100_WRITE_COUNTER_2, &clvalue_2);
+  if(err){
+    printf("Cannot write to counter 2\n");
+    return 1;
+  }
+  /* Write higher byte */
+  err = ioctl(file_handle , ME8100_WRITE_COUNTER_2, &chvalue_2);
+  if(err){
+    printf("Cannot write to counter 2\n");
+    return 1;
+  }
+
+
+
+  /*-------------------------------- END ------------------------------------*/
+
+  printf("Please press return to terminate program :\n");
+  getchar();
+
+  printf("Close path to me8100_%d\n", minor);
+  err = close(file_handle);
+  if(err){
+    printf("Kann Pfad nicht schliessen\n");
+    return 1;
+  }
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_dio/Makefile	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,15 @@
+# Makefile for the Meilhaus me8100 driver test program
+# If you are in the directory ./me8100_test_dio where the Makefile 
+# and the sources resides, you can use the make command with 
+# following parmeters:
+# $make 	  generates the executable me8100_test_dio
+# $make clean     deletes all files including *.o and *~ 
+
+me8100_test_dio:me8100_test_dio.o
+	gcc -o me8100_test_dio me8100_test_dio.o 
+
+me8100_test_dio.o:me8100_test_dio.c ../me8100.h
+	gcc -c me8100_test_dio.c -Wall -O
+
+clean:
+	rm -f *.o *~
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_dio/me8100_test_dio.c	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,219 @@
+/*
+ * Source File : me8100_test_dio.c
+ * Destination : me8100_test_dio
+ * Author      : GG (Guenter Gebhardt)                                 
+ *    
+ *                                                                     
+ * File History: Version   Date       Editor   Action                  
+ *---------------------------------------------------------------------
+ *               1.00.00   01.07.12   GG       first release           
+ *                                                                     
+ *---------------------------------------------------------------------
+ *                                                                     
+ * Description:
+ *   This program shows the use of the driver and the digital inputs
+ *   and outputs. First the outputs are tested in sink and source mode. 
+ *   Then the inputs are tested.
+ */
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <linux/spinlock.h>
+
+#include "../me8100.h"
+
+int main(void){
+  int err = 0;
+  int minor = 0;
+  int count = 0;
+  int i;
+  static int file_handle = -1;
+
+  unsigned short ctrl_a;
+  unsigned short ctrl_b;
+  unsigned short value_a;
+  unsigned short value_b;
+
+  printf("%c%3s", 27, "[2J");
+  printf("<<<--- ME8100 TESTPROGRAM FOR DIO --->>>\n\n");
+
+  /*
+   * You can select up to four me8100 baords, if installed.
+   * 0 is the first board.
+   */
+  printf("Please type in the Minor Device Number of Board to open : ");
+  count = scanf("%d", &minor);
+  if(!count){
+    printf("Invalid Input !\n");
+    return 1;
+  }
+  printf("Open path /dev/me8100_%d !\n\n", minor);
+  
+  switch(minor){
+  case 0:
+    file_handle = open("/dev/me8100_0", O_RDWR, 0);
+    break;
+  case 1:
+    file_handle = open("/dev/me8100_1", O_RDWR, 0);
+    break;
+  case 2:
+    file_handle = open("/dev/me8100_2", O_RDWR, 0);
+    break;
+  case 3:
+    file_handle = open("/dev/me8100_3", O_RDWR, 0);
+    break;
+  default:
+    printf("Invalid Input !\n");
+    return 1;
+  }
+
+  if(file_handle < 0){
+    printf("Cannot open path !\n");
+    return 1;
+  }
+
+  /*
+   * DIGITAL I/O 
+   *
+   * Now that you have access to the me8100 have to configurate
+   * the board according to your needs. 
+   * The ME8100_B has got two output ports with 16 bit each. You can
+   * decide wether the port is driven as sink or as source.
+   * Additionally, you have to enable the outputs explicitly.
+   */
+
+
+  /*------------------ BOTH OUTPUT PORTS AS SOURCE --------------------------*/
+
+  printf("\nPlease press return to test both output ports as source :\n");
+  getchar();
+  getchar();
+  
+  /* Setup for the first output port */
+  ctrl_a = 0x90; /* We want to configure as source and to enable the outputs */
+  err = ioctl(file_handle , ME8100_WRITE_CTRL_A, &ctrl_a);
+  if(err){
+    printf("Cannot setup output port A \n");
+    return 1;
+  }
+
+  /* Setup for the second output port */
+  ctrl_b = 0x90; /* We want to configure as source and to enable the outputs */
+  err = ioctl(file_handle , ME8100_WRITE_CTRL_B, &ctrl_b);
+  if(err){
+    printf("Cannot setup output port B \n");
+    return 1;
+  }
+
+  for(i = 0; i < 16; i++){
+    /* Write to port A */
+    value_a = 0x1 << i;
+    err = ioctl(file_handle, ME8100_WRITE_DO_A, &value_a); /* Do the job */
+    if(err){
+      printf("Cannot write to output port A \n");
+      return 1;
+    }
+    printf("Write to Port A : 0x%04X\n", value_a);
+    
+    /* Write to port B */
+    value_b = 0x8000 >> i;
+    err = ioctl(file_handle, ME8100_WRITE_DO_B, &value_b); /* Do the job */
+    if(err){
+      printf("Cannot write to port B \n");
+      return 1;
+    }
+    printf("Write to Port B : 0x%04X\n\n", value_b);
+    sleep(1);
+  }
+
+
+  /*-------------------- BOTH OUTPUT PORTS AS SINK --------------------------*/
+
+  printf("Please press return to test both output ports as sink :\n");
+  getchar();
+  
+  /* Setup for the first output port */
+  ctrl_a = 0x80; /* We want to configure as sink and to enable the outputs */
+  err = ioctl(file_handle , ME8100_WRITE_CTRL_A, &ctrl_a);
+  if(err){
+    printf("Cannot setup output port A \n");
+    return 1;
+  }
+
+  /* Setup for the second output port */
+  ctrl_b = 0x80; /* We want to configure as sink and to enable the outputs */
+  err = ioctl(file_handle , ME8100_WRITE_CTRL_B, &ctrl_b);
+  if(err){
+    printf("Cannot setup output port B \n");
+    return 1;
+  }
+
+  for(i = 0; i < 16; i++){
+    /* Write to port A */
+    value_a = 0x1 << i;
+    err = ioctl(file_handle, ME8100_WRITE_DO_A, &value_a); /* Do the job */
+    if(err){
+      printf("Cannot write to output port A \n");
+      return 1;
+    }
+    printf("Write to Port A : 0x%04X\n", value_a);
+    
+    /* Write to port B */
+    value_b = 0x8000 >> i;
+    err = ioctl(file_handle, ME8100_WRITE_DO_B, &value_b); /* Do the job */
+    if(err){
+      printf("Cannot write to port B \n");
+      return 1;
+    }
+    printf("Write to Port B : 0x%04X\n\n", value_b);
+    sleep(1);
+  }
+
+
+  /*--------------------------- READ FROM BOTH INPUT PORTS -----------------------*/
+
+  printf("Please press return to read from both input ports :\n");
+  getchar();
+
+  for(i = 0; i < 20; i++){
+    /* Read from port A */
+    err = ioctl(file_handle, ME8100_READ_DI_A, &value_a);  /* Do the job */
+    if(err){
+      printf("Cannot read port A \n");
+      return 1;
+    }
+    /* 
+     * The result is put into value_a. 
+     * We simply print the result as a hex number. 
+     */
+    printf("Read from Port A: 0x%04X\n", value_a);
+    
+    
+    /* Read from port B */
+    err = ioctl(file_handle, ME8100_READ_DI_B, &value_b);  /* Do the job */
+    if(err){
+      printf("Cannot read port B \n");
+      return 1;
+    }
+    /* 
+     * The result is put into value_b. 
+     * We simply print the result as a hex number. 
+     */
+    printf("Read from Port B: 0x%04X\n\n", value_b);
+    sleep(1);
+  }
+
+
+  /*-------------------------------- END ------------------------------------*/
+
+  printf("Close path to me8100_%d\n", minor);
+  err = close(file_handle);
+  if(err){
+    printf("Kann Pfad nicht schliessen\n");
+    return 1;
+  }
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_int/Makefile	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,15 @@
+# Makefile for the Meilhaus me8100 driver test program
+# If you are in the directory ./me8100_test_int where the Makefile 
+# and the sources resides, you can use the make command with 
+# following parmeters:
+# $make 	  generates the executable me8100_test_int
+# $make clean     deletes all files including *.o and *~ 
+
+me8100_test_int:me8100_test_int.o
+	gcc -o me8100_test_int me8100_test_int.o 
+
+me8100_test_int.o:me8100_test_int.c ../me8100.h
+	gcc -c me8100_test_int.c -Wall -O
+
+clean:
+	rm -f *.o *~
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/me8100_test_int/me8100_test_int.c	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,232 @@
+/*
+ * Source File : me8100_test_int.c                                            
+ * Destination : me8100_test_int.out                                              
+ * Author      : GG (Guenter Gebhardt)                                 
+ *    
+ *                                                                     
+ * File History: Version   Date       Editor   Action                  
+ *---------------------------------------------------------------------
+ *               1.00.00   01.07.12   GG       first release           
+ *                                                                     
+ *---------------------------------------------------------------------
+ *                                                                     
+ * Description:
+ *   This program shows the usage of the driver and the interrupt
+ *   facility of the me8100. First the board is configured, in order to
+ *   generate an interrupt when a bit pattern of 0x0001 on port a and a bit
+ *   pattern of 0x0100 an port b is pending. Then the board is configured, 
+ *   in order to generate an interrupt with a bit mask of 0x0001 on port a 
+ *   and a bit mask of 0x0100 on port b. 
+ *   We install a signal handler, which is informed by the interrupt routine 
+ *   by signalling of the driver, when a interrupt occures.
+ */
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <linux/spinlock.h>
+
+#include "../me8100.h"
+
+/* Prototypes */
+static void signal_handler(int);
+
+/* Counts the interrupts */
+static me8100_int_occur_type intcounts;
+
+/* Count of signal handler execution */
+static int i = 0;
+
+/* Path to the ME8100 board */
+static int file_handle = -1;
+
+int main(void){
+  int err = 0;
+  int minor = 0;
+  int oflags = 0;
+
+  unsigned short pattern_a;
+  unsigned short pattern_b;
+  unsigned short mask_a;
+  unsigned short mask_b;
+  unsigned short ctrl_a;
+  unsigned short ctrl_b;
+
+  unsigned char icsr;
+
+  printf("%c%3s", 27, "[2J");
+  printf("<<<--- ME8100 TESTPROGRAM FOR INT --->>>\n\n");
+
+  /*
+   * You can select up to four me8100 baords, if installed.
+   * 0 is the first board.
+   */
+  printf("Please type in the minor device number of the board to open : ");
+  scanf("%d", &minor);
+  printf("Open path /dev/me8100_%d !\n\n", minor);
+  
+  switch(minor){
+  case 0:
+    file_handle = open("/dev/me8100_0", O_RDWR, 0);
+    break;
+  case 1:
+    file_handle = open("/dev/me8100_1", O_RDWR, 0);
+    break;
+  case 2:
+    file_handle = open("/dev/me8100_2", O_RDWR, 0);
+    break;
+  case 3:
+    file_handle = open("/dev/me8100_3", O_RDWR, 0);
+    break;
+  default:
+    printf("Invalid input !\n");
+    return 1;
+  }
+
+  if(file_handle < 0){
+    printf("Cannot open path !\n");
+    return 1;
+  }
+
+  /*---------------------- general setup ------------------------------------*/
+
+  /* install the signal handler */
+  signal(SIGIO, signal_handler);
+
+  /* set current process as owner of the path */
+  fcntl(file_handle, F_SETOWN, getpid());
+
+  /* read the flags of the path */
+  oflags = fcntl(file_handle, F_GETFL);
+
+  /* Inform the driver to put the current process on the fasync queue */
+  fcntl(file_handle, F_SETFL, oflags | FASYNC); 
+
+  /* enable both interrupts on the plx, set interrupts to high active */
+  icsr =
+    LOCAL_INT1_EN |
+    LOCAL_INT1_POL |
+    LOCAL_INT2_EN |
+    LOCAL_INT2_POL |
+    PCI_INT_EN;
+
+  err = ioctl(file_handle, ME8100_SETUP_ICSR, &icsr);
+  if(err){
+    printf("Cannot setup PLX\n");
+    return 1;
+  }
+
+
+
+  /*-------------------- Interrupt caused by bit pattern -----------------*/
+
+  /* Set the proper bit pattern for port a */
+  pattern_a = 0x1;
+  err = ioctl(file_handle, ME8100_WRITE_PATTERN_A, &pattern_a);
+  if(err){
+    printf("Cannot write pattern a\n");
+    return 1;
+  }
+
+  /* Set the proper bit pattern for port b */
+  pattern_b = 0x100;
+  err = ioctl(file_handle, ME8100_WRITE_PATTERN_B, &pattern_b);
+  if(err){
+    printf("Cannot write pattern b\n");
+    return 1;
+  }
+
+  /* Enable interrupt signalling by bit pattern for port a */
+  ctrl_a = 0x40;
+  err = ioctl(file_handle, ME8100_WRITE_CTRL_A, &ctrl_a);
+  if(err){
+    printf("Cannot write ctrl a\n");
+    return 1;
+  }
+
+  /* Enable interrupt signalling by bit pattern for port b */
+  ctrl_b = 0x40;
+  err = ioctl(file_handle, ME8100_WRITE_CTRL_B, &ctrl_b);
+  if(err){
+    printf("Cannot write ctrl b\n");
+    return 1;
+  }
+
+  printf("<<<--- WAITING FOR INTERRUPTS BY BIT PATTERN --->>>\n\n");
+
+  i = 0;
+  /* execute until 0x8 interrupt will be occured */
+  while(i < 0x4){
+  }
+
+
+  /*-------------------- Interrupt caused by bit mask -----------------*/
+
+  /* Set the proper bit mask for port a */
+  mask_a = 0x1;
+  err = ioctl(file_handle, ME8100_WRITE_MASK_A, &mask_a);
+  if(err){
+    printf("Cannot write mask a\n");
+    return 1;
+  }
+
+  /* Set the proper bit mask for port b */
+  mask_b = 0x100;
+  err = ioctl(file_handle, ME8100_WRITE_MASK_B, &mask_b);
+  if(err){
+    printf("Cannot write mask b\n");
+    return 1;
+  }
+
+  /* Enable interrupt signalling by bit mask for port a */
+  ctrl_a = 0x60;
+  err = ioctl(file_handle, ME8100_WRITE_CTRL_A, &ctrl_a);
+  if(err){
+    printf("Cannot write ctrl a\n");
+    return 1;
+  }
+
+  /* Enable interrupt signalling by bit mask for port b */
+  ctrl_b = 0x60;
+  err = ioctl(file_handle, ME8100_WRITE_CTRL_B, &ctrl_b);
+  if(err){
+    printf("Cannot write ctrl b\n");
+    return 1;
+  }
+
+  printf("<<<--- WAITING FOR INTERRUPTS BY BIT MASK --->>>\n\n");
+
+  i = 0;
+  /* execute until 0x8 interrupt will be occured */
+  while(i < 0x4){
+  }
+
+
+  /*-------------------------------- END ------------------------------------*/
+
+  printf("Close path to me8100_%d\n", minor);
+  err = close(file_handle);
+  if(err){
+    printf("Kann Pfad nicht schliessen\n");
+    return 1;
+  }
+
+  return 1;
+}
+
+
+
+void signal_handler(int sig){
+  int err = 0;
+  i++;
+  err = ioctl(file_handle, ME8100_GET_INT_COUNT, &intcounts);
+  if(err)
+    return;
+
+  printf("<<<--- ME8100 SIGNAL HANDLER CALLED --->>>\n"
+	 "Execution   = %04d\n"
+	 "int_count_1 = %04d\n"
+	 "int_count_2 = %04d\n\n", i, intcounts.int1, intcounts.int2);
+  return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pci-compat.h	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,190 @@
+
+/* This header only makes sense when included in a 2.0 compile */
+
+/*
+ * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
+ * Copyright (C) 2001 O'Reilly & Associates
+ *
+ * The source code in this file can be freely used, adapted,
+ * and redistributed in source or binary form, so long as an
+ * acknowledgment appears in derived source files.  The citation
+ * should list that the code comes from the book "Linux Device
+ * Drivers" by Alessandro Rubini and Jonathan Corbet, published
+ * by O'Reilly & Associates.   No warranty is attached;
+ * we cannot take responsibility for errors or fitness for use.
+ */
+
+#ifndef _PCI_COMPAT_H_
+#define _PCI_COMPAT_H_
+
+#ifdef __KERNEL__
+
+/*
+ * This only makes sense if <linux/pci.h> is already included, *and*
+ * we are using 2.0.
+*/
+#if defined(LINUX_PCI_H) && (LINUX_VERSION_CODE & 0xffff00) == 0x020000
+
+#include <linux/bios32.h> /* pcibios_* */
+#include <linux/malloc.h> /* kmalloc */
+
+/* fake the new pci interface based on the old one: encapsulate bus/devfn */
+struct pci_fake_dev {
+  int index;
+  unsigned short vendor, device;
+  void *driver_data; /* net i.f. drivers make it point to net_device */
+  u8 bus;
+  u8 devfn;
+};
+#define pci_dev pci_fake_dev /* the other pci_dev is unused by 2.0 drivers */
+
+
+#ifndef PCI_HEADER_TYPE_NORMAL /* These definitions are missing from 2.0 */
+#  define PCI_HEADER_TYPE_NORMAL 0
+#  define PCI_HEADER_TYPE_BRIDGE 1
+#  define   PCI_PRIMARY_BUS         0x18    /* Primary bus number */
+#  define   PCI_SECONDARY_BUS       0x19    /* Secondary bus number */
+#  define   PCI_SUBORDINATE_BUS     0x1a    /* Highest bus behind the bridge */
+#  define PCI_HEADER_TYPE_CARDBUS 2
+#  define   PCI_CB_PRIMARY_BUS      0x18    /* PCI bus number */
+#  define   PCI_CB_CARD_BUS         0x19    /* CardBus bus number */
+#  define   PCI_CB_SUBORDINATE_BUS  0x1a    /* Subordinate bus number */
+#endif
+
+extern inline struct pci_dev *pci_find_device(unsigned int vendorid,
+					      unsigned int devid,
+					      struct pci_dev *from)
+{
+    struct pci_dev *pptr = kmalloc(sizeof(*pptr), GFP_KERNEL);
+    int index = 0;
+    int ret;
+
+    if (!pptr) return NULL;
+    if (from) index = from->index + 1;
+    pptr->index = index;
+    ret = pcibios_find_device(vendorid, devid, index,
+			      &pptr->bus, &pptr->devfn);
+    if (ret) { kfree(pptr); return NULL; }
+    /* fill other fields */
+    pcibios_read_config_word(pptr->bus, pptr->devfn,
+			     PCI_VENDOR_ID, &pptr->vendor);
+    pcibios_read_config_word(pptr->bus, pptr->devfn,
+			     PCI_DEVICE_ID, &pptr->device);
+    return pptr;
+}
+
+#if 0
+/* this used to be only the base class, Hmm... better not offer it*/
+extern inline struct pci_dev *pci_find_class(unsigned int class,
+					     struct pci_dev *from)
+{
+    struct pci_dev *pptr = kmalloc(sizeof(*pptr), GFP_KERNEL);
+    int index = 0;
+    int ret;
+
+    if (!pptr) return NULL;
+    if (from) index = from->index + 1;
+    pptr->index = index;
+    ret = pcibios_find_class(class, index,
+			      &pptr->bus, &pptr->devfn);
+    if (ret) { kfree(pptr); return NULL; }
+    /* fill other fields */
+    pcibios_read_config_word(pptr->bus, pptr->devfn,
+			     PCI_VENDOR_ID, &pptr->vendor);
+    pcibios_read_config_word(pptr->bus, pptr->devfn,
+			     PCI_DEVICE_ID, &pptr->device);
+    return pptr;
+}
+#endif
+
+/* this is used by pciregions instead */
+extern inline struct pci_dev *pci_find_slot (unsigned int bus,
+					     unsigned int devfn)
+{
+    struct pci_dev *pptr = kmalloc(sizeof(*pptr), GFP_KERNEL);
+    int index = 0;
+    unsigned short vendor;
+    int ret;
+
+    if (!pptr) return NULL;
+    pptr->index = index; /* 0 */
+    ret = pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor);
+    if (ret /* == PCIBIOS_DEVICE_NOT_FOUND or whatever error */
+	|| vendor==0xffff || vendor==0x0000) {
+        kfree(pptr); return NULL;
+    }
+    printk("ok (%i, %i %x)\n", bus, devfn, vendor);
+    /* fill other fields */
+    pptr->bus = bus;
+    pptr->devfn = devfn;
+    pcibios_read_config_word(pptr->bus, pptr->devfn,
+			     PCI_VENDOR_ID, &pptr->vendor);
+    pcibios_read_config_word(pptr->bus, pptr->devfn,
+			     PCI_DEVICE_ID, &pptr->device);
+    return pptr;
+}
+
+
+
+/* this is not used in the real (2.2, 2.4) implementation, but we need it */
+extern inline void pci_release_device(struct pci_dev *dev)
+{
+    kfree(dev);
+}
+
+/* struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn); */
+
+#define pci_present pcibios_present
+
+extern inline int
+pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val)
+{
+    return pcibios_read_config_byte(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_read_config_word(struct pci_dev *dev, u8 where, u16 *val)
+{
+    return pcibios_read_config_word(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_read_config_dword(struct pci_dev *dev, u8 where, u32 *val)
+{
+    return pcibios_read_config_dword(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_write_config_byte(struct pci_dev *dev, u8 where, u8 val)
+{
+    return pcibios_write_config_byte(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_write_config_word(struct pci_dev *dev, u8 where, u16 val)
+{
+    return pcibios_write_config_word(dev->bus, dev->devfn, where, val);
+}
+
+extern inline int
+pci_write_config_dword(struct pci_dev *dev, u8 where, u32 val)
+{
+    return pcibios_write_config_dword(dev->bus, dev->devfn, where, val);
+}
+
+extern inline void pci_set_master(struct pci_dev *dev)
+{
+    u16 cmd;
+    pcibios_read_config_word(dev->bus, dev->devfn, PCI_COMMAND, &cmd);
+    cmd |= PCI_COMMAND_MASTER;
+    pcibios_write_config_word(dev->bus, dev->devfn, PCI_COMMAND, cmd);
+}
+
+#endif /* version 2.0 and pci.h included */
+#endif /* __KERNEL__ */
+#endif /* _PCI_COMPAT_H_ */
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sysdep.h	Wed Jan 16 14:01:55 2002 +0100
@@ -0,0 +1,820 @@
+/*
+ * sysdep.h -- centralizing compatibility issues between 2.0, 2.2, 2.4
+ *
+ * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
+ * Copyright (C) 2001 O'Reilly & Associates
+ *
+ * The source code in this file can be freely used, adapted,
+ * and redistributed in source or binary form, so long as an
+ * acknowledgment appears in derived source files.  The citation
+ * should list that the code comes from the book "Linux Device
+ * Drivers" by Alessandro Rubini and Jonathan Corbet, published
+ * by O'Reilly & Associates.   No warranty is attached;
+ * we cannot take responsibility for errors or fitness for use.
+ *
+ * $Id$
+ */
+
+
+#ifndef _SYSDEP_H_
+#define _SYSDEP_H_
+
+#ifndef LINUX_VERSION_CODE
+#  include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION /* pre-2.1.90 didn't have it */
+#  define KERNEL_VERSION(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
+#endif
+
+/* only allow 2.0.x  2.2.y and 2.4.z */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0) /* not < 2.0 */
+#  error "This kernel is too old: not supported by this file"
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) /* not > 2.4, by now */
+#  error "This kernel is too recent: not supported by this file"
+#endif
+#if (LINUX_VERSION_CODE & 0xff00) == 1 /* not 2.1 */
+#  error "Please don't use linux-2.1, use 2.2 or 2.4 instead"
+#endif
+#if (LINUX_VERSION_CODE & 0xff00) == 3 /* not 2.3 */
+#  error "Please don't use linux-2.3, use 2.4 instead"
+#endif
+
+/* remember about the current version */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+#  define LINUX_20
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#  define LINUX_22
+#else
+#  define LINUX_24
+#endif
+
+/* we can't support versioning in pre-2.4 because we #define some functions */
+#if !defined(LINUX_24) && defined(CONFIG_MODVERSIONS)
+#  error "This sysdep.h can't support CONFIG_MODVERSIONS"
+#  error "and old kernels at the same time."
+#  error "Either use 2.4 or avoid using versioning"
+#endif
+
+#ifndef LINUX_20 /* include vmalloc.h if this is 2.2/2.4 */
+#  ifdef VM_READ /* a typical flag defined by mm.h */
+#    include <linux/vmalloc.h>
+#  endif
+#endif
+
+#include <linux/sched.h>
+
+/* Modularization issues */
+#ifdef LINUX_20
+#  define __USE_OLD_SYMTAB__
+#  define EXPORT_NO_SYMBOLS register_symtab(NULL);
+#  define REGISTER_SYMTAB(tab) register_symtab(tab)
+#else
+#  define REGISTER_SYMTAB(tab) /* nothing */
+#endif
+
+#ifdef __USE_OLD_SYMTAB__
+#  define __MODULE_STRING(s)         /* nothing */
+#  define MODULE_PARM(v,t)           /* nothing */
+#  define MODULE_PARM_DESC(v,t)      /* nothing */
+#  define MODULE_AUTHOR(n)           /* nothing */
+#  define MODULE_DESCRIPTION(d)      /* nothing */
+#  define MODULE_SUPPORTED_DEVICE(n) /* nothing */
+#endif
+
+/*
+ * In version 2.2 (up to 2.2.19, at least), the macro for request_module()
+ * when no kmod is there is wrong. It's a "do {} while 0" but it shouldbe int
+ */
+#ifdef LINUX_22
+#  ifndef CONFIG_KMOD
+#    undef request_module
+#    define request_module(name) -ENOSYS
+#  endif
+#endif
+
+
+#ifndef LINUX_20
+#  include <linux/init.h>     /* module_init/module_exit */
+#endif
+
+#ifndef module_init
+#  define module_init(x)        int init_module(void) { return x(); }
+#  define module_exit(x)        void cleanup_module(void) { x(); }
+#endif
+
+#ifndef SET_MODULE_OWNER
+#  define SET_MODULE_OWNER(structure) /* nothing */
+#endif
+
+/*
+ * "select" changed in 2.1.23. The implementation is twin, but this
+ * header is new
+ *
+ */
+#ifdef LINUX_20
+#  define __USE_OLD_SELECT__
+#else
+#  include <linux/poll.h>
+#endif
+
+#ifdef LINUX_20
+#  define INODE_FROM_F(filp) ((filp)->f_inode)
+#else
+#  define INODE_FROM_F(filp) ((filp)->f_dentry->d_inode)
+#endif
+
+/* Other changes in the fops are solved using wrappers */
+
+/*
+ * Wait queues changed with 2.3
+ */
+#ifndef DECLARE_WAIT_QUEUE_HEAD
+#  define DECLARE_WAIT_QUEUE_HEAD(head) struct wait_queue *head = NULL
+   typedef  struct wait_queue *wait_queue_head_t;
+#  define init_waitqueue_head(head) (*(head)) = NULL
+
+/* offer wake_up_sync as an alias for wake_up */
+#  define wake_up_sync(head) wake_up(head)
+#  define wake_up_interruptible_sync(head) wake_up_interruptible(head)
+
+/* Pretend we have add_wait_queue_exclusive */
+#  define add_wait_queue_exclusive(q,entry) add_wait_queue ((q), (entry))
+
+#endif /* no DECLARE_WAIT_QUEUE_HEAD */
+
+/*
+ * Define wait_event for 2.0 kernels.  (This ripped off directly from
+ * the 2.2.18 sched.h)
+ */
+#ifdef LINUX_20
+
+#define __wait_event(wq, condition) 					\
+do {									\
+	struct wait_queue __wait;					\
+									\
+	__wait.task = current;						\
+	add_wait_queue(&wq, &__wait);					\
+	for (;;) {							\
+		current->state = TASK_UNINTERRUPTIBLE;			\
+		mb();							\
+		if (condition)						\
+			break;						\
+		schedule();						\
+	}								\
+	current->state = TASK_RUNNING;					\
+	remove_wait_queue(&wq, &__wait);				\
+} while (0)
+
+#define wait_event(wq, condition) 					\
+do {									\
+	if (condition)	 						\
+		break;							\
+	__wait_event(wq, condition);					\
+} while (0)
+
+#define __wait_event_interruptible(wq, condition, ret)			\
+do {									\
+	struct wait_queue __wait;					\
+									\
+	__wait.task = current;						\
+	add_wait_queue(&wq, &__wait);					\
+	for (;;) {							\
+		current->state = TASK_INTERRUPTIBLE;			\
+		mb();							\
+		if (condition)						\
+			break;						\
+		if (!signal_pending(current)) {				\
+			schedule();					\
+			continue;					\
+		}							\
+		ret = -ERESTARTSYS;					\
+		break;							\
+	}								\
+	current->state = TASK_RUNNING;					\
+	remove_wait_queue(&wq, &__wait);				\
+} while (0)
+	
+#define wait_event_interruptible(wq, condition)				\
+({									\
+	int __ret = 0;							\
+	if (!(condition))						\
+		__wait_event_interruptible(wq, condition, __ret);	\
+	__ret;								\
+})
+#endif
+
+
+/*
+ * 2.3 added tasklets
+ */
+#ifdef LINUX_24
+#  define HAVE_TASKLETS
+#endif
+
+
+
+
+/* FIXME: implement the other versions of wake_up etc */
+
+
+/*
+ * access to user space: use the 2.2 functions,
+ * and implement them as macros for 2.0
+ */
+
+#ifdef LINUX_20
+#  include <asm/segment.h>
+#  define access_ok(t,a,sz)           (verify_area((t),(void *) (a),(sz)) ? 0 : 1)
+#  define verify_area_20              verify_area
+#  define   copy_to_user(t,f,n)         (memcpy_tofs((t), (f), (n)), 0)
+#  define copy_from_user(t,f,n)       (memcpy_fromfs((t), (f), (n)), 0)
+#  define   __copy_to_user(t,f,n)       copy_to_user((t), (f), (n))
+#  define __copy_from_user(t,f,n)     copy_from_user((t), (f), (n))
+
+#  define PUT_USER(val,add)           (put_user((val),(add)), 0)
+#  define __PUT_USER(val,add)         PUT_USER((val),(add))
+
+#  define GET_USER(dest,add)          ((dest)=get_user((add)), 0)
+#  define __GET_USER(dest,add)        GET_USER((dest),(add))
+#else
+#  include <asm/uaccess.h>
+#  include <asm/io.h>
+#  define verify_area_20(t,a,sz) (0) /* == success */
+#  define   PUT_USER   put_user
+#  define __PUT_USER __put_user
+#  define   GET_USER   get_user
+#  define __GET_USER __get_user
+#endif
+
+/*
+ * Allocation issues
+ */
+#ifdef GFP_USER /* only if mm.h has been included */
+#  ifdef LINUX_20
+#    define __GFP_DMA GFP_DMA /* 2.0 didn't have the leading __ */
+#  endif
+#  ifndef LINUX_24
+#    define __GFP_HIGHMEM  0  /* was not there */
+#    define GFP_HIGHUSER   0   /* idem */
+#  endif
+
+#  ifdef LINUX_20
+#    define __get_free_pages(a,b) __get_free_pages((a),(b),0)
+#  endif
+#  ifndef LINUX_24
+#    define get_zeroed_page get_free_page
+#  endif
+#endif
+
+/* ioremap */
+#if defined(LINUX_20) && defined(_LINUX_MM_H)
+#  define ioremap_nocache ioremap
+#  ifndef __i386__
+   /* This simple approach works for non-PC platforms. */
+#    define ioremap vremap
+#    define iounmap vfree
+#  else /* the PC has <expletive> ISA; 2.2 and 2.4 remap it, 2.0 needs not */
+extern inline void *ioremap(unsigned long phys_addr, unsigned long size)
+{
+    if (phys_addr >= 0xA0000 && phys_addr + size <= 0x100000)
+        return (void *)phys_addr;
+    return vremap(phys_addr, size);
+}
+
+extern inline void iounmap(void *addr)
+{
+    if ((unsigned long)addr >= 0xA0000
+            && (unsigned long)addr < 0x100000)
+        return;
+    vfree(addr);
+}
+#  endif
+#endif
+
+/* Also, define check_mem_region etc */
+#ifndef LINUX_24
+#  define check_mem_region(a,b)     0 /* success */
+#  define request_mem_region(a,b,c) /* nothing */
+#  define release_mem_region(a,b)   /* nothing */
+#endif
+
+/* implement capable() for 2.0 */
+#ifdef LINUX_20
+#  define capable(anything)  suser()
+#endif
+
+/* The use_count of exec_domain and binfmt changed in 2.1.23 */
+
+#ifdef LINUX_20
+#  define INCRCOUNT(p)  ((p)->module ? __MOD_INC_USE_COUNT((p)->module) : 0)
+#  define DECRCOUNT(p)  ((p)->module ? __MOD_DEC_USE_COUNT((p)->module) : 0)
+#  define CURRCOUNT(p)  ((p)->module && (p)->module->usecount)
+#else
+#  define INCRCOUNT(p)  ((p)->use_count++)
+#  define DECRCOUNT(p)  ((p)->use_count--)
+#  define CURRCOUNT(p)  ((p)->use_count)
+#endif
+
+/*
+ * /proc has changed a lot across the versions...
+ */
+#ifdef LINUX_20
+#  define USE_PROC_REGISTER
+#endif
+
+
+/*
+ * 2.2 didn't have create_proc_{read|info}_entry yet.
+ * And it looks like there are no other "interesting" entry point, as
+ * the rest is somehow esotique (mknod, symlink, ...)
+ */
+#ifdef LINUX_22
+#  ifdef PROC_SUPER_MAGIC  /* Only if procfs is being used */
+extern inline struct proc_dir_entry *create_proc_read_entry(const char *name,
+                          mode_t mode, struct proc_dir_entry *base, 
+                          read_proc_t *read_proc, void * data)
+{
+    struct proc_dir_entry *res=create_proc_entry(name,mode,base);
+    if (res) {
+        res->read_proc=read_proc;
+        res->data=data;
+    }
+    return res;
+}
+
+#    ifndef create_proc_info_entry /* added in 2.2.18 */
+typedef int (get_info_t)(char *, char **, off_t, int, int);
+extern inline struct proc_dir_entry *create_proc_info_entry(const char *name,
+        mode_t mode, struct proc_dir_entry *base, get_info_t *get_info)
+{
+        struct proc_dir_entry *res=create_proc_entry(name,mode,base);
+        if (res) res->get_info=get_info;
+        return res;
+}
+#    endif  /* no create_proc_info_entry */
+#  endif
+#endif
+
+#ifdef LINUX_20
+#  define test_and_set_bit(nr,addr)  test_bit((nr),(addr))
+#  define test_and_clear_bit(nr,addr) clear_bit((nr),(addr))
+#  define test_and_change_bit(nr,addr) change_bit((nr),(addr))
+#endif
+
+
+/* 2.0 had no read and write memory barriers, and 2.2 lacks the
+   set_ functions */
+#ifndef LINUX_24
+#  ifdef LINUX_20
+#    define wmb() mb() /* this is a big penalty on non-reordering platfs */
+#    define rmb() mb() /* this is a big penalty on non-reordering platfs */
+#  endif /* LINUX_20 */
+
+#define set_mb() do { var = value; mb(); } while (0)
+#define set_wmb() do { var = value; wmb(); } while (0)
+#endif /* ! LINUX_24 */
+
+
+
+/* 2.1.30 removed these functions. Let's define them, just in case */
+#ifndef LINUX_20
+#  define queue_task_irq      queue_task
+#  define queue_task_irq_off  queue_task
+#endif
+
+/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */
+
+#ifdef LINUX_20
+
+#  include <asm/byteorder.h>
+#  ifdef __LITTLE_ENDIAN
+#    define cpu_to_le16(x) (x)
+#    define cpu_to_le32(x) (x)
+#    define cpu_to_be16(x) htons((x))
+#    define cpu_to_be32(x) htonl((x))
+#  else
+#    define cpu_to_be16(x) (x)
+#    define cpu_to_be32(x) (x)
+     extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);}
+     extern inline __u32 cpu_to_le32(__u32 x) { return (x>>24) |
+             ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24);}
+#  endif
+
+#  define le16_to_cpu(x)  cpu_to_le16(x)
+#  define le32_to_cpu(x)  cpu_to_le32(x)
+#  define be16_to_cpu(x)  cpu_to_be16(x)
+#  define be32_to_cpu(x)  cpu_to_be32(x)
+
+#  define cpu_to_le16p(addr) (cpu_to_le16(*(addr)))
+#  define cpu_to_le32p(addr) (cpu_to_le32(*(addr)))
+#  define cpu_to_be16p(addr) (cpu_to_be16(*(addr)))
+#  define cpu_to_be32p(addr) (cpu_to_be32(*(addr)))
+
+   extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);}
+   extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);}
+   extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);}
+   extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);}
+
+#  define le16_to_cpup(x) cpu_to_le16p(x)
+#  define le32_to_cpup(x) cpu_to_le32p(x)
+#  define be16_to_cpup(x) cpu_to_be16p(x)
+#  define be32_to_cpup(x) cpu_to_be32p(x)
+
+#  define le16_to_cpus(x) cpu_to_le16s(x)
+#  define le32_to_cpus(x) cpu_to_le32s(x)
+#  define be16_to_cpus(x) cpu_to_be16s(x)
+#  define be32_to_cpus(x) cpu_to_be32s(x)
+
+#endif
+
+#ifdef LINUX_20
+#  define __USE_OLD_REBUILD_HEADER__
+#endif
+
+/*
+ * 2.0 didn't include sema_init, so we make our own - but only if it
+ * looks like semaphore.h got included.
+ */
+#ifdef LINUX_20
+#  ifdef MUTEX_LOCKED   /* Only if semaphore.h included */
+     extern inline void sema_init (struct semaphore *sem, int val)
+     {
+         sem->count = val;
+         sem->waking = sem->lock = 0;
+         sem->wait = NULL;
+     }
+#  endif
+#endif /* LINUX_20 */
+
+/*
+ * In 2.0, there is no real need for spinlocks, and they weren't really
+ * implemented anyway.
+ *
+ * XXX the _irqsave variant should be defined eventually to do the
+ * right thing.
+ */
+#ifdef LINUX_20
+typedef int spinlock_t;
+#  define spin_lock(lock)
+#  define spin_unlock(lock)
+#  define spin_lock_init(lock)
+
+#  define spin_lock_irqsave(lock,flags) do { \
+        save_flags(flags); cli(); } while (0);
+#  define spin_unlock_irqrestore(lock,flags) restore_flags(flags);
+#endif
+
+/*
+ * 2.1 stuffed the "flush" method into the middle of the file_operations
+ * structure.  The FOP_NO_FLUSH symbol is for drivers that do not implement
+ * flush (most of them), it can be inserted in initializers for all 2.x
+ * kernel versions.
+ */
+#ifdef LINUX_20
+#  define FOP_NO_FLUSH   /* nothing */
+#  define TAG_LLSEEK    lseek
+#  define TAG_POLL      select
+#else
+#  define FOP_NO_FLUSH  NULL,
+#  define TAG_LLSEEK    llseek
+#  define TAG_POLL      poll
+#endif
+
+
+
+/*
+ * fasync changed in 2.2.
+ */
+#ifdef LINUX_20
+/*  typedef struct inode *fasync_file; */
+#  define fasync_file struct inode *
+#else
+  typedef int fasync_file;
+#endif
+
+/* kill_fasync had less arguments, and a different indirection in the first */
+#ifndef LINUX_24
+#  define kill_fasync(ptrptr,sig,band)  kill_fasync(*(ptrptr),(sig))
+#endif
+
+/* other things that are virtualized: define the new functions for the old k */
+#ifdef LINUX_20
+#  define in_interrupt() (intr_count!=0)
+#  define mdelay(x) udelay((x)*1000)
+#  define signal_pending(current)  ((current)->signal & ~(current)->blocked)
+#endif
+
+#ifdef LINUX_PCI_H /* only if PCI stuff is being used */
+#  ifdef LINUX_20
+#    include "pci-compat.h" /* a whole set of replacement functions */
+#  else
+#    define  pci_release_device(d) /* placeholder, used in 2.0 to free stuff */
+#  endif
+#endif
+
+
+
+/*
+ * Some task state stuff
+ */
+
+#ifndef set_current_state
+#  define set_current_state(s) current->state = (s);
+#endif
+
+#ifdef LINUX_20
+extern inline void schedule_timeout(int timeout)
+{
+    current->timeout = jiffies + timeout;
+    current->state = TASK_INTERRUPTIBLE;
+    schedule();
+    current->timeout = 0;
+}
+
+extern inline long sleep_on_timeout(wait_queue_head_t *q, signed long timeout)
+{
+    signed long early = 0;
+        
+    current->timeout = jiffies + timeout;
+    sleep_on (q);
+    if (current->timeout > 0) {
+        early = current->timeout - jiffies;
+        current->timeout = 0;
+    }
+    return early;
+}
+
+
+extern inline long interruptible_sleep_on_timeout(wait_queue_head_t *q,
+                signed long timeout)
+{
+    signed long early = 0;
+        
+    current->timeout = jiffies + timeout;
+    interruptible_sleep_on (q);
+    if (current->timeout > 0) {
+        early = current->timeout - jiffies;
+        current->timeout = 0;
+    }
+    return early;
+}
+
+#endif /* LINUX_20 */
+
+/*
+ * Schedule_task was a late 2.4 addition.
+ */
+#ifndef LINUX_24
+extern inline int schedule_task(struct tq_struct *task)
+{
+        queue_task(task, &tq_scheduler);
+        return 1;
+}
+#endif
+
+
+/*
+ * Timing issues
+ */
+#ifdef LINUX_20
+#  define get_fast_time do_gettimeofday
+#endif
+
+#ifdef _LINUX_DELAY_H /* only if linux/delay.h is included */
+#  ifndef mdelay /* linux-2.0 */
+#    ifndef MAX_UDELAY_MS
+#      define MAX_UDELAY_MS   5
+#    endif
+#    define mdelay(n) (\
+        (__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \
+        ({unsigned long msec=(n); while (msec--) udelay(1000);}))
+#  endif /* mdelay */
+#endif /* _LINUX_DELAY_H */
+
+
+/*
+ * No del_timer_sync before 2.4
+ */
+#ifndef LINUX_24
+#  define del_timer_sync(timer) del_timer(timer)  /* and hope */
+#endif
+
+/*
+ * mod_timer wasn't present in 2.0
+ */
+#ifdef LINUX_20
+static inline int mod_timer(struct timer_list *timer, unsigned long expires)
+{
+    int pending = del_timer(timer);
+    if (pending) {
+        timer->expires = expires;
+        add_timer(timer);
+    }
+    return pending;
+}
+#endif
+/*
+ * Various changes in mmap and friends.
+ */
+
+#ifndef NOPAGE_SIGBUS
+#  define NOPAGE_SIGBUS  NULL  /* return value of the nopage memory method */
+#  define NOPAGE_OOM     NULL  /* No real equivalent in older kernels */
+#endif
+
+#ifndef VM_RESERVED            /* Added 2.4.0-test10 */
+#  define VM_RESERVED 0
+#endif
+
+#ifdef LINUX_24 /* use "vm_pgoff" to get an offset */
+#define VMA_OFFSET(vma)  ((vma)->vm_pgoff << PAGE_SHIFT)
+#else /* use "vm_offset" */
+#define VMA_OFFSET(vma)  ((vma)->vm_offset)
+#endif
+
+#ifdef MAP_NR
+#define virt_to_page(page) (mem_map + MAP_NR(page))
+#endif
+
+#ifndef get_page
+#  define get_page(p) atomic_inc(&(p)->count)
+#endif
+
+/*
+ * No DMA lock in 2.0.
+ */
+#ifdef LINUX_20
+static inline unsigned long claim_dma_lock(void)
+{
+    unsigned long flags;
+    save_flags(flags);
+    cli();
+    return flags;
+}
+
+static inline void release_dma_lock(unsigned long flags)
+{
+    restore_flags(flags);
+}
+#endif
+
+
+/*
+ * I/O memory was not managed by ealier kernels, define them as success
+ */
+
+#if 0 /* FIXME: what is the right way to do request_mem_region? */
+#ifndef LINUX_24
+#  define check_mem_region(start, len)          0
+#  define request_mem_region(start, len, name)  0
+#  define release_mem_region(start, len)        0
+
+   /*
+    * Also, request_ and release_ region used to return void. Return 0 instead
+    */
+#  define request_region(s, l, n)  ({request_region((s),(l),(n));0;})
+#  define release_region(s, l)     ({release_region((s),(l));0;})
+
+#endif /* not LINUX_24 */
+#endif
+
+/*
+ * Block layer stuff.
+ */
+#ifndef LINUX_24
+
+/* BLK_DEFAULT_QUEUE for use with these macros only!!!! */
+#define BLK_DEFAULT_QUEUE(major) blk_dev[(major)].request_fn
+#define blk_init_queue(where,request_fn) where = request_fn;
+#define blk_cleanup_queue(where) where = NULL;
+
+/* No QUEUE_EMPTY in older kernels */
+#ifndef QUEUE_EMPTY  /* Driver can redefine it too */
+#  define QUEUE_EMPTY (CURRENT != NULL)
+#endif
+
+#ifdef RO_IOCTLS
+static inline int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg)
+{
+    int err;
+
+    switch (cmd) {
+      case BLKRAGET: /* return the readahead value */
+        if (!arg)  return -EINVAL;
+        err = ! access_ok(VERIFY_WRITE, arg, sizeof(long));
+        if (err) return -EFAULT;
+        PUT_USER(read_ahead[MAJOR(dev)],(long *) arg);
+        return 0;
+
+      case BLKRASET: /* set the readahead value */
+        if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+        if (arg > 0xff) return -EINVAL; /* limit it */
+        read_ahead[MAJOR(dev)] = arg;
+        return 0;
+
+      case BLKFLSBUF: /* flush */
+        if (! capable(CAP_SYS_ADMIN)) return -EACCES; /* only root */
+        fsync_dev(dev);
+        invalidate_buffers(dev);
+        return 0;
+
+        RO_IOCTLS(dev, arg);
+    }
+    return -ENOTTY;
+}
+#endif  /* RO_IOCTLS */
+
+#ifdef LINUX_EXTENDED_PARTITION /* defined in genhd.h */
+static inline void register_disk(struct gendisk *gdev, kdev_t dev,
+                unsigned minors, struct file_operations *ops, long size)
+{
+    if (! gdev)
+        return;
+    resetup_one_dev(gdev, MINOR(dev) >> gdev->minor_shift);
+}
+#endif /* LINUX_EXTENDED_PARTITION */
+
+
+#else  /* it is Linux 2.4 */
+#define HAVE_BLKPG_H
+#endif /* LINUX_24 */
+
+
+
+#ifdef LINUX_20 /* physical and virtual addresses had the same value */
+#  define __pa(a) (a)
+#  define __va(a) (a)
+#endif
+
+/*
+ * Network driver compatibility
+ */
+
+/*
+ * 2.0 dev_kfree_skb had an extra arg.  The following is a little dangerous
+ * in that it assumes that FREE_WRITE is always wanted.  Very few 2.0 drivers
+ * use FREE_READ, but the number is *not* zero...
+ *
+ * Also: implement the non-checking versions of a couple skb functions -
+ * but they still check in 2.0.
+ */
+#ifdef LINUX_20
+#  define dev_kfree_skb(skb) dev_kfree_skb((skb), FREE_WRITE);
+
+#  define __skb_push(skb, len) skb_push((skb), (len))
+#  define __skb_put(skb, len)  skb_put((skb), (len))
+#endif
+
+/*
+ * Softnet changes in 2.4
+ */
+#ifndef LINUX_24
+#  ifdef _LINUX_NETDEVICE_H /* only if netdevice.h was included */
+#  define netif_start_queue(dev) clear_bit(0, (void *) &(dev)->tbusy);
+#  define netif_stop_queue(dev)  set_bit(0, (void *) &(dev)->tbusy);
+
+static inline void netif_wake_queue(struct device *dev)
+{
+    clear_bit(0, (void *) &(dev)->tbusy);
+    mark_bh(NET_BH);
+}
+
+/* struct device became struct net_device */
+#  define net_device device
+#  endif /* netdevice.h */
+#endif /* ! LINUX_24 */
+
+/*
+ * Memory barrier stuff, define what's missing from older kernel versions
+ */
+#ifdef switch_to /* this is always a macro, defined in <asm/sysstem.h> */
+
+#  ifndef set_mb
+#    define set_mb(var, value) do {(var) = (value); mb();}  while 0
+#  endif
+#  ifndef set_rmb
+#    define set_rmb(var, value) do {(var) = (value); rmb();}  while 0
+#  endif
+#  ifndef set_wmb
+#    define set_wmb(var, value) do {(var) = (value); wmb();}  while 0
+#  endif
+
+/* The hw barriers are defined as sw barriers. A correct thing if this
+   specific kernel/platform is supported but has no specific instruction */
+#  ifndef mb
+#    define mb barrier
+#  endif
+#  ifndef rmb
+#    define rmb barrier
+#  endif
+#  ifndef wmb
+#    define wmb barrier
+#  endif
+
+#endif /* switch to (i.e. <asm/system.h>) */
+
+
+#endif /* _SYSDEP_H_ */