/* SimpleIrq
   Produce a rectangle wave on output 0 of a parallel port 
   link pins 9 and 10 to cause an interrupt.

*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/cons.h>
#include <pthread.h>
#include <asm/io.h>
#include <asm/rt_irq.h>
#include <rtl.h>
#include <rtl_core.h>

#include <rtl_sync.h>  
#include <rtl_sched.h>  


#define TICK_PERIOD 500000  /* nanoseconds  = 500uSecs*/
#define LPT_PORT 0x378
#define LPT_CONT 0x37A
#define LPT_IRQ 7

int tperiod = 500000; /* nSecs */

int tas_count = 0;

int irq_count = 0;

hrtime_t t_tas;
hrtime_t t_irq;


pthread_t mythread;

unsigned int do_irq(unsigned int irq,struct pt_regs *regs)
{
  if (irq_count < 5 ) {
    printk(" we got an interrupt flag \n");
  }
  if ( irq_count == 1 ) {
      t_irq = clock_gethrtime(rtl_getschedclock());
  }

  irq_count++;
  outb(0, LPT_PORT);;        /* reset the parallel port */
  return 0;

}



void setup_irq(void) {

//  outb_p(inb_p(0x21)&(0x80),0x21);
//  outb_p(0x20,0x20); // this may screw us up  

  outb_p(inb_p(LPT_CONT)|(0x10),LPT_CONT);

  /* request an IRQ */
  rtl_request_irq(LPT_IRQ, do_irq);

  /* reset interrupt */
  outb(inb(LPT_PORT) &~(0x81),LPT_PORT);

 /* hard enable interupt */
  rtl_hard_enable_irq(LPT_IRQ);

}

void cleanup_irq(void) {
  outb_p(inb_p(LPT_CONT)&~(0x10),LPT_CONT);
  rtl_free_irq(LPT_IRQ);
}



/* periodic function to set irq bit */
void *fun(void * myt) {

  hrtime_t next;
  char t;
  t = 0x81;

  next = clock_gethrtime(rtl_getschedclock())+ 2 * tperiod ;

  pthread_make_periodic_np(pthread_self(), next,tperiod);

  do {
    pthread_wait_np();
    if ( tas_count == 1 ) {
      t_tas = clock_gethrtime(rtl_getschedclock());
    }
    tas_count++;
    outb(t, LPT_PORT);;        /* write on the parallel port */

  } while(1);

}



int init_module(void){

  int tstatus;

  pthread_attr_t attr;
  struct sched_param sched_param;
  setup_irq();
 
  pthread_attr_init (&attr);
  pthread_attr_setcpu_np(&attr, 0 );

  sched_param.sched_priority = 1;
  pthread_attr_setschedparam (&attr, &sched_param);

  tstatus = pthread_create (&mythread,  &attr, fun, 0);

  return 0;
}

void cleanup_module(void){
  int tdiff;
  pthread_delete_np(mythread);

  tdiff = (int) ( t_irq - t_tas);
  cleanup_irq();
  printk("tcount %d irq_count %d lat %d \n",tas_count, irq_count,tdiff );

  outb(0x00, LPT_PORT);        /* write on the parallel port */


}




