commit 8316337284df2c876d5280419cac544041a14c56
Author: Tom Mullins <tmullins@andrew.cmu.edu>
Date:   Thu Oct 18 14:28:51 2012 -0400

    Added kernel module for quad encoders
    
    The kernel module is not finished yet. Also, added various config files
    that go on every Gumstix. It would be nice to have an automated script
    for installing them.

diff --git a/scout_gumstix/README b/scout_gumstix/README
new file mode 100644
index 0000000..dae01d3
--- /dev/null
+++ b/scout_gumstix/README
@@ -0,0 +1,12 @@
+etc/ contains various configuration files that should go into /etc/ on the
+Gumstix. etc/hostname and etc/hosts have "scout1" in them, which should be
+replaced with a unique name for each scout.
+
+encoder/ contains the source code for a kernel module which must be built on
+the Gumstix. It provides /dev/encN with 0 <= N < 4. Count value are signed
+integers. Examples:
+$ cat /dev/enc0
+104522
+$ echo 0 > /dev/enc0
+$ cat /dev/enc0
+0
diff --git a/scout_gumstix/encoder/Makefile b/scout_gumstix/encoder/Makefile
new file mode 100644
index 0000000..298282e
--- /dev/null
+++ b/scout_gumstix/encoder/Makefile
@@ -0,0 +1,9 @@
+obj-m += encoder.o
+
+all: encoder.ko
+
+encoder.ko: encoder.c
+	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+clean:
+	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
diff --git a/scout_gumstix/encoder/encoder.c b/scout_gumstix/encoder/encoder.c
new file mode 100644
index 0000000..19ff4f1
--- /dev/null
+++ b/scout_gumstix/encoder/encoder.c
@@ -0,0 +1,213 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/semaphore.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}};
+struct class *enc_class = NULL;
+dev_t first_dev;
+
+struct enc_t
+{
+  char buf[BUF_LEN+1];
+  struct cdev cdev;
+  struct semaphore sem;
+  struct device *device;
+  int status;
+  enc_val_t value;
+  dev_t dev;
+}
+encs[N_ENCODERS];
+
+static int enc_open(struct inode *inode, struct file *file);
+static ssize_t enc_read(struct file *file, char __user *udata, size_t count,
+    loff_t *offset);
+static ssize_t enc_write(struct file *file, const char __user *udata,
+    size_t count, loff_t *offset);
+static void enc_free(struct enc_t *enc);
+static int enc_init(dev_t dev, struct enc_t *enc);
+
+struct file_operations fops =
+{
+  .owner = THIS_MODULE,
+  .open = enc_open,
+  .read = enc_read,
+  .write = enc_write
+};
+
+static int enc_open(struct inode *inode, struct file *file)
+{
+  int i = iminor(inode);
+  if (i < 0 || i >= N_ENCODERS)
+  {
+    return -ENXIO;
+  }
+  file->private_data = &encs[i];
+  return 0;
+}
+
+static ssize_t enc_read(struct file *file, char __user *udata, size_t count,
+    loff_t *offset)
+{
+  int len;
+  struct enc_t *enc = file->private_data;
+  if (down_interruptible(&enc->sem))
+  {
+    return -ERESTARTSYS;
+  }
+  len = snprintf(enc->buf, BUF_LEN+1, "%ld\n", enc->value);
+  if (*offset > len)
+  {
+    return 0;
+  }
+  len -= *offset;
+  if (len > count)
+  {
+    len = count;
+  }
+  copy_to_user(udata, enc->buf + *offset, len);
+  *offset += len;
+  up(&enc->sem);
+  return len;
+}
+
+static ssize_t enc_write(struct file *file, const char __user *udata,
+    size_t count, loff_t *offset)
+{
+  char *end;
+  struct enc_t *enc = file->private_data;
+  if (down_interruptible(&enc->sem))
+  {
+    return -ERESTARTSYS;
+  }
+  if (count > BUF_LEN)
+  {
+    count = BUF_LEN;
+  }
+  copy_from_user(enc->buf, udata, count);
+  enc->buf[count] = '\0';
+  enc->value = simple_strtol(enc->buf, &end, 10);
+  *offset += count;
+  up(&enc->sem);
+  return count;
+}
+
+static int enc_init(dev_t dev, struct enc_t *enc)
+{
+  int err;
+
+  // initialize members
+  cdev_init(&enc->cdev, &fops);
+  enc->cdev.owner = THIS_MODULE;
+  enc->dev = dev;
+  enc->status = 0;
+  sema_init(&enc->sem, 1);
+
+  // make cdev file operations accessible
+  err = cdev_add(&enc->cdev, enc->dev, N_ENCODERS);
+  if (err)
+  {
+    enc_free(enc);
+    return err;
+  }
+  enc->status = 1;
+
+  // make file in /dev
+  enc->device = device_create(enc_class, NULL, enc->dev, NULL, "enc%d",
+      MINOR(enc->dev));
+  if (IS_ERR(enc->device))
+  {
+    err = PTR_ERR(enc->device);
+    enc->device = NULL;
+    enc_free(enc);
+    return err;
+  }
+  enc->status = 2;
+
+  return 0;
+}
+
+static void enc_free(struct enc_t *enc)
+{
+  switch (enc->status)
+  {
+    case 2:
+      device_destroy(enc_class, enc->dev);
+      enc->device = NULL;
+    case 1:
+      cdev_del(&enc->cdev);
+  }
+  enc->status = 0;
+}
+
+static void enc_exit_module(void)
+{
+  int i;
+  for (i = 0; i < N_ENCODERS; i++)
+  {
+    enc_free(&encs[i]);
+  }
+  if (enc_class) {
+    class_destroy(enc_class);
+    enc_class = NULL;
+  }
+  unregister_chrdev_region(first_dev, N_ENCODERS);
+}
+module_exit(enc_exit_module);
+
+static int __init enc_init_module(void)
+{
+  int i, err;
+
+  // allocate character devices and get major number
+  err = alloc_chrdev_region(&first_dev, 0, N_ENCODERS, "encoder");
+  if (err)
+  {
+    printk(KERN_ALERT "Error %d in alloc_chrdev_region\n", err);
+    return 1;
+  }
+
+  // register our device class for files in /dev
+  enc_class = class_create(THIS_MODULE, "encoder");
+  if (IS_ERR(enc_class))
+  {
+    err = PTR_ERR(enc_class);
+    enc_class = NULL;
+    printk(KERN_WARNING "Error %d creating device class\n", err);
+  }
+  else
+  {
+    // initialize enc structures
+    for (i = 0; i < N_ENCODERS; i++) {
+      err = enc_init(first_dev + i, &encs[i]);
+      if (err)
+      {
+        printk(KERN_WARNING "Error %d adding enc%d\n", err, i);
+        enc_exit_module();
+        return 1;
+      }
+    }
+  }
+
+  return 0;
+}
+module_init(enc_init_module);
diff --git a/scout_gumstix/etc/fstab b/scout_gumstix/etc/fstab
new file mode 100644
index 0000000..dc50894
--- /dev/null
+++ b/scout_gumstix/etc/fstab
@@ -0,0 +1,2 @@
+proc /proc proc nodev,noexec,nosuid 0 0
+/dev/mmcblk0p2 / ext3 errors=remount-ro,user_xattr,noatime,nodiratime,commit=120 0 1
diff --git a/scout_gumstix/etc/hostname b/scout_gumstix/etc/hostname
new file mode 100644
index 0000000..501320f
--- /dev/null
+++ b/scout_gumstix/etc/hostname
@@ -0,0 +1 @@
+scout1
diff --git a/scout_gumstix/etc/hosts b/scout_gumstix/etc/hosts
new file mode 100644
index 0000000..7b7dfe2
--- /dev/null
+++ b/scout_gumstix/etc/hosts
@@ -0,0 +1,8 @@
+127.0.0.1	localhost
+127.0.1.1	scout1
+::1		localhost ip6-localhost ip6-loopback
+fe00::0		ip6-localnet
+ff00::0		ip6-mcastprefix
+ff02::1		ip6-allnodes
+ff02::2		ip6-allrouters
+
diff --git a/scout_gumstix/etc/init/ttyO2.conf b/scout_gumstix/etc/init/ttyO2.conf
new file mode 100644
index 0000000..3e351f8
--- /dev/null
+++ b/scout_gumstix/etc/init/ttyO2.conf
@@ -0,0 +1,9 @@
+# This service maintains a getty on ttyO2 from the point the system is
+# started until it is shut down again.
+
+start on stopped rc RUNLEVEL=[12345]
+
+stop on runlevel [!12345]
+
+respawn
+exec /sbin/getty -8 115200 ttyO2
diff --git a/scout_gumstix/etc/network/interfaces b/scout_gumstix/etc/network/interfaces
new file mode 100644
index 0000000..d66c524
--- /dev/null
+++ b/scout_gumstix/etc/network/interfaces
@@ -0,0 +1,5 @@
+# interfaces(5) file used by ifup(8) and ifdown(8)
+auto lo
+iface lo inet loopback
+auto usb0
+iface usb0 inet dhcp
diff --git a/scout_gumstix/etc/rc.local b/scout_gumstix/etc/rc.local
new file mode 100644
index 0000000..850cf2e
--- /dev/null
+++ b/scout_gumstix/etc/rc.local
@@ -0,0 +1,30 @@
+#!/bin/sh -e
+#
+# rc.local
+#
+# This script is executed at the end of each multiuser runlevel.
+# Make sure that the script will "exit 0" on success or any other
+# value on error.
+#
+# In order to enable or disable this script just change the execution
+# bits.
+#
+# By default this script does nothing.
+
+export_as_output() {
+  last_dir="$PWD"
+  cd /sys/class/gpio
+  echo "$1" > export
+  cd "gpio$1"
+  echo out > direction
+  chgrp gpio value
+  chown g+w value # TODO necessary?
+  cd "$last_dir"
+}
+
+# export motor driver gpio pins
+for i in 70 71 72 73 74 75 76 77; do
+  export_as_output $i
+done
+
+exit 0
diff --git a/scout_gumstix/etc/udev/rules.d/40-encoder.rules b/scout_gumstix/etc/udev/rules.d/40-encoder.rules
new file mode 100644
index 0000000..55c5279
--- /dev/null
+++ b/scout_gumstix/etc/udev/rules.d/40-encoder.rules
@@ -0,0 +1 @@
+SUBSYSTEM=="encoder", GROUP="gpio"
