--- /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_ */