
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/delay.h>

#include <asm/io.h>
#include <asm/uaccess.h>

#define DEFAULT_MP_BASE	0x240
#define DEFAULT_MP_IRQ 11	
#define MP_MASK 0x04
#define MP_BIT_LEN 11
#define MP_WORD_LEN 3
unsigned int mpwordbuf[MP_WORD_LEN];
#define BIT_RESTART 0xFF

static short mp_in_use;
static char mp_name[10] = "mp";

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  static wait_queue_head_t mp_irq_waitQ;
#else
  static struct wait_queue* mp_irq_waitQ;
#endif

static int mp_major = 0;
static int mp_irq = 0;
static int mp_base  = 0; 

int startTrack;
static int bitpos = 0;
static int wordpos = 0;
static int parity = 0;
static unsigned char bits = 0;

#define BUFSIZE 10000
static unsigned char bitbuf[BUFSIZE];
static unsigned char charbuf[BUFSIZE];
int charcount;
volatile int head;
volatile int tail;
void processbit(unsigned long);
void processword(void);
int trackReady;
DECLARE_TASKLET(mp_tasklet, processbit, 0);

MODULE_PARM(mp_base,"i");
MODULE_PARM(mp_irq, "i");

/*
** Name
**	mp_handler  --  Interrupt handler for track 1
**
** Synopsis
**	DEFAULT_HLP_IRQ1
**
** Description
**	This is the interrupt handler for the interrupts received
**	from track one of the card reader.  This function reads the
**	data bit from the card reader and saves it in it bit buffer
**	'bitbuf1'.
**
*/
void mp_handler(int irq, void *dev_id, struct pt_regs *regs) {
  static unsigned long jif = 0;
  //printk("interrupt service routine woke up\n");

  /*
  ** Read and save the bit in the circular bit buffer
  ** then increment the head to point to the next available
  ** location.
  */
// JJ
  if (head != (tail+BUFSIZE-2)%BUFSIZE)
  {
    if (jiffies - jif > 1)
    {
printk("timed out. ");
      bitbuf[head] = BIT_RESTART;
      head = (head + 1) % BUFSIZE;
    }

    bitbuf[head] = (((inb(mp_base))) & MP_MASK)>>2;
    head = (head + 1) % BUFSIZE;
  }
  /*
  ** Don't process the data bits here, simply schedule a
  ** 'bottom handler' to read the bits from the circular
  ** bit buffer and perform the necessary processing
  */
  tasklet_schedule(&mp_tasklet);
  jif = jiffies;
}

void dumpBin(int b)
{
  int i=0;
  for (i=0; i<MP_BIT_LEN; i++)
  {
    printk("%1d", (b & 1<<(MP_BIT_LEN-1)) >> (MP_BIT_LEN-1));
    b = b<<1;
  }
  printk(" ");
}

int rev(int i)
{
  int rc = 0, j;
  for (j=0; j<MP_BIT_LEN; j++)
  {
    rc = (rc<<1) | (i&0x01);
    i = i>>1;
  }
  return rc;
}

void processword()
{
  int status, dx, dy;

  status = (rev(mpwordbuf[0]) & (0xFF<<1)) >> 1;
  dumpBin(rev(mpwordbuf[0]));
  dumpBin(rev(mpwordbuf[1]));
  dumpBin(rev(mpwordbuf[2]));

  dx = (rev(mpwordbuf[1]) & (0xFF<<1)) >> 1;
//  dumpBin(dx);
  if (dx & 0x80)
  {
    if (status & 0x10)
      dx = - (((~dx) & 0xFF) +1);
    else
      dx = 0;
  }
  else
  {
    if (status & 0x10)
      dx = 0;
  }

  dy = (rev(mpwordbuf[2]) & (0xFF<<1)) >> 1;
//  dumpBin(dy);
  if (dy & 0x80)
  {
    if (status & 0x20)
      dy = - (((~dy) & 0xFF) +1);
    else
      dy = 0;
  }
  else
  {
    if (status & 0x20)
      dy = 0;
  }

printk("dx=%d dy=%d   ", dx, dy);
printk("\n");
}

