/* * 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 * 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 #include #include #include #include #include #include #include #include #include #include /* 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;ii_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>= 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