initial checkin from subversion

This commit is contained in:
Tretter
2017-02-07 20:03:49 +00:00
commit ed54469421
108 changed files with 1999 additions and 0 deletions

486
par.c Normal file
View File

@@ -0,0 +1,486 @@
/*
* par.c V0.32
*
* Copyright (C) by R.E.Wolff -- R.E.Wolff@BitWizard.nl
*
* This software may be used and distributed according to the terms
* of the GNU Public License, incorporated herein by reference.
*
* I prefer if you try to contact me if you have enhancements,
* instead of forking off a different branch.....
*
* date by what
* Written: Apr 8 1996 REW initial revision
* changed: Apr 11 1996 REW Added some more comments.
* changed: May 12 1996 REW Finished the "read" code.
* changed: May 15 1996 REW Fixed detection. Code cleanup.
* changed: May 16 1996 PK Removed depreciated put_fs_byte call
* Fixed the BIT(x) macro
* The wake-up woke up the wrong process
* Added and removed some debugging code
* Init_module now initializes rstate
* Locked out forbidden bits
* Inverted inverting inputs and outputs
* Made D0..D7 readable (renumbered others)
* changed: May 18 1996 REW Incorporated above changes with cleanup.
* fixed crash at rmmod time.
* changed: May 23 1996 PK Added locking for outputs
*
* who-is-who:
* initials full name Email address
* REW Roger E. Wolff R.E.Wolff@BitWizard.nl
* PK Peter Knoppers knop@duteca.et.tudelft.nl
*
* This kernel module can be used instead of lp or plip.
* It will give you access to the bits on the parallel port.
*
* You can control external devices this way. As an example, here
* is the schematic for a set of 8 leds:
*
* name pinno
* / _____
* D0 2 -----|>|---|_____|-------+
* / _____ |
* D1 3 -----|>|---|_____|-------+
* / _____ |
* D2 4 -----|>|---|_____|-------+
* / _____ |
* D3 5 -----|>|---|_____|-------+
* / _____ |
* D4 6 -----|>|---|_____|-------+
* / _____ |
* D5 7 -----|>|---|_____|-------+
* / _____ |
* D6 8 -----|>|---|_____|-------+
* / _____ |
* D7 9 -----|>|---|_____|-------+
* |
* GND 25 -------------------------+
*
* /
* You can use any led ( -|>|- ) you can find. The resistors need
* to be in the 200 to 1k region. 200 ohms will make them quite bright.
* (14 ma current), I have them much weaker (3.5 ma current).
* For testing on my laptop, I currently use 810 ohms..... A modern
* datasheet mentions 14 ma as the source drive capability.
*
* Original Paralel ports are spec-ed to 2 ma at 2.4 volts. The IEEE1284
* spec states 14ma. This is good enough for a led :-)
*
**************************************************************************
*
* For reference: the pinout of the par port:
* data relative
* pin pin direction minor
* number name in/out number
* 1 STROBE out ?
* 2 D0 out 0
* 3 D1 out 1
* 4 D2 out 2
* 5 D3 out 3
* 6 D4 out 4
* 7 D5 out 5
* 8 D6 out 6
* 9 D7 out 7
* 10 ACK in
* 11 BUSY in
* 12 PAPOUT in
* 13 SLCT in
* 14 AUTOFD out ?
* 15 ERROR in
* 16 INIT out ?
* 17 SLCTIN out ?
* 18-25 GND
*
**************************************************************************
*
* The devices in /dev are:
*
* par0a - par0h (minors 0-7 ) -> D0-D7 on lp0,
* par0i - par0l (minors 8-11) -> control outputs on lp0
* par1a - par1h (minors 16-23) -> D0-D7 on lp1,
* par1i - par1l (minors 24-27) -> control outputs on lp1
* par2a - par2h (minors 32-39) -> D0-D7 on lp2,
* par2i - par2l (minors 40-43) -> control outputs on lp2
*
* Most modern computers only have lp1.
*
* use
* mknod parXY c 60 <minor>
* where 60 is the (current) major number....
*
* insmod with "base=0xYYY" if you have a port at a weird address.
* (-- untested: tell me if it works... :-)
*
* - Inputs.
* A par port has about 5 inputs. They can be polled at say
* 50 ms intervals without too much overhead. (My 386sx25 has
* been doing that about 200 million times in 4 months)
* the driver will implement blocking-read (no polling in userspace!).
* DONE.
**************************************************************************
*
* BUGS and restrictions:
*
* (bug me about it if you want it fixed/enhanced -- that's why
* they are called bugs..... :-)
*
* -More than 5 inputs.
* IF (notice the big if....) your port is 8255 compatible
* without any buffering between the port and the chip, it
* should be possible to reconfigure the chip to give about 13
* inputs available on the connector.
*
*
* -A major number should be allocated with the devices coordinator.
* -- Under way....
*
* -Like lp.c and plip.c this doesn't allocate the region at open-time
* but when the device is initialized. This means that you can't compile
* two of these into the kernel. If I'm going to reconfigure the 8255,
* it might not even be a bad idea to keep it this way.....
*
* -Cannot be compiled into the kernel. Only module.
*
* -driver interpreted scripts. Allows low-overhead and accurate
* timing to be done....
*/
#include <linux/module.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/segment.h>
/* Debugging stuff. */
#ifdef EBUG
#define DEBUG
#endif
#ifdef DEBUG
#define dprintk(a) printk a
#else
#define dprintk(f)
#endif
/* Administratrivia */
#ifndef PAR_MAJOR
#define PAR_MAJOR 60
#endif
/* Hardware related stuff */
#define NR_BITS_PER_PORT 16 /* best if this is a power of two */
#define ALLOWEDWRITEBITS 0x0fff /* three bits reserved, one bit enables IRQ */
#define INVERTEDOUTPUTS 0x0b00 /* three bits are inverted in the hardware */
#define ALLOWEDREADBITS 0xf8ff /* two bits reserved, one bit is IRQ status */
#define INVERTEDINPUTBITS 0x8000 /* one bit is inverted in the hardware */
#define POLL_INTERVAL 5
#define NR_PAR 3
int base[NR_PAR]={0x3bc,0x378,0x278};
int exists[NR_PAR]={0,};
#if NR_BITS_PER_PORT != 16
#warning "There are several places where NR_BITS_PER_PORT is fixed at 16"
#warning "Just changing the define doesn't work."
#endif
/* Local variables. */
static unsigned short state[NR_PAR]={0,};
static int nreaders = 0;
static struct timer_list par_timer;
struct switch_info {
struct wait_queue *waitq;
int bitval;
int nreaders;
int nwriters;
};
struct switch_info pdat[NR_PAR*NR_BITS_PER_PORT];
#define BIT(x) ((rstate[(x) / NR_BITS_PER_PORT] >> ((x) % NR_BITS_PER_PORT)) & 1)
static unsigned short rstate[NR_PAR];
static unsigned short orstate[NR_PAR];
static unsigned long lastread;
#define DATAPORT(i) (base[i]+0)
#define STATUSPORT(i) (base[i]+1)
#define CTRLPORT(i) (base[i]+2)
static void update_rstate (void)
{
int i;
for (i=0;i<NR_PAR;i++)
if (exists[i]) {
rstate[i] = (((inb (DATAPORT(i)) & 0xff) ) |
((inb (STATUSPORT(i)) * 0xff) << 8)) ^
INVERTEDINPUTBITS;
}
lastread = jiffies;
}
static int par_write(struct inode * inode,
struct file * file,
const char * buf,
int count)
{
unsigned int minor = MINOR(inode->i_rdev);
int port,bit;
port = minor / NR_BITS_PER_PORT;
bit = minor % NR_BITS_PER_PORT;
buf += count-1;
if (get_user (buf) & 1)
set_bit (minor,state);
else
clear_bit (minor,state);
dprintk (("setting port %d (0x%3x) to %02x.\n",
port,DATAPORT(port),state[port]));
if (bit % NR_BITS_PER_PORT < 8)
outb ((state[port] ^ INVERTEDOUTPUTS) , DATAPORT(port));
else
outb ((state[port] ^ INVERTEDOUTPUTS) >> 8, CTRLPORT(port));
return count;
}
/*
* Bug: if you are not guaranteed to see all transitions.
*
* Bug: If two processes wait for the same input pin, only
* One will correctly work (maybe alternating). This needs fixing.
*/
static int par_read(struct inode * inode, struct file * file,
char * buf, int count)
{
int bitno = MINOR(inode->i_rdev);
int t;
struct switch_info *pd = pdat+bitno;
dprintk(("par_read: request for minor %d, current value = %d\n", bitno,
pd->bitval));
while (pd->bitval == (t = BIT(bitno))) {
interruptible_sleep_on(& pd->waitq);
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
}
pd->bitval = t;
put_user (t,buf++);
return 1;
}
/* XXX Is this correct? */
static int par_select(struct inode * inode, struct file * file,
int sel_type, select_table * wait)
{
int bitno = MINOR(inode->i_rdev);
int bitval;
/* Check sel_type? */
bitval = pdat[bitno].bitval;
if (bitval != BIT (bitno)) return 1;
select_wait( & pdat[bitno].waitq, wait);
return 0;
}
static void par_timer_function (unsigned long data)
{
int i,j;
unsigned short t;
if (lastread != jiffies) update_rstate ();
for (i=0;i<NR_PAR;i++) {
if ((t = orstate[i] ^ rstate[i])) {
for (j=i*NR_BITS_PER_PORT;t != 0;t >>= 1,j++) {
if ((t & 1) && (pdat[j].nreaders)) {
wake_up(&pdat[j].waitq);
}
}
orstate[i] = rstate[i];
}
}
par_timer.expires = jiffies + POLL_INTERVAL;
add_timer (&par_timer);
}
static int par_open(struct inode * inode, struct file * file)
{
int minor = MINOR(inode->i_rdev);
dprintk (("par_open called. Minor = %d.\n", minor));
if (file->f_flags == O_RDWR) {
printk ("par: Cannot do both read and write on minor %d.\n",minor);
return -EPERM;
}
if (!exists [minor / NR_BITS_PER_PORT])
return -ENODEV;
if (file->f_flags == O_RDONLY) {
if ((1 << (minor % NR_BITS_PER_PORT) & ALLOWEDREADBITS) == 0)
return -ENODEV;
pdat[minor].bitval = -1;
pdat[minor].nreaders++;
dprintk(("par_open: nreaders for minor %d is now %d\n", minor,
pdat[minor].nreaders));
if (!nreaders) {
init_timer (&par_timer);
par_timer.function = par_timer_function;
par_timer.expires = jiffies+POLL_INTERVAL;
add_timer (&par_timer);
dprintk(("par_open: timer added, at jiffies=%ld, expires at %ld\n",
jiffies, par_timer.expires));
dprintk(("par_open: rstate=[%02x,%02x,%02x]\n",
rstate[0], rstate[1], rstate[2]));
}
nreaders++;
} else {
if ((1 << (minor % NR_BITS_PER_PORT) & ALLOWEDWRITEBITS) == 0)
return -ENODEV;
if (pdat[minor].nwriters)
return -EBUSY;
pdat[minor].nwriters++;
}
MOD_INC_USE_COUNT;
return 0;
}
static void par_release(struct inode * inode, struct file * file)
{
if (file->f_flags == O_RDONLY) {
nreaders--;
if (!nreaders) {
del_timer (&par_timer);
}
} else
pdat[MINOR(inode->i_rdev)].nwriters--;
MOD_DEC_USE_COUNT;
}
static struct file_operations par_fops = {
NULL, /* par_lseek */
par_read, /* par_read */
par_write, /* par_write */
NULL, /* par_readdir */
par_select, /* par_select */
NULL, /* par_ioctl */
NULL, /* par_mmap */
par_open, /* par_open */
par_release /* par_release */
};
#ifdef MODULE
int init_module (void)
{
int i,sz,t,nports;
if (register_chrdev(PAR_MAJOR,"par",&par_fops)) {
printk("par: unable to get major %d\n", PAR_MAJOR);
return -EIO;
}
nports = 0;
for (i=0;i<NR_PAR;i++) {
sz = (base[i] & 4) ? 3 : 8;
if (check_region (base[i],sz))
continue;
outb (0,DATAPORT(i));
udelay (50);
t = inb (DATAPORT(i));
if (t != 0)
continue;
exists[i]++;
request_region (base[i],sz,"par");
/* initialize state[i] with the current state of the hardware */
state[i] = (((inb (DATAPORT(i)) & 0xff) ) |
((inb (CTRLPORT(i)) & 0xff) << 8)) ^ INVERTEDOUTPUTS;
nports++;
printk ("par: port %d at 0x%3x. Current value: 0x%04x\n",
i,base[i],state[i]);
}
update_rstate();
return nports?0:-EIO;
}
void cleanup_module(void)
{
int i;
int sz;
/* Most modules don't check this it must be ok to continue anyway.... */
if (MOD_IN_USE) {
printk ("par module is still in use. Now what?\n");
/* return; */
}
/* Don't forget this..... (BOOM) */
if (nreaders)
del_timer (&par_timer);
unregister_chrdev(PAR_MAJOR,"par");
for (i = 0; i < NR_PAR; i++) {
sz = (base[i] & 4) ? 3 : 8;
if (exists[i])
release_region(base[i],sz);
}
printk ("par_cleanup: Bye bye...\n");
}
#else
#warning I am not sure if you can compile this into the kernel.....
#warning For instance the initialization is never called....
#endif
/*
* Local variables:
* compile-command: "cc -DMODULE -D__KERNEL__ -Wall -O2 -DEBUG -c par.c
* tab-width: 8
* End:
*/