/*********************************************************************** ** parint uses the Parallel Port interrupt and record a timestamp. ** -------------------------------------------------------------------- ** Written by: ** Tony Denault ** ** Institute for Astronomy, University of Hawaii ** 2680 Woodlawn Drive, Honolulu, HI 96822 ** http://www.irtfweb.ifa.hawaii.edu ************************************************************************ */ #ifndef __KERNEL__ # define __KERNEL__ #endif /*------------------------------ ** include files **------------------------------ */ //#include #include //#include #include #include /* printk() */ #include /* everything... */ #include /* error codes */ #include /* size_t */ #include #include #include #include #include #include #include #include #include #include #include #include /* copy from/to user */ #include #include "parint.h" /*------------------------------ ** defines & declare structure **------------------------------ */ MODULE_LICENSE("GPL"); #define DEBUG 0 #define PARINT_VERSION "11.05" // Version is YY.MM #define PARINT_MAJOR 67 // major device number #define BASEPORT 0x378 // address of lp0 #define NUM_PORTS 3 // we use 2 port address #define NUM_TV 250 // storage for timestamps. #define MAX( a, b ) ((a) < (b) ? (b) : (a)) #define MIN( a, b ) ((a) < (b) ? (a) : (b)) struct stuff_t { wait_queue_head_t waitq; // wait queue for select. struct parport * pp; // reference to parport struct pardevice * pdev; // reference to device on parport unsigned long io_base; // port address start/end unsigned long io_base_end; //int irq; // PARINT's IRQ value. unsigned int i_cnt; // number of time the ISR was called. unsigned long devid; // used for request_irq()/free_irq() int num_procs; // number of processes with device open // circular buffer to hold time data, tv[]. use tv_lock to modify; // Use head/tail manage buffer. spinlock_t tv_lock; // lock for tv_data. volatile struct timeval *tv_head; // too keep track of the data in tv[]; volatile struct timeval *tv_tail; struct timeval tv[NUM_TV]; // time val data buffer. struct timeval oneshot_tv; // oneshot time value. int oneshot_has_data; // does one shot have data. }; /*----------------------------- ** prototype functions **----------------------------- */ static void parint_handler( int irq, void * dev_id, struct pt_regs * regs); int init_module(void); void cleanup_module (void); static ssize_t parint_read(struct file * file, char __user *buffer, size_t count, loff_t *offp); static ssize_t parint_write(struct file * file, const char __user *buffer, size_t count, loff_t *offp); static unsigned int parint_poll(struct file * file, poll_table * wait ); static int parint_open(struct inode *inode, struct file *filp); static int parint_release(struct inode *inode, struct file *filp); static int parint_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); static int ioctl_get_time( unsigned int * i_cnt, int * num_ret, struct timeval * tv, int max_tv, struct stuff_t *s); static int ioctl_flush( struct stuff_t *s ); static int ioctl_pulse( struct timeval * tv, struct stuff_t *s ); static int ioctl_oneshot_clear( struct stuff_t *s); static int ioctl_oneshot_get( int * u_has_data, struct timeval * u_tv, struct stuff_t *s); static int ioctl_t1( struct parint_parms_t *p, struct stuff_t *s ); static int ioctl_t2( struct parint_parms_t *p, struct stuff_t *s ); static int ms2jiffies( int ms ); /*----------------------------- ** global variable **----------------------------- */ struct file_operations Pmac_fops = { .read = parint_read, .write = parint_write, .open = parint_open, .poll = parint_poll, .ioctl = parint_ioctl, .release = parint_release }; static struct stuff_t Stuff; // hold internal driver info /*--------------------------------------------------------------------------------------------- ** incr_tv() - increment the tv_head/tail circular buffer pointer in a way that nobody sees ** an intermediate value. */ static inline void incr_tv(volatile struct timeval **tvp, struct timeval *tv) { if (*tvp == (tv + NUM_TV - 1)) *tvp = tv; /* Wrap */ else (*tvp)++; } /*----------------------------------------------------------------- ** parint_handler() - ISR for parint. Timestamps the interrupts. **----------------------------------------------------------------- */ static void parint_handler( int irq, void * dev_id, struct pt_regs * regs) { // add new data to Stuff... spin_lock( &Stuff.tv_lock); do_gettimeofday( (struct timeval *)Stuff.tv_head ); // get new data if( !Stuff.oneshot_has_data ) // copy new data to one shot { Stuff.oneshot_tv.tv_sec = Stuff.tv_head->tv_sec; Stuff.oneshot_tv.tv_usec = Stuff.tv_head->tv_usec; Stuff.oneshot_has_data = 1; /*TRUE*/ } incr_tv( &Stuff.tv_head, Stuff.tv ); // increment pointer if( Stuff.tv_tail == Stuff.tv_head ) incr_tv( &Stuff.tv_tail, Stuff.tv ); // too much data, advance tail to make room spin_unlock( &Stuff.tv_lock); Stuff.i_cnt++; // number of interrpts serviced by parint. wake_up_interruptible( &Stuff.waitq ); // New data, wait up any processes if( DEBUG ) { int n; spin_lock( &Stuff.tv_lock); if( Stuff.tv_head < Stuff.tv_tail ) n = NUM_TV - (Stuff.tv_head-Stuff.tv_tail); else n = (Stuff.tv_head-Stuff.tv_tail); spin_unlock( &Stuff.tv_lock); printk("ISR icnt=%u cnt=%d h%02d t%02d\n", Stuff.i_cnt, n, Stuff.tv_head-Stuff.tv, Stuff.tv_tail-Stuff.tv); } return; } /*----------------------------- ** parint_init() **----------------------------- */ static int __init parint_init(void) { /* initialize Stuff - internal driver data structure */ memset( &Stuff, 0, sizeof(Stuff) ); // clear device data structure Stuff.tv_lock = SPIN_LOCK_UNLOCKED; // a lock for Stuff. Stuff.tv_head = Stuff.tv; Stuff.tv_tail = Stuff.tv; init_waitqueue_head(&Stuff.waitq); // initialize the wait queue Stuff.pp = NULL; Stuff.pdev = NULL; /*------------------------------ ** get access to parport */ // get reference to port Stuff.pp = parport_find_base( BASEPORT ); if( !Stuff.pp ) { printk( KERN_ERR "parint: parport_find_base FAILED!\n"); goto lerr; } // register device and handlers Stuff.pdev = parport_register_device( Stuff.pp, "parint", NULL, NULL, parint_handler, PARPORT_DEV_EXCL, &Stuff); if( !Stuff.pdev ) { printk( KERN_ERR "parint: parport_register_device FAILED!\n"); goto lerr; } // now claim the port if( parport_claim( Stuff.pdev ) ) { printk( KERN_ERR "parint: parport_claim() FAILED!\n"); goto lerr; } // enable interrupts. parport_enable_irq( Stuff.pp ); // register the char device if( register_chrdev( PARINT_MAJOR, "parint", &Pmac_fops) ) { printk( KERN_ERR "parint: register_chrdev() FAILED!\n"); goto lerr; } printk( KERN_INFO "parint: Parallel Port Interrupt Driver %s. HZ=%d\n", PARINT_VERSION, HZ); return 0; lerr: if( Stuff.pdev ) { parport_release( Stuff.pdev ); parport_unregister_device( Stuff.pdev ); } return -ENODEV; } module_init(parint_init); /*----------------------------- ** parint_cleanup() **----------------------------- */ static void __exit parint_cleanup(void) { printk( KERN_INFO "parint: cleanup_module()\n"); // parport parport_enable_irq( Stuff.pp ); parport_release( Stuff.pdev ); parport_unregister_device( Stuff.pdev ); // unregister char device unregister_chrdev (PARINT_MAJOR, "parint"); } module_exit(parint_cleanup); /*----------------------------- ** parint_read() **----------------------------- */ static ssize_t parint_read(struct file * file, char __user *buffer, size_t count, loff_t *offp) { /* it invalid to read from driver */ return -EINVAL; } /*----------------------------- ** parint_write() **----------------------------- */ static ssize_t parint_write(struct file * file, const char __user *buffer, size_t count, loff_t *offp) { /* it invalid to write to his driver */ return -EINVAL; } /*----------------------------- ** parint_open() **----------------------------- */ static int parint_open(struct inode *inode, struct file *filp) { Stuff.num_procs++; if( DEBUG ) printk( KERN_INFO "parint: parint_open()\n"); return 0; } /*----------------------------- ** parint_poll() **----------------------------- */ static unsigned int parint_poll(struct file * file, poll_table * wait ) { int n; poll_wait( file, &Stuff.waitq, wait); // Any data to read? spin_lock( &Stuff.tv_lock); if( Stuff.tv_head < Stuff.tv_tail ) n = NUM_TV - (Stuff.tv_head-Stuff.tv_tail); else n = (Stuff.tv_head-Stuff.tv_tail); spin_unlock( &Stuff.tv_lock); if( n ) return POLLIN|POLLRDNORM; return 0; } /*----------------------------- ** parint_release() **----------------------------- */ static int parint_release(struct inode *inode, struct file *filp) { Stuff.num_procs--; if( DEBUG ) printk( KERN_INFO "parint: parint_release()\n"); return 0; } /*----------------------------- ** parint_ioctl() **----------------------------- */ static int parint_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct parint_parms_t p; // local copy of 'arg' copy_from_user( &p, (void*)arg, sizeof(p)); //printk( KERN_INFO "parint_ioctl: cmd=%d.\n", cmd); switch( cmd ) { case PARINT_IOCTL_GET_TIME: { p.ret = ioctl_get_time( p.p[0], p.p[1], p.p[2], p.l[0], &Stuff ); break; } case PARINT_IOCTL_FLUSH: { p.ret = ioctl_flush( &Stuff ); break; } case PARINT_IOCTL_PULSE: { p.ret = ioctl_pulse( p.p[0], &Stuff ); break; } case PARINT_IOCTL_ONESHOT_CLEAR: { p.ret = ioctl_oneshot_clear( &Stuff ); break; } case PARINT_IOCTL_ONESHOT_GET: { p.ret = ioctl_oneshot_get( p.p[0], p.p[1], &Stuff ); break; } case PARINT_IOCTL_T1: { p.ret = ioctl_t1( &p, &Stuff ); break; } case PARINT_IOCTL_T2: { p.ret = ioctl_t2( &p, &Stuff ); break; } default: p.ret = -EFAULT; if(DEBUG) printk( KERN_INFO "parint: received unknown command (%d)\n",cmd ); break; } copy_to_user( (void*)arg, &p, sizeof(p)); return 0; } /*******************************************************************/ /** PARINT ioctl functions **/ /*******************************************************************/ /*------------------------------------------------------------------------- ** ioctl_get_time() - get some timestamp data. **------------------------------------------------------------------------- */ static int ioctl_get_time( unsigned int * i_cnt, // O: return i_cnt to caller. user space address. int * num_ret, // O: number of tv copied to the user. user space address. struct timeval * tv, // O: where to copy the tv data. user space address. int max_tv, // I: number of tv[] provide by the user. struct stuff_t *s // I: driver data ) { int i, n; struct timeval * tv_copy; // alocate space to so we can copy data to transfer to the user tv_copy = kmalloc( NUM_TV*sizeof(struct timeval), GFP_KERNEL); if( !tv_copy ) { put_user( 0, num_ret ); put_user( 0, i_cnt ); return -ENOMEM; } /* transfer data from tv[] to tv_copy. This remove data from tv[] */ n = 0; spin_lock( &s->tv_lock); while( (s->tv_head != s->tv_tail) && ( n < max_tv) ) { tv_copy[n].tv_sec = s->tv_tail->tv_sec; tv_copy[n].tv_usec = s->tv_tail->tv_usec; n++; //printk(" GT: %d %d \n", s->tv_head-s->.tv, s->tv_tail-s->tv); incr_tv( &s->tv_tail, s->tv ); // increment pointer } spin_unlock( &s->tv_lock); /* transfer to user space */ for( i=0; ii_cnt, i_cnt ); kfree( tv_copy ); return 0; } /*------------------------------------------------------------------------- ** ioctl_flush() - deletes any drive tv[] data. **------------------------------------------------------------------------- */ static int ioctl_flush( struct stuff_t *s // I: driver data ) { spin_lock( &s->tv_lock); s->tv_head = s->tv_tail = s->tv; spin_unlock( &s->tv_lock); return 0; } /*------------------------------------------------------------------------- ** ioctl_pulse() - parint pulse iocntl () - this is a test/debug function ** we can test the driver by connecting a data pin to ACK (pin 10). **------------------------------------------------------------------------- */ static int ioctl_pulse( struct timeval * tv, // retuns timestamp struct stuff_t *s // driver data ) { do_gettimeofday( tv ); parport_write_data( s->pp, 0xff); /* All ON */ parport_write_data( s->pp, 0xff); /* All ON */ parport_write_data( s->pp, 0xff); /* All ON */ parport_write_data( s->pp, 0x01); /* leave D0 on */ return 0; } /*------------------------------------------------------------------------- ** ioctl_oneshot_clear() - clear (and enables) the one shot feature. ** That mean the next interrupt will be saved in the oneshot variable. **------------------------------------------------------------------------- */ static int ioctl_oneshot_clear( struct stuff_t *s // driver data ) { spin_lock( &s->tv_lock); s->oneshot_has_data = 0; /* false */ s->oneshot_tv.tv_sec = 0; /* initialize time to 0 */ s->oneshot_tv.tv_usec = 0; spin_unlock( &s->tv_lock); return 0; } /*------------------------------------------------------------------------- ** ioctl_oneshot_get() - copies one shot data to user space. **------------------------------------------------------------------------- */ static int ioctl_oneshot_get( int * u_has_data, // O: returns 'has_data' flag. user space address. struct timeval * u_tv, // O: returns oneshot time. user space address. struct stuff_t *s // I: driver data ) { int has_data; struct timeval tv; // get the one shot data. spin_lock( &s->tv_lock); has_data = s->oneshot_has_data; tv.tv_sec = s->oneshot_tv.tv_sec; tv.tv_usec = s->oneshot_tv.tv_usec; spin_unlock( &s->tv_lock); // copy to user space put_user( has_data, u_has_data); put_user( tv.tv_sec, &u_tv->tv_sec); put_user( tv.tv_usec, &u_tv->tv_usec); return 0; } /*------------------------------------------------------------------------- ** ioctl_t1() - parint test1 iocntl () - call interrupt handler via ioctl ** for debugging only **------------------------------------------------------------------------- */ static int ioctl_t1( struct parint_parms_t *p, // copy of 'arg' struct stuff_t *s // driver data ) { // call irq handler parint_handler( 7, NULL, NULL ); return 0; } /*------------------------------------------------------------------------- ** ioctl_t2() - parint test2 iocntl () - copy BASEPORT 0..2 to userland. ** for debugging only **------------------------------------------------------------------------- */ static int ioctl_t2( struct parint_parms_t *p, // copy of 'arg' struct stuff_t *s // driver data ) { unsigned char data; unsigned char status; unsigned char control; unsigned char *uc_p = p->p[0];; data = parport_read_data(s->pp); status = parport_read_status(s->pp); control = parport_read_control(s->pp); put_user( data, uc_p ); put_user( status, uc_p+1 ); put_user( control, uc_p+2 ); return 0; } /*------------------------------------------------------------------------- ** ms2jiffies() - returns number of jiffies to insure ** ms milliseconds has elapsed. Minimum is 1. **------------------------------------------------------------------------- */ static int ms2jiffies( int ms ) { int j; j = (ms * HZ) / 1000; // calculate number of jiffies if( j < 1 ) j = 1; // minimum is 1. return j; } /***********************************************************************************/ /** End Of File **/ /***********************************************************************************/