/* pit.c * Version 0.0 * * Driver for the 8253 or 8254 programmable interval timer chip, attached to * the ISA bus or to the parallel port. * * This code's under the GNU General Public License. * * Written by Andy Goth , 2003. * * Assignment for CSE 3442-001 lab 5. * Dr. Roger Walker, University of Texas at Arlington */ #ifndef __KERNEL__ #define __KERNEL__ #endif #ifndef MODULE #define MODULE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pit.h" /* --- Function prototypes. --- */ /* Module loading. */ static int pit_init (void); static void pit_exit (void); /* File operations. */ static loff_t pit_llseek (struct file*, loff_t, int); static int pit_read (struct file*, char*, size_t, loff_t*); static unsigned int pit_poll(struct file*, poll_table*); static int pit_ioctl (struct inode*, struct file*, unsigned int, unsigned long); static int pit_open (struct inode*, struct file*); static int pit_release (struct inode*, struct file*); static int pit_fasync (int, struct file*, int); /* Miscellaneous helpers. */ static int pit_reg_io (char*, int, int); static void pit_cleanup (void); /* Hardware access. */ static void pit_interrupt (int, void*, struct pt_regs*); static void pit_out_timer (unsigned char, unsigned int); static void pit_outb (unsigned char, unsigned int); /* --- Module documentation. --- */ MODULE_AUTHOR ("Andy Goth "); MODULE_DESCRIPTION ("Parport 8253/8254 PIT device driver"); MODULE_LICENSE ("GPL"); MODULE_SUPPORTED_DEVICE("8253/8254 PIT"); EXPORT_NO_SYMBOLS; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) module_init (pit_init); module_exit (pit_exit); #else static int module_init(void) {return pit_init();} static void module_exit(void) { pit_exit();} #endif /* --- Module options. --- */ #define MOD_OPTION(name, def, desc) \ int name = def; \ MODULE_PARM(name, "i"); \ MODULE_PARM_DESC(name, desc); MOD_OPTION(debug, 0, "Debug level"); MOD_OPTION(parport_io, 0x378, "Parallel port: base I/O port"); MOD_OPTION(parport_irq, 7, "Parallel port: IRQ number"); MOD_OPTION(timer_io, 0x304, "8253 data bus: base I/O port; " "0 indicates direct attachment to parport"); /* --- Global variables. --- */ /* If nonzero, each /dev/pit open besides the first causes data to be written * to the parallel port. If a wire connects parport pins 9 and 10, then this * causes an interrupt. * * At a command prompt, type the following to generate interrupts at a rate of * approximately ten per second. The call to dd opens /dev/pit and immediately * closes it again, and the call to sleep inserts the proper delay. Press ^c * to stop the loop. * * while [ 1 ]; do * dd if=/dev/pit of=/dev/null count=0 2> /dev/null * sleep 0.1s * done * * Of course, this is just for testing, in the absense of a real 8253. It * probably shouldn't be used in conjunction with timer_io = 0. Also, it's * better to start the actual /dev/pit monitor program before the above. */ static const int pit_interrupt_hack = 0; /* Nonzero if the 8253 is attached directly to the parport. */ static int pit_direct; /* Number of times /dev/pit is currently opened. */ static int pit_open_count; /* Number of ticks since the last read. */ static unsigned long pit_tick_count; /* If true, ticks are counted. If false, the parport masks interrupts. */ static int pit_ticks_enabled; /* For blocking reads and poll/select... */ static DECLARE_WAIT_QUEUE_HEAD(pit_read_queue); /* And for aynchronous I/O... */ static struct fasync_struct* pit_async_queue; /* Current divisor value. */ static pit_divisor_t pit_divisor; /* Flags for pit_cleanup. */ static int pit_have_misc_dev; static int pit_have_parport_io; static int pit_have_parport_irq; static int pit_have_timer_io; /* Just a magic value. */ static char* pit_magic = "You're a duck!"; /* File operations table. */ static struct file_operations pit_fops = { .llseek = pit_llseek, .read = pit_read, .poll = pit_poll, .ioctl = pit_ioctl, .open = pit_open, .release = pit_release, .fasync = pit_fasync }; /* /dev/pit registration information. */ static struct miscdevice pit_device = { MISC_DYNAMIC_MINOR, "pit", &pit_fops }; /* --- Function definitions. --- */ /* Called on module load. */ static int __init pit_init(void) { /* Direct parport attachment? */ pit_direct = (timer_io == 0); /* Register /dev/pit. */ if (misc_register(&pit_device) < 0) { printk(KERN_ERR "pit_init: unable to register misc char " "device /dev/pit\n"); pit_cleanup(); return -EBUSY; } pit_have_misc_dev = 1; /* Register the parport io ports. */ if (pit_reg_io("pit (parport)", parport_io, 4) < 0) { pit_cleanup(); return -EBUSY; } pit_have_parport_io = 1; /* If connected to the ISA bus, register the 8253 timer ports. */ if (!pit_direct) { if (pit_reg_io("pit (8253 data)", timer_io, 4) < 0) { pit_cleanup(); return -EBUSY; } pit_have_timer_io = 1; } /* Set the 8253/8254 control words: * * 0x36 = 0 0 1 1 0 1 1 0 * 0x76 = 0 1 1 1 0 1 1 0 * 0xb6 = 1 0 1 1 0 1 1 0 * `.' `.' `._.' `-- 16-bit binary counter * | | `------ Mode 3: square wave rate generator * | `------------- Load LSB then MSB * |----------------- 00: Counter 0 * |----------------- 01: Counter 1 * `----------------- 10: Counter 2 * * I use square waves for all three counters because this potentially * offers more accurate timing than interrupt-on-terminal-count, which * relies on the interrupt handler to continually reset the 8253, during * which time an input pulse on pin CLK0 may have been lost. */ pit_out_timer(0x36, 3); pit_out_timer(0x76, 3); pit_out_timer(0xb6, 3); /* 'k, done. */ printk(KERN_INFO "pit_init: module loaded\n"); return 0; } /* Called on module unload. */ static void __exit pit_exit(void) { pit_cleanup(); printk(KERN_INFO "pit_exit: module unloaded\n"); } /* Called on seeking device. /dev/pit doesn't support seeking, so... */ loff_t pit_llseek(struct file* filp, loff_t off, int whence) { return -ESPIPE; } /* Called on read from device. /dev/pit, when read, yields the number of ticks * since the last read. When no ticks have occurred, read blocks. */ static int pit_read(struct file* filp, char* buf, size_t count, loff_t* ppos) { int retval; unsigned long data; DECLARE_WAITQUEUE(wait, current); /* Don't allow reading less than a single record. This little check makes * things much easier... */ if (count < sizeof(unsigned long)) return -EINVAL; /* No seeking! :^) */ if (ppos != &filp->f_pos) return -ESPIPE; /* Give the interrupt handler a means of waking us. */ add_wait_queue(&pit_read_queue, &wait); /* First time though, there may be a tick to report. If not, block * until one occurs and try again (loop). * * I'd use wait_event_interruptible(), but doing things the long way gives * me a little bit more control. * * This code is patterned after similar in linux/drivers/char/rtc.c . */ while (1) { __set_current_state(TASK_INTERRUPTIBLE); data = pit_tick_count; /* This may look goofy, but, assuming the decrement is atomic, it * protects against the case of an interrupt happening right here and * its tick being lost. */ barrier(); pit_tick_count -= data; /* Okay, got the data. */ if (data > 0) { /* Send it to userland. */ retval = put_user(data, (unsigned long*)buf); if (retval == 0) retval = sizeof(unsigned long); break; } /* Handle non-blocking reads. */ if (filp->f_flags & O_NONBLOCK) { /* No tick, but the reader doesn't want to wait. */ retval = -EAGAIN; break; } /* We get signal? */ if (signal_pending(current)) { /* Tell the vfs layer to restart us. */ retval = -ERESTARTSYS; break; } /* Sleep until the interrupt handler code wakes us. */ schedule(); } /* Cleanup time. */ __set_current_state(TASK_RUNNING); remove_wait_queue(&pit_read_queue, &wait); return retval; } /* Called on poll/select on the device. /dev/pit is considered readable when * one or more timer ticks have elapsed since the last read. */ static unsigned int pit_poll(struct file* filp, poll_table* ptbl) { /* Ensure that at least one tick has happened. */ if (pit_tick_count == 0) poll_wait(filp, &pit_read_queue, ptbl); /* Return the tick status. */ if (pit_tick_count > 0) return POLLIN | POLLRDNORM; else return 0; } /* Called on ioctl on the device. See pit.h for a list of supported ioctls. */ static int pit_ioctl(struct inode* node, struct file* filp, unsigned int cmd, unsigned long arg) { int retval = 0; switch (cmd) { case PIT_IGNORE_TICKS: /* Stop counting ticks. */ pit_ticks_enabled = 0; pit_outb(0, parport_io + 2); /* XXX: This triggers an interrupt, for some reason. */ break; case PIT_ENABLE_TICKS: /* Start listening to ticks. */ pit_ticks_enabled = 1; pit_outb(16, parport_io + 2); break; case PIT_READ_DIVISOR: /* Send the divisors to userland. */ if (copy_to_user((char*)arg, &pit_divisor, sizeof(pit_divisor)) != 0) retval = -EFAULT; break; case PIT_SET_DIVISOR: /* Get the divisors from userland. */ if (copy_from_user(&pit_divisor, (char*)arg, sizeof(pit_divisor)) != 0) retval = -EFAULT; /* Fall through. */ case PIT_RESET_TIME: /* Write the divisors to the 8253. */ pit_out_timer(pit_divisor.clk0 % 256, 0); pit_out_timer(pit_divisor.clk0 / 256, 0); pit_out_timer(pit_divisor.clk1 % 256, 1); pit_out_timer(pit_divisor.clk1 / 256, 1); pit_out_timer(pit_divisor.clk2 % 256, 2); pit_out_timer(pit_divisor.clk2 / 256, 2); break; default: /* -ENAUGHTY. :^) */ retval = -ENOTTY; } return retval; } /* Called on device open. Set some defaults and register the IRQ. */ static int pit_open(struct inode* node, struct file* filp) { int retval; /* If execution has reached this point, then grant access. */ MOD_INC_USE_COUNT; if (pit_open_count == 0) { /* Tell the parport to mask interrupts. */ pit_ticks_enabled = 0; pit_outb(0, parport_io + 2); /* Register the parport irq line. */ retval = request_irq(parport_irq, pit_interrupt, 0, "pit (parport)", pit_magic); if (retval != 0) { printk(KERN_ERR "pit_open: request_irq() returned %d\n", retval); MOD_DEC_USE_COUNT; return retval; } pit_have_parport_irq = 1; } else if (pit_interrupt_hack) { pit_outb(255, parport_io + 0); udelay(10); pit_outb(0, parport_io + 0); } ++pit_open_count; return 0; } /* Called on device close. Set the parport to mask interrupts and unregister * the IRQ. */ static int pit_release(struct inode* node, struct file* filp) { --pit_open_count; if (pit_open_count == 0) { /* Tell the parport to mask interrupts. */ pit_ticks_enabled = 0; pit_outb(0, parport_io + 2); /* Unregister the IRQ. */ if (pit_have_parport_irq) { pit_have_parport_irq = 0; free_irq(parport_irq, pit_magic); } } /* Remove this filp from the asynchronously notified filp's. */ pit_fasync(-1, filp, 0); /* And... done. */ MOD_DEC_USE_COUNT; return 0; } /* Called whenever a program enables or disables asynchronous I/O. */ static int pit_fasync(int fd, struct file* filp, int mode) { return fasync_helper(fd, filp, mode, &pit_async_queue); } /* Registers io ports. */ static int pit_reg_io(char* name, int base, int extent) { if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)) { /* Linux 2.2: Call check_region(), then request_region(). * request_region() returns void, so we have to assume nothing changes * in between the calls to check_region() and request_region(). */ if (check_region(base, extent)) goto error; } if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)) { request_region(base, extent, name); } else { /* Linux 2.4: request_region() returns a struct resource* on success * and NULL on failure. The cast to void* is to appease the compiler * when compiling for 2.2. */ if ((void*)request_region(base, extent, name) == NULL) goto error; } /* Success. */ return 0; error: /* Uh oh. */ printk(KERN_ERR "pit_reg_io: %s io ports (0x%03x-0x%03x) in use\n", name, base, base + extent - 1); return -EBUSY; } /* Unregisters everything. */ static void pit_cleanup(void) { /* Unregister /dev/timer. */ if (pit_have_misc_dev) { pit_have_misc_dev = 0; /* Can't fail? */ misc_deregister(&pit_device); } /* Unregister the parport io ports. */ if (pit_have_parport_io) { pit_have_parport_io = 0; /* While I'm at it, I might as well do this... :^) */ pit_ticks_enabled = 0; pit_outb(0, parport_io + 2); release_region(parport_io, 4); } /* Unregister the timer io ports. */ if (pit_have_timer_io) { pit_have_timer_io = 0; release_region(timer_io, 4); } /* Unregister the parport irq line. */ if (pit_have_parport_irq) { pit_have_parport_irq = 0; printk(KERN_DEBUG "pit_cleanup: free_irq(parport_irq=%d, " "\"pit\"); (shouldn't happen)\n", parport_irq); free_irq(parport_irq, "pit"); } } /* Called whenever the timer ticks. Increments the tick counter and wakes any * readers. */ static void pit_interrupt(int irq, void* dev_id, struct pt_regs* regs) { /* XXX: I need to read the status register to clear the interrupt, * which is weird. */ inb(parport_io + 1); if (!pit_ticks_enabled) { if (debug >= 1) { printk(KERN_DEBUG "pit_interrupt: received unexpected " "interrupt %d\n", irq); } } else { if (debug >= 1) { printk(KERN_DEBUG "pit_interrupt: received interrupt %d\n", irq); } /* Should I try to prevent overflow? Naah... */ ++pit_tick_count; /* Wake up any readers. */ wake_up_sync(&pit_read_queue); /* Notify any asynchronous I/O readers. */ kill_fasync(&pit_async_queue, SIGIO, POLL_IN); } } /* Writes a byte to the 8253, however it's attached. */ static void pit_out_timer(unsigned char data, unsigned int addr) { if (pit_direct) { /* Parport control register (parport_io + 2) bits: * * 0 0 0 e y x 0 w * | | | | | | | `-- (inv) --> /STROBE --> /WR * | | | | | | `---- (inv) --> /AUTOFEED --> no connection * | | | | | `------ ----- --> /INIT --> A0 * | | | | `-------- (inv) --> /SELECT-IN --> A1 * | | | `------------ Interrupt enable * | | `-------------- Direction * `-^---------------- Reserved */ /* Compensate for the inverter in-line with /SELECT-IN. */ addr = (addr & 3) ^ 2; /* First, present the data to 8253 pins D7 to D0. */ pit_outb(data, parport_io + 0); /* Next, send a momentary low to /WR. */ pit_outb((pit_ticks_enabled << 4) | (addr << 2) | 1, parport_io + 2); /* Wait a moment. */ udelay(5); /* Finally, bring /WR high again. */ pit_outb((pit_ticks_enabled << 4) | (addr << 2) | 0, parport_io + 2); } else { /* How nice. The ISA bus takes care of /WR, A0, and A1 for me. */ pit_outb(data, timer_io + addr); } } /* Writes a byte value to a hardware port. */ static void pit_outb(unsigned char val, unsigned port) { if (debug >= 1) { printk(KERN_DEBUG "pit_outb: writing byte value 0x%02x " "(%d%d%d%d %d%d%d%d) to port 0x%03x\n", (unsigned int)val, (val >> 7) & 1, (val >> 6) & 1, (val >> 5) & 1, (val >> 4) & 1, (val >> 3) & 1, (val >> 2) & 1, (val >> 1) & 1, (val >> 0) & 1, port); } outb(val, port); } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */ /* EOF */