# HG changeset patch # User heiko # Date 1011186115 -3600 # Node ID c9b8efdb53699de94cead62c917aed8a4e9bd64a Initial revision diff -r 000000000000 -r c9b8efdb5369 GNU_GPL --- /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. + + + Copyright (C) 19yy + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff -r 000000000000 -r c9b8efdb5369 Makefile --- /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 diff -r 000000000000 -r c9b8efdb5369 README --- /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 + +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 + +The major number you already know. 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 + + + + + + + + + diff -r 000000000000 -r c9b8efdb5369 me8100-driver --- /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 + + + diff -r 000000000000 -r c9b8efdb5369 me8100.c --- /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 +#ifdef CONFIG_SMP +# define __SMP__ +#endif + + +/* + * Basic facilities for modules. + * Defines __module_kernel_version. + * Includes (UTS_RELEASE, LINUX_VERSION_CODE, ...) + */ +#include + +/* + * Needed for the registration of I/O and MEMORY regions. + * (request_region, ...) + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include + + +/* 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"); + } +} diff -r 000000000000 -r c9b8efdb5369 me8100.h --- /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) diff -r 000000000000 -r c9b8efdb5369 me8100_test_counter/Makefile --- /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 diff -r 000000000000 -r c9b8efdb5369 me8100_test_counter/me8100_test_counter.c --- /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 +#include +#include +#include +#include +#include + +#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; +} diff -r 000000000000 -r c9b8efdb5369 me8100_test_dio/Makefile --- /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 diff -r 000000000000 -r c9b8efdb5369 me8100_test_dio/me8100_test_dio.c --- /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 +#include +#include +#include +#include +#include + +#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; +} diff -r 000000000000 -r c9b8efdb5369 me8100_test_int/Makefile --- /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 diff -r 000000000000 -r c9b8efdb5369 me8100_test_int/me8100_test_int.c --- /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 +#include +#include +#include +#include +#include + +#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; +} diff -r 000000000000 -r c9b8efdb5369 pci-compat.h --- /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 is already included, *and* + * we are using 2.0. +*/ +#if defined(LINUX_PCI_H) && (LINUX_VERSION_CODE & 0xffff00) == 0x020000 + +#include /* pcibios_* */ +#include /* 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_ */ + + + + + diff -r 000000000000 -r c9b8efdb5369 sysdep.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 +#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 +# endif +#endif + +#include + +/* 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 /* 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 +#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 +# 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 +# include +# 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 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 +# 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 */ + +# 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. ) */ + + +#endif /* _SYSDEP_H_ */