commit f024710be92a87d10ad8c50d0784a7e1f3f4a5fe
Author: Tom Mullins <tmullins@andrew.cmu.edu>
Date:   Mon Oct 22 13:15:02 2012 -0400

    Added actual reads from hardware to encoder.ko
    
    Not yet tested on Gumstix (especially since I don't think we have the
    encoders yet).

diff --git a/scout_gumstix/encoder/encoder.c b/scout_gumstix/encoder/encoder.c
index 19ff4f1..4055b4c 100644
--- a/scout_gumstix/encoder/encoder.c
+++ b/scout_gumstix/encoder/encoder.c
@@ -5,24 +5,16 @@
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/semaphore.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
 #include <asm/uaccess.h>
 
 MODULE_AUTHOR("Tom Mullins");
 MODULE_LICENSE("GPL");
 
-/*
- * TODO
- *
- * clean up before final move to gumstix
- * add interrupt counting
- *
- */
-
-typedef long enc_val_t;
-
 #define BUF_LEN 20
 #define N_ENCODERS 4
-int enc_pins[N_ENCODERS][2] = {{78, 79}, {80, 81}, {82, 83}, {84, 85}};
+const int enc_pins[N_ENCODERS][2] = {{78, 79}, {80, 81}, {82, 83}, {84, 85}};
 struct class *enc_class = NULL;
 dev_t first_dev;
 
@@ -32,8 +24,9 @@ struct enc_t
   struct cdev cdev;
   struct semaphore sem;
   struct device *device;
+  int irq[2];
   int status;
-  enc_val_t value;
+  atomic_t value;
   dev_t dev;
 }
 encs[N_ENCODERS];
@@ -74,7 +67,7 @@ static ssize_t enc_read(struct file *file, char __user *udata, size_t count,
   {
     return -ERESTARTSYS;
   }
-  len = snprintf(enc->buf, BUF_LEN+1, "%ld\n", enc->value);
+  len = snprintf(enc->buf, BUF_LEN+1, "%d\n", atomic_read(&enc->value));
   if (*offset > len)
   {
     return 0;
@@ -94,6 +87,7 @@ static ssize_t enc_write(struct file *file, const char __user *udata,
     size_t count, loff_t *offset)
 {
   char *end;
+  int new_value;
   struct enc_t *enc = file->private_data;
   if (down_interruptible(&enc->sem))
   {
@@ -105,15 +99,38 @@ static ssize_t enc_write(struct file *file, const char __user *udata,
   }
   copy_from_user(enc->buf, udata, count);
   enc->buf[count] = '\0';
-  enc->value = simple_strtol(enc->buf, &end, 10);
+  new_value = simple_strtoi(enc->buf, &end, 10);
+  atomic_set(&enc->value, new_value);
   *offset += count;
   up(&enc->sem);
   return count;
 }
 
-static int enc_init(dev_t dev, struct enc_t *enc)
+irqreturn_t intA(int irq, void *enc, struct pt_regs *regs) {
+  int this = gpio_get_value(enc->pins[0]);
+  int other = gpio_get_value(enc->pins[1]);
+  if (this == other) {
+    atomic_inc(&enc->value);
+  } else {
+    atomic_dec(&enc->value);
+  }
+  return IRQ_HANDLED;
+}
+
+irqreturn_t intB(int irq, void *enc, struct pt_regs *regs) {
+  int this = gpio_get_value(enc->pins[1]);
+  int other = gpio_get_value(enc->pins[0]);
+  if (this == other) {
+    atomic_dec(&enc->value);
+  } else {
+    atomic_inc(&enc->value);
+  }
+  return IRQ_HANDLED;
+}
+
+static int enc_init(dev_t dev, struct enc_t *enc, int *pins)
 {
-  int err;
+  int err, i;
 
   // initialize members
   cdev_init(&enc->cdev, &fops);
@@ -131,6 +148,40 @@ static int enc_init(dev_t dev, struct enc_t *enc)
   }
   enc->status = 1;
 
+  // register gpio and interrupts
+  for (i = 0; i < 2; i++) {
+
+    enc->pins[i] = pins[i];
+
+    enc->irq[i] = gpio_to_irq(pins[i]);
+    if (enc->irq[i] < 0) {
+      printk("Error %d requesting irq number for gpio %d\n", enc->irq[i],
+          enc->pins[i]);
+      return 1;
+    }
+
+    err = gpio_direction_input(enc->pins[i]);
+    if (err < 0) {
+      printk("Error %d setting gpio %d to input\n", err, enc->pins[i]);
+      return 1;
+    }
+
+    err = request_irq(enc->irq[i], i? intB : intA, SA_INTERRUPT, "encoder", enc);
+    if (err < 0) {
+      printk("Error %d requesting irq %d\n", err, enc->irq[i]);
+      return 1;
+    }
+
+    // TODO the error checking here does not properly clean up after itself
+    // TODO perhaps we should use gpio_request? probably not necessary...
+  }
+  enc->status = 2;
+
+  return 0;
+}
+
+int enc_create_dev(struct enc_t *enc) {
+
   // make file in /dev
   enc->device = device_create(enc_class, NULL, enc->dev, NULL, "enc%d",
       MINOR(enc->dev));
@@ -141,7 +192,7 @@ static int enc_init(dev_t dev, struct enc_t *enc)
     enc_free(enc);
     return err;
   }
-  enc->status = 2;
+  enc->status = 3;
 
   return 0;
 }
@@ -150,9 +201,12 @@ static void enc_free(struct enc_t *enc)
 {
   switch (enc->status)
   {
-    case 2:
+    case 3:
       device_destroy(enc_class, enc->dev);
       enc->device = NULL;
+    case 2:
+      free_irq(enc->irq[0], enc);
+      free_irq(enc->irq[1], enc);
     case 1:
       cdev_del(&enc->cdev);
   }
@@ -186,6 +240,17 @@ static int __init enc_init_module(void)
     return 1;
   }
 
+  // initialize enc structures
+  for (i = 0; i < N_ENCODERS; i++) {
+    err = enc_init(first_dev + i, &encs[i], enc_pins[i]);
+    if (err)
+    {
+      printk(KERN_WARNING "Error %d initializing encoder %d\n", err, i);
+      enc_exit_module();
+      return 1;
+    }
+  }
+
   // register our device class for files in /dev
   enc_class = class_create(THIS_MODULE, "encoder");
   if (IS_ERR(enc_class))
@@ -196,12 +261,12 @@ static int __init enc_init_module(void)
   }
   else
   {
-    // initialize enc structures
+    // create each device
     for (i = 0; i < N_ENCODERS; i++) {
-      err = enc_init(first_dev + i, &encs[i]);
+      err = enc_create_dev(&encs[i]);
       if (err)
       {
-        printk(KERN_WARNING "Error %d adding enc%d\n", err, i);
+        printk(KERN_WARNING "Error %d creating enc%d\n", err, i);
         enc_exit_module();
         return 1;
       }