/*
** Name
**	processbit
**
** Synopsis
**
** Description
**	This is the tasklet that processes bits from track 1 of the
**	card reader.
*/
void processbit(unsigned long data)
{
  while (tail != head)
  {
    if (bitbuf[tail] == BIT_RESTART)
    {
      processword();
      bitpos = 0;
      bits = 0;
      parity = 0;
      wordpos = 0;
      mpwordbuf[0] = mpwordbuf[1] = mpwordbuf[2] = 0;
      tail = (tail + 1) % BUFSIZE;
      continue;
    }

//    bits = (bits >> 1) | ((bitbuf[tail] & 1) << 6);
    bits = bits <<  1 | (bitbuf[tail] & 1);
    bitpos++;
//printk("%1d", bitbuf[tail] & 1);
if (bitpos == 11)
//  printk(" ");
    parity += bitbuf[tail] & 1;

    if (bitpos == MP_BIT_LEN)
    {
//printk("Found a word!\n");
//      charbuf[charcount++] = [bits1&0x3FF];
      mpwordbuf[wordpos++] = bits&0x3FF;
      bits = 0;
      bitpos = 0;
      parity = 0;

      if (wordpos == MP_WORD_LEN)
      {
        processword();
        wordpos = 0;
        mpwordbuf[0] = mpwordbuf[1] = mpwordbuf[2] = 0;
      }
    }

    tail = (tail + 1) % BUFSIZE;
  }
}

static ssize_t mp_read (
  struct file *filp,
  char *buf,        /* The buffer to fill with data */
  size_t count,     /* The length of the buffer */
  loff_t *offset){  /* Our offset in the file */    

/*
  int track1Length = 0;
  int track2Length = 0;
  unsigned char *kbuf;

  if(!track1Ready && !track2Ready)
    interruptible_sleep_on(&hlp_irq_waitQ);

//printk("\n");
// JJ
  if(track1Ready) {
    track1Ready = 0;
    kbuf = (unsigned char *) kmalloc(strlen(TRACK1_PROMPT)+charcount1+2, GFP_KERNEL);
    strcpy(kbuf, TRACK1_PROMPT);
    strcat(kbuf, charbuf1);
    strcat(kbuf, "\n");
    track1Length = strlen(kbuf);
    copy_to_user(buf, kbuf, track1Length);
    kfree(kbuf);
  }

  if (track2Ready) {
    track2Ready = 0;
    kbuf = (unsigned char *) kmalloc(strlen(TRACK2_PROMPT)+charcount2+2, GFP_KERNEL);
    strcpy(kbuf, TRACK2_PROMPT);
    strcat(kbuf, charbuf2);
    strcat(kbuf, "\n");
    track2Length = strlen(kbuf);
    copy_to_user(buf+track1Length, kbuf, track2Length);
    kfree(kbuf);
  }
  return (track1Length+track2Length);
*/
  return 0;
}


static int mp_open(struct inode *inode, struct file *filp){

    if(mp_in_use)
      return -EBUSY;
    mp_in_use = 1;

    MOD_INC_USE_COUNT;
    return 0;
}                                     


static int mp_release(struct inode *inode, struct file *filp){

   mp_in_use = 0;
   MOD_DEC_USE_COUNT;
   return 0;
 }                                                       

struct file_operations mp_fops = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
  NULL,
#endif
  NULL,
//  mp_read,
NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  mp_open,
  NULL,
  mp_release
};

int init_module(void) {

  int result;

  printk(KERN_INFO "Starting doing stuff\n");

  head = tail = 0;
  charcount = 0;
  startTrack = 0;
  trackReady = 0;
  bitpos = 0;
  parity = 0;
  bits = 0;

    if(!mp_irq)
      mp_irq = DEFAULT_MP_IRQ;
    if(!mp_base)
      mp_base = DEFAULT_MP_BASE;

    // we want to keep 378 free and use only 379 and 37A so that spikes have 378
    result = check_region(mp_base, 8);

    if (result) {
        printk(KERN_INFO "%s: can't get I/O address 0x%x\n", mp_name,mp_base);
        return result;
    }
    request_region(mp_base, 8, mp_name); 

    result = register_chrdev(mp_major, mp_name, &mp_fops);
    if (result < 0){
      printk(KERN_INFO "%s: can't get major number\n", mp_name);
      release_region(mp_base, 8);
      return result;
    }
     
   if (!mp_major)
     mp_major = result;

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
    init_waitqueue_head(&mp_irq_waitQ);
#endif

   result = request_irq(mp_irq, mp_handler,  SA_INTERRUPT, mp_name, NULL);
   if (result) {
     printk(KERN_INFO "%s: failed to install irq %i handler\n",mp_name,mp_irq);
     unregister_chrdev(mp_major, mp_name);
     release_region(mp_base,1);
     return result;
   }


    printk(KERN_INFO "Done doing stuff\n");
    return 0;

}

void cleanup_module(void) {

    unregister_chrdev(mp_major, mp_name);
    release_region(mp_base,8);

    free_irq(mp_irq, NULL);
}

