/* par0.c Simple driver for the parallel port for read/write via the 8 data pins (+ 8x 330 Ohm). Todo: Read of ASCII instead of binary numbers. This Program is free software; you can redistribute it and/od 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 WARRENTY; without even the implied warrenty of MERCHANTABILITY of 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. Rolf Freitag 2005, rolf.freitag at email.de */ #ifdef __sparc__ # error "This Program can't run on the Sparc platform, where there's no concept of I/O space." #endif // define default parallel port #undef PPB0 #ifdef __alpha__ # define PPB0 0x3bc #else # define PPB0 0x378 /* default 0x378, first parallel port (onboard), 0x278 the second ... */ #endif #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #include /* for the device driver */ #include /* u16, u32 and such */ #include /* for the module */ #include /* for verify_area */ #include /* printk() */ #include /* register_chrdev, everything... */ #include /* error codes */ #include /* size_t */ #include /* check_region and such */ #include // udelay #include /* copy_to_user and such */ #include /* inb */ //#include "/usr/include/signal.h" #include #include #include #include //#include #include #include // do_sigaction, kernel_thread //#include //#include // CONFIG_PCI //#include // pcibios_find_device .. //#include // ENODEV ... //#define PPB1 0x278 /* Parport base for /dev/parport1: 0x278 (this may not always be the case) */ #define port_range 4 /* I/O-range for the status and the controll register of the standard parallel port */ int par0_io; MODULE_PARM (par0_io, "i"); // parallel port base (=data register address in i/o-space) int par0_delay; MODULE_PARM (par0_delay, "i"); // Initial duration of a high/low periode. The delay between two edges is approx. (par0_delay -1)µs. // measured with par0_delay = 1, 2 and 1001: 500 kHz, 300 kHz, 500 Hz. int par0write; MODULE_PARM (par0write, "i"); // should the port be used for writing or reading (i == 0) MODULE_LICENSE ("GPL"); static int par0_major = 189; static unsigned int ppb = PPB0; static int thread_id = 0; // static wait_queue_head_t wq; static DECLARE_COMPLETION (on_exit); struct file_operations par0_fops; static int thread_code (void *data) { unsigned long ul; daemonize ("par0Thread"); allow_signal (SIGKILL); allow_signal (SIGTERM); for (;;) { outb (0xff, ppb); // on if ((ul = par0_delay)) udelay (ul - 1); else break; outb (0x00, ppb); // off if ((ul = par0_delay)) udelay (ul - 1); else break; } thread_id = 0; complete_and_exit (&on_exit, 0); } int __init init_module (void) { const char *name = "par0"; int result = 0; if (par0_io > 0) ppb = par0_io; printk (KERN_CRIT "par0_io=%d\n", par0_io); if (0 == request_region (ppb, port_range, name)) { printk (KERN_CRIT "module par0: Cannot get the nessisary I/O-region.\n"); return (-1); } outb (0x0b, ppb + 2); /* write mode */ thread_id = kernel_thread (thread_code, NULL, CLONE_KERNEL); if (thread_id == 0) return -EIO; if ((result = register_chrdev (par0_major, "par0", &par0_fops)) < 0) { result = register_chrdev (0, "par0", &par0_fops); /* dynamic major number allocation */ par0_major = result; if (result < 0) { printk (KERN_CRIT "module par0: Cannot register device.\n"); release_region (ppb, port_range); return result; } } return 0; /* default done */ } /* init_module */ void __exit cleanup_module (void) { if (thread_id) kill_proc (thread_id, SIGTERM, 1); wait_for_completion (&on_exit); outb (0x00, ppb); // off unregister_chrdev (par0_major, "par0"); release_region (ppb, port_range); } int par0_open (struct inode *inode, struct file *filp) /* inode->i_rdev = 0 */ { // MOD_INC_USE_COUNT; // for kernelversion < 2.6 return 0; } int par0_close (struct inode *inode, struct file *filp) { // MOD_DEC_USE_COUNT; // for kernelversion < 2.6 return 0; } // read from user space static ssize_t par0_read (struct file *filp, char *buff, size_t count, loff_t * ppos) { unsigned char uc; par0_delay=0; // for killing the thread while (thread_id) schedule_timeout (1); // wait outb (0x2b, ppb + 2); /* read mode */ mdelay (1); // wait 1 ms uc = inb (ppb); /* direct reading */ __copy_to_user (buff, &uc, 1); /* copy 1 byte */ return (1); // return and remain in read mode without the thread } // write from user space static ssize_t par0_write (struct file *filp, __user const char *buff, size_t count, loff_t * ppos) { __copy_from_user (&par0_delay, buff, 4); /* copy 4 byte */ outb (0x0b, ppb + 2); /* write mode */ if (0 == thread_id) // no current thread { if (par0_delay) // it thread is allowed { thread_id = kernel_thread (thread_code, NULL, CLONE_KERNEL); if (thread_id == 0) return -EIO; } } return (4); // return and remain in write mode with the thread } struct file_operations par0_fops = { read:par0_read, write:par0_write, open:par0_open, release:par0_close, }; #undef PPB0