Subversion Repositories Projects

Compare Revisions

Ignore whitespace Rev 246 → Rev 247

/linux/oe/user.collection/recipes/gpio-event-module/gpio-event-module.bb
0,0 → 1,37
DESCRIPTION = "Linux Driver for Detecting GPIO events"
SECTION = "base"
PRIORITY = "optional"
LICENSE = "GPL"
RDEPENDS = "kernel (${KERNEL_VERSION})"
DEPENDS = "virtual/kernel"
 
PV="svn${SRCDATE}"
PR = "r2"
 
INITSCRIPT_NAME = "gpio-event"
INITSCRIPT_PARAMS = "defaults 40"
 
SVN_REPO_URI = "http://svn.hylands.org/linux/gpio-event/module"
SRC_URI = "svn://svn.hylands.org/linux/gpio-event/;module=module;proto=http"
 
S = "${WORKDIR}/module"
 
inherit module update-rc.d
 
do_stage () {
install -m 0644 ${S}/gpio-event-drv.h ${STAGING_INCDIR}/
}
 
do_install() {
install -d ${D}${base_libdir}/modules/${KERNEL_VERSION}/extra/
install -m 0644 ${S}/gpio-event-drv.ko ${D}${base_libdir}/modules/${KERNEL_VERSION}/extra/
 
install -d ${D}${sysconfdir}/init.d/
install -m 0755 ${S}/gpio-event.init ${D}${sysconfdir}/init.d/gpio-event
}
 
PACKAGES = "${PN}"
 
FILES_${PN} = "${base_libdir}/modules/"
FILES_${PN} += "${sysconfdir}/"
 
/linux/oe/user.collection/recipes/gpio-module/gpio-module.bb
0,0 → 1,37
DESCRIPTION = "Linux Driver for manipulating GPIO pins"
SECTION = "base"
PRIORITY = "optional"
LICENSE = "GPL"
RDEPENDS = "kernel (${KERNEL_VERSION})"
DEPENDS = "virtual/kernel"
 
PV="svn${SRCDATE}"
PR = "r0"
 
INITSCRIPT_NAME = "gpio"
INITSCRIPT_PARAMS = "defaults 40"
 
SVN_REPO_URI = "http://svn.hylands.org/linux/gpio/module"
SRC_URI = "svn://svn.hylands.org/linux/gpio/;module=module;proto=http"
 
S = "${WORKDIR}/module"
 
inherit module update-rc.d
 
do_stage () {
install -m 0644 ${S}/user-gpio-drv.h ${STAGING_INCDIR}/
}
 
do_install() {
install -d ${D}${base_libdir}/modules/${KERNEL_VERSION}/extra/
install -m 0644 ${S}/user-gpio-drv.ko ${D}${base_libdir}/modules/${KERNEL_VERSION}/extra/
 
install -d ${D}${sysconfdir}/init.d/
install -m 0755 ${S}/gpio.init ${D}${sysconfdir}/init.d/gpio
}
 
PACKAGES = "${PN}"
 
FILES_${PN} = "${base_libdir}/modules/"
FILES_${PN} += "${sysconfdir}/"
 
/linux/oe/user.collection/recipes/hellomake/hellomake_1.0.0.bb
0,0 → 1,20
DESCRIPTION = "hello world sample program"
 
PR = "r0"
 
DEPENDS = ""
 
SRC_URI = " \
file://hello.c \
file://Makefile \
"
 
S = "${WORKDIR}"
 
do_install () {
install -d ${D}${bindir}/
install -m 0755 ${S}/hello ${D}${bindir}/
}
 
FILES_${PN} = "${bindir}/hello"
 
/linux/oe/user.collection/recipes/hellomake/files/hello.c
0,0 → 1,10
#include <stdio.h>
 
int main( int argc, char **argv )
{
(void)argc;
(void)argv;
 
printf( "Hello World (built with make)\n" );
return 0;
}
/linux/oe/user.collection/recipes/hellomake/files/Makefile
0,0 → 1,3
hello: hello.c
${CC} ${CFLAGS} ${LDFLAGS} -o hello hello.c
 
/linux/oe/user.collection/recipes/gpio-lib/gpio-lib.bb
0,0 → 1,30
DESCRIPTION = "User Lib for manipulating GPIO pins"
SECTION = "base"
PRIORITY = "optional"
LICENSE = "GPL"
DEPENDS = "gpio-module"
 
PV="svn${SRCDATE}"
PR = "r0"
 
SVN_REPO_URI = "http://svn.hylands.org/linux/gpio/lib"
SRC_URI = "svn://svn.hylands.org/linux/gpio/;module=lib;proto=http"
 
S = "${WORKDIR}/lib"
 
TARGET_CC_ARCH += "${LDFLAGS}"
 
do_stage () {
oe_libinstall -so -a libgpio ${STAGING_LIBDIR}
install -m 0644 ${S}/user-gpio.h ${STAGING_INCDIR}/
}
 
do_install() {
install -d ${D}${libdir}/
install -m 0755 ${S}/libgpio.so ${D}${libdir}/
}
 
PACKAGES = "${PN}"
 
FILES_${PN} = "${libdir}/libgpio.so"
 
/linux/oe/user.collection/recipes/gpio-event-module-org/files/gpio-event.init
0,0 → 1,38
#!/bin/sh
#
# Start the gpio-event driver
#
 
start() {
echo "Starting gpio-event..."
 
# load driver
/sbin/modprobe gpio-event
}
stop() {
echo "Stopping gpio-event..."
 
/sbin/modprobe -r gpio-event
}
restart() {
stop
start
}
 
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo $"Usage: $0 {start|stop|restart}"
exit 1
esac
 
exit $?
 
/linux/oe/user.collection/recipes/gpio-event-module-org/files/gpio-event.h
0,0 → 1,115
/****************************************************************************
*
* Copyright (c) 2006 Dave Hylands <dhylands@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*
****************************************************************************
*
* This driver allows multiple GPIO pins to be monitored and allows a user
* mode program to be notified when the pin changes.
*
****************************************************************************/
 
#if !defined( GPIO_EVENT_DRV_H )
#define GPIO_EVENT_DRV_H
 
/* ---- Include Files ----------------------------------------------------- */
 
#if defined( __KERNEL__ )
# include <linux/types.h>
# include <linux/time.h>
# include <linux/ioctl.h>
#else
# include <stdint.h>
# include <sys/time.h>
# include <sys/ioctl.h>
#endif
 
 
/* ---- Constants and Types ----------------------------------------------- */
 
// The ioctl "magic" is just some character value which is used to help
// detect when incorrect ioctl values are sent down to a driver.
 
#define GPIO_EVENT_IOCTL_MAGIC 'G'
 
/**
* Deefines for each of the ioctl commands. Note that since we want to reduce
* the possibility that a user mode program gets out of sync with a given
* driver, we explicitly assign a value to each enumeration. This makes
* it more difficult to stick new ioctl's in the middle of the list.
*/
 
typedef enum
{
GPIO_EVENT_CMD_FIRST = 0x80,
 
GPIO_EVENT_CMD_MONITOR_GPIO = 0x80,
GPIO_EVENT_CMD_SET_READ_MODE = 0x81,
 
/* Insert new ioctls here */
 
GPIO_EVENT_CMD_LAST,
 
} GPIO_EVENT_CMD;
 
typedef enum
{
GPIO_EventRisingEdge = 0x01,
GPIO_EventFallingEdge = 0x02,
GPIO_EventBothEdges = GPIO_EventRisingEdge | GPIO_EventFallingEdge,
 
} GPIO_EventEdgeType_t;
 
typedef struct
{
uint8_t gpio; // gpio to monitor
uint8_t onOff; // 0 = stop monitoring, 1 = start monitoring
GPIO_EventEdgeType_t edgeType; // Monitor rising/falling/both edges?
uint8_t debounceMilliSec; // debounce time in milliseconds
 
} GPIO_EventMonitor_t;
 
typedef enum
{
GPIO_EventReadModeAscii = 0x00, // Reads return ASCII data (default)
GPIO_EventReadModeBinary = 0x01, // Reads return Binary data
 
} GPIO_EventReadMode_t;
 
/*
* Definitions for the actual ioctl commands
*/
 
#define GPIO_EVENT_IOCTL_MONITOR_GPIO _IOW( GPIO_EVENT_IOCTL_MAGIC, GPIO_EVENT_CMD_MONITOR_GPIO, GPIO_EventMonitor_t ) // arg is GPIO_EventMonitor *
#define GPIO_EVENT_IOCTL_SET_READ_MODE _IO( GPIO_EVENT_IOCTL_MAGIC, GPIO_EVENT_CMD_SET_READ_MODE ) // arg is int
 
/*
* Definitions for sysctl. The top level define has to be unique system wide.
* The kernel defines values 1 thru about 10 (see include/linunx/sysctl.h)
*/
 
#define CTL_GPIO_EVENT 0x47504576 // 'GPEv' in hex form
 
/*
* Reads return GPIO_Event_t structures
*/
 
typedef struct
{
uint8_t gpio; // GPIO that this event is for
GPIO_EventEdgeType_t edgeType; // Type of edge detected
struct timeval time; // Time the event occurred
 
} GPIO_Event_t;
 
#endif // GPIO_EVENT_DRV_H
 
/linux/oe/user.collection/recipes/gpio-event-module-org/files/Makefile
0,0 → 1,26
#############################################################################
#
# Makefile for building the gpio-event kernel module
#
#############################################################################
 
obj-m := gpio-event.o
 
PWD = $(shell pwd)
KBUILD_FLAGS = -C $(KERNEL_PATH) M=$(PWD) KERNELRELEASE=$(KERNELRELEASE)
 
$(info ==============================================================)
$(info KERNEL_PATH = $(KERNEL_PATH))
$(info KERNELRELEASE = $(KERNELRELEASE))
$(info ARCH = $(ARCH))
$(info ==============================================================)
$(error Die)
 
default: modules
 
modules:
$(MAKE) $(KBUILD_FLAGS) modules
 
clean:
rm -rf *.o *~ *.ko *.mod.c
 
/linux/oe/user.collection/recipes/gpio-event-module-org/files/gpio-event.c
0,0 → 1,1202
/****************************************************************************
*
* Copyright (c) 2006 Dave Hylands <dhylands@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*
****************************************************************************
*
* This driver allows multiple GPIO pins to be monitored and allows a user
* mode program to be notified when the pin changes.
*
****************************************************************************/
 
/* ---- Include Files ---------------------------------------------------- */
 
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/version.h>
#include <linux/sched.h>
 
#include <asm/uaccess.h>
#include <asm/ioctls.h>
 
#include <mach/gpio.h>
 
#include "gpio-event.h"
 
/* ---- Public Variables ------------------------------------------------- */
/* ---- Private Constants and Types -------------------------------------- */
 
#define GPIO_EVENT_DEV_NAME "gpio-event"
 
#define DEBUG_ENABLED 1
 
#if DEBUG_ENABLED
# define DEBUG( flag, fmt, args... ) do { if ( gDebug ## flag ) printk( "%s: " fmt, __FUNCTION__ , ## args ); } while (0)
#else
# define DEBUG( flag, fmt, args... )
#endif
 
/* ---- Private Variables ------------------------------------------------ */
 
static char gBanner[] __initdata = KERN_INFO "GPIO Event Monitor 0.1 Compiled: " __DATE__ " at " __TIME__ "\n";
 
static int gDebugTrace = 0;
static int gDebugIoctl = 0;
static int gDebugError = 1;
static int gLostEvents = 0;
 
static struct ctl_table_header *gSysCtlHeader;
 
static struct ctl_table gSysCtlSample[] =
{
{
.ctl_name = 1,
.procname = "lost-events",
.data = &gLostEvents,
.maxlen = sizeof( int ),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = 101,
.procname = "debug-trace",
.data = &gDebugTrace,
.maxlen = sizeof( int ),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = 102,
.procname = "debug-ioctl",
.data = &gDebugIoctl,
.maxlen = sizeof( int ),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = 103,
.procname = "debug-error",
.data = &gDebugError,
.maxlen = sizeof( int ),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{ 0 }
};
 
static struct ctl_table gSysCtl[] =
{
{
.ctl_name = CTL_GPIO_EVENT,
.procname = "gpio-event",
.mode = 0555,
.child = gSysCtlSample
},
{ 0 }
};
 
/*
* An instance of GPIO_FileData_t is maintained for file open
*/
 
#define GPIO_EVENT_QUEUE_LEN 20
 
// GPIO_EVENT_BUFFER_SIZE needs to be big enough to hold the ASCII version
// of the GPIO_Event_t as well as the binary version of the GPIO_Event_t
 
#define GPIO_EVENT_BUFFER_SIZE 32
 
typedef struct
{
struct list_head list;
wait_queue_head_t waitQueue;
 
spinlock_t queueLock;
GPIO_Event_t queueData[ GPIO_EVENT_QUEUE_LEN ];
volatile int getIndex;
volatile int putIndex;
volatile int numEvents;
 
GPIO_EventReadMode_t readMode;
 
char buffer[ GPIO_EVENT_BUFFER_SIZE ];
int bufBytes;
 
} GPIO_FileData_t;
 
/*
* An instance of GPIO_PinData_t is maintained for each GPIO line which is
* monitored,
*/
 
typedef enum
{
PIN_LOW = 0, // Matches level of GPIO line
PIN_HIGH = 1,
PIN_BOUNCING_LOW,
PIN_BOUNCING_HIGH,
} PinState_t;
 
typedef struct
{
struct list_head list; // list of all pins
 
int gpio; // The gpio line being monitored
 
// We maintain two lists, a global list of pins, and a list associated with each open
 
 
struct timer_list debounceTimer; // Timer to wake u up after an edge
uint8_t debounceMilliSec; // debounce time in milliseconds
char devName[ 16 ]; // gpio xx event
 
GPIO_EventEdgeType_t edgeType; // Type of edge(s) we're looking for.
 
PinState_t pinState; // Was the GPIO line low or high?
 
} GPIO_PinData_t;
 
static volatile int gReportLostEvents = 1;
 
static struct class *gGpioEventClass = NULL;
static struct cdev gGpioEventCDev;
static dev_t gGpioEventDevNum = 0;
 
static spinlock_t gFileListLock = SPIN_LOCK_UNLOCKED;
static spinlock_t gPinListLock = SPIN_LOCK_UNLOCKED;
 
static LIST_HEAD( gFileList );
static LIST_HEAD( gPinList );
 
static struct proc_dir_entry *gProcGpioEvent;
static struct proc_dir_entry *gProcPins;
 
 
/* ---- Private Function Prototypes -------------------------------------- */
/* ---- Functions -------------------------------------------------------- */
 
typedef struct
{
unsigned long flags;
struct list_head *list;
 
} pin_seq_t;
 
/****************************************************************************
*
* pin_seq_start
*
* seq_file iterator which goes through the pins being monitored
*
****************************************************************************/
 
static void *pin_seq_start( struct seq_file *s, loff_t *pos )
{
pin_seq_t *ps;
loff_t i;
 
s->private = NULL;
 
if (( ps = kcalloc( 1, sizeof( pin_seq_t ), GFP_KERNEL )) == NULL )
{
return ERR_PTR( -ENOMEM );
}
s->private = ps;
 
spin_lock_irqsave( &gPinListLock, ps->flags );
 
if ( list_empty( &gPinList ))
{
DEBUG( Trace, "list_empty\n" );
return NULL;
}
ps->list = gPinList.next;
 
for ( i = 0; i < *pos; i++ )
{
if ( list_is_last( ps->list, &gPinList ))
{
DEBUG( Trace, "No item @ %llu\n", i + 1 );
return NULL;
}
ps->list = ps->list->next;
}
 
 
DEBUG( Trace, "ps->list = 0x%08lx, *pos = %llu\n", (long)ps->list, *pos );
 
return ps->list;
 
} // pin_seq_start
 
/****************************************************************************
*
* pin_seq_show
*
* seq_file iterator which goes through the pins being monitored
*
****************************************************************************/
 
static int pin_seq_show( struct seq_file *s, void *v )
{
GPIO_PinData_t *pin = list_entry( v, GPIO_PinData_t, list );
char *edgeTypeStr;
 
DEBUG( Trace, "v = 0x%08lx\n", (long)v );
 
switch ( pin->edgeType )
{
case GPIO_EventRisingEdge: edgeTypeStr = "Rising "; break;
case GPIO_EventFallingEdge: edgeTypeStr = "Falling"; break;
case GPIO_EventBothEdges: edgeTypeStr = "Both "; break;
default: edgeTypeStr = "Unknown"; break;
}
 
seq_printf( s, "GPIO: %3d Edge: %s Debounce: %d msec\n", pin->gpio, edgeTypeStr, pin->debounceMilliSec );
 
return 0;
 
} // pin_seq_show
/****************************************************************************
*
* pin_seq_next
*
* seq_file iterator which goes through the pins being monitored
*
****************************************************************************/
 
static void *pin_seq_next( struct seq_file *s, void *v, loff_t *pos )
{
pin_seq_t *ps = s->private;
 
DEBUG( Trace, "v = 0x%08lx *pos = %llu\n", (long)v, *pos );
 
if ( list_is_last( ps->list, &gPinList ))
{
DEBUG( Trace, "ps->list = 0x%08lx (end of list)\n", (long)ps->list );
 
return NULL;
}
(*pos)++;
ps->list = ps->list->next;
 
DEBUG( Trace, "ps->list = 0x%08lx\n", (long)ps->list );
 
return ps->list;
 
} // pin_seq_next
 
/****************************************************************************
*
* pin_seq_stop
*
* seq_file iterator which goes through the pins being monitored
*
****************************************************************************/
 
static void pin_seq_stop( struct seq_file *s, void *v )
{
pin_seq_t *ps = s->private;
 
DEBUG( Trace, "v = 0x%08lx\n", (long)v );
 
if ( ps != NULL )
{
spin_unlock_irqrestore( &gPinListLock, ps->flags );
kfree( ps );
}
 
} // pin_seq_stop
 
/****************************************************************************
*
* pin_seq_ops
*
* Ties all of the pin_seq_xxx routines together.
*
****************************************************************************/
 
static struct seq_operations pin_seq_ops =
{
.start = pin_seq_start,
.next = pin_seq_next,
.stop = pin_seq_stop,
.show = pin_seq_show
};
 
/****************************************************************************
*
* pins_proc_open
*
* Open method for /proc/gpio-event/pin
*
****************************************************************************/
 
static int pins_proc_open( struct inode *inode, struct file *file )
{
DEBUG( Trace, "called\n" );
 
return seq_open( file, &pin_seq_ops );
}
 
/****************************************************************************
*
* pin_proc_ops
*
* File operations for our /proc/gpio-event/pins file
*
****************************************************************************/
 
static struct file_operations pins_proc_ops =
{
.owner = THIS_MODULE,
.open = pins_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
 
 
/****************************************************************************
*
* find_pin
*
* Searches the list to see if 'gpio' is currently being monitored.
*
****************************************************************************/
 
static GPIO_PinData_t *find_pin( int gpio )
{
struct list_head *pin;
 
assert_spin_locked( &gPinListLock );
 
list_for_each( pin, &gPinList )
{
GPIO_PinData_t *pinData = list_entry( pin, GPIO_PinData_t, list );
 
if ( pinData->gpio == gpio )
{
return pinData;
}
}
 
return NULL;
 
} // find_pin
 
/****************************************************************************
*
* gpio_event_queue_event
*
* Queues an sample event from the bottom half to the top half. This
* function queues up the event on every file that's open.
*
****************************************************************************/
 
static void gpio_event_queue_event( const GPIO_Event_t *gpioEvent )
{
unsigned long flags;
struct list_head *file;
 
DEBUG( Trace, "gpio %d:%c@%ld.%06ld\n",
gpioEvent->gpio,
gpioEvent->edgeType == GPIO_EventRisingEdge ? 'R' : 'F',
gpioEvent->time.tv_sec,
gpioEvent->time.tv_usec );
 
// Queue up the event on all of the open files
//
// This function is only called from the ISR, with interrupts already
// disabled.
 
spin_lock_irqsave( &gFileListLock, flags );
 
list_for_each( file, &gFileList )
{
GPIO_FileData_t *fileData = list_entry( file, GPIO_FileData_t, list );
 
spin_lock( &fileData->queueLock );
{
if ( fileData->numEvents >= GPIO_EVENT_QUEUE_LEN )
{
// Queue is full - Only report first event lost
if ( gReportLostEvents )
{
printk( KERN_ERR "GPIO Event: event lost due to queue full\n" );
gReportLostEvents = 0;
}
gLostEvents++;
}
else
{
fileData->queueData[ fileData->putIndex++ ] = *gpioEvent;
if ( fileData->putIndex >= GPIO_EVENT_QUEUE_LEN )
{
fileData->putIndex = 0;
}
fileData->numEvents++;
}
}
spin_unlock( &fileData->queueLock );
 
wake_up_interruptible( &fileData->waitQueue );
}
spin_unlock_irqrestore( &gFileListLock, flags );
 
} // gpio_event_queue_event
 
/****************************************************************************
*
* gpio_event_dequeue_event
*
* Removes an event from the queue
*
****************************************************************************/
 
static int gpio_event_dequeue_event( GPIO_FileData_t *fileData, GPIO_Event_t *gpioEvent )
{
unsigned long flags;
int eventAvailable = 0;
 
spin_lock_irqsave( &fileData->queueLock, flags );
{
if ( fileData->numEvents > 0 )
{
*gpioEvent = fileData->queueData[ fileData->getIndex++ ];
if ( fileData->getIndex >= GPIO_EVENT_QUEUE_LEN )
{
fileData->getIndex = 0;
}
fileData->numEvents--;
 
eventAvailable = 1;
 
if ( fileData->numEvents == 0 )
{
// Since somebody is reading the queue now, indicate that we
// can report lost events again
 
gReportLostEvents = 1;
}
}
}
spin_unlock_irqrestore( &fileData->queueLock, flags );
 
DEBUG( Trace, "gpio %d:%c@%ld.%06ld\n",
gpioEvent->gpio,
gpioEvent->edgeType == GPIO_EventRisingEdge ? 'R' : 'F',
gpioEvent->time.tv_sec,
gpioEvent->time.tv_usec );
 
return eventAvailable;
 
} // gpio_event_dequeue_event
 
/****************************************************************************
*
* gpio_event_irq
*
****************************************************************************/
 
static irqreturn_t gpio_event_irq( int irq, void *dev_id )
{
GPIO_PinData_t *pinData = (GPIO_PinData_t *)dev_id;
GPIO_Event_t gpioEvent;
int currLevel = gpio_get_value( pinData->gpio );
 
// We're called with interrupts disabled.
 
(void)irq;
 
do_gettimeofday( &gpioEvent.time );
gpioEvent.gpio = pinData->gpio;
 
if ( pinData->debounceMilliSec == 0 )
{
// We assume that this is a clean signal
 
pinData->pinState = (PinState_t)currLevel;
 
if ( pinData->edgeType == GPIO_EventBothEdges )
{
// There's no register to tell which edge just occurred. So we
// assume that it just changed into its current level.
 
if ( currLevel )
{
// Pin is currently high, so this must be a rising edge
 
gpioEvent.edgeType = GPIO_EventRisingEdge;
}
else
{
// Pin is currently low, so this must be a falling edge
 
gpioEvent.edgeType = GPIO_EventFallingEdge;
}
}
else
{
// If we're only monitoring one type of edge, then that's the one
// that happened.
 
gpioEvent.edgeType = pinData->edgeType;
}
gpio_event_queue_event( &gpioEvent );
}
else
{
gpioEvent.edgeType = 0;
 
// If we need to debounce, then we need to monitor both edges, and
// use the debounce timer to figure out the real state. So we don't
// actually know which edge we just got. We use a state machine
// to track things.
 
switch ( pinData->pinState )
{
case PIN_LOW:
{
pinData->pinState = PIN_BOUNCING_HIGH;
gpioEvent.edgeType = GPIO_EventRisingEdge;
break;
}
 
case PIN_HIGH:
{
pinData->pinState = PIN_BOUNCING_LOW;
gpioEvent.edgeType = GPIO_EventFallingEdge;
break;
}
 
default:
{
break;
}
}
 
if (( pinData->edgeType & gpioEvent.edgeType ) != 0 )
{
// This is an edge that the user is interested in - send it along.
 
gpio_event_queue_event( &gpioEvent );
}
 
// Disable interrupts for our gpio to allow debounce to occur. The
// timer will re-enable the interrupt.
 
disable_irq_nosync( irq );
 
// Since we have no idea when in the current jiffy that the edge
// occurred, we add 1 to the calculation to guarantee at least one
// whole jiffy.
 
mod_timer( &pinData->debounceTimer, jiffies + msecs_to_jiffies( pinData->debounceMilliSec ) + 1 );
}
 
return IRQ_HANDLED;
 
} // gpio_event_irq
 
/****************************************************************************
*
* gpio_event_timer
*
****************************************************************************/
 
void gpio_event_timer( unsigned long data )
{
GPIO_PinData_t *pinData = (GPIO_PinData_t *)data;
 
// This function is called when the debounce timer for a gpio expires.
// We record the state of the pin so that we can figure out what the
// next edge will be.
 
pinData->pinState = ( gpio_get_value( pinData->gpio ) != 0 );
 
// Turn interrupts back on so we can catch the next edge
 
enable_irq( gpio_to_irq( pinData->gpio ));
 
} // gpio_event_timer
 
/****************************************************************************
*
* gpio_event_monitor
*
****************************************************************************/
 
static int gpio_event_monitor( GPIO_EventMonitor_t *monitor )
{
int rc = 0;
unsigned long flags;
GPIO_PinData_t *pinData;
unsigned long irqFlags;
 
spin_lock_irqsave( &gPinListLock, flags );
 
if ( monitor->onOff )
{
// Check to make sure we aren't already monitoring the gpio
 
if (( pinData = find_pin( monitor->gpio )) != NULL )
{
// We are already monitoring the pin. Unmonitor the pin and then
// proceed.
 
monitor->onOff = 0;
 
spin_unlock_irqrestore( &gPinListLock, flags );
gpio_event_monitor( monitor );
spin_lock_irqsave( &gPinListLock, flags );
}
 
if (( pinData = kcalloc( 1, sizeof( *pinData ), GFP_KERNEL )) == NULL )
{
DEBUG( Error, "GPIO %d: Out of memory\n", monitor->gpio );
rc = -ENOMEM;
goto out;
}
 
INIT_LIST_HEAD( &pinData->list );
 
snprintf( pinData->devName, sizeof( pinData->devName ), "gpio %d event", monitor->gpio );
 
// Note:
// Calling request_irq will automatically set the pin to be an input.
 
irqFlags = 0;
 
if ( monitor->debounceMilliSec == 0 )
{
// A clean signal is being presented, so we can just look for
// a particular edge
 
if (( monitor->edgeType | GPIO_EventRisingEdge ) != 0 )
{
irqFlags |= IRQF_TRIGGER_RISING;
}
if (( monitor->edgeType | GPIO_EventFallingEdge ) != 0 )
{
irqFlags |= IRQF_TRIGGER_FALLING;
}
}
else
{
// Since we need to debounce, we need to look for both types of
// edges, since we get both types of edges whenever a bounce
// happens.
 
irqFlags |= IRQF_TRIGGER_RISING;
irqFlags |= IRQF_TRIGGER_FALLING;
}
 
if (( rc = request_irq( gpio_to_irq( monitor->gpio ), gpio_event_irq, irqFlags, pinData->devName, pinData )) != 0 )
{
DEBUG( Error, "Unable to register irq for GPIO %d\n", monitor->gpio );
kfree( pinData );
goto out;
}
 
pinData->gpio = monitor->gpio;
pinData->edgeType = monitor->edgeType;
pinData->debounceMilliSec = monitor->debounceMilliSec;
 
init_timer( &pinData->debounceTimer );
 
pinData->debounceTimer.data = (unsigned long)pinData;
pinData->debounceTimer.function = gpio_event_timer;
 
list_add_tail( &pinData->list, &gPinList );
 
if ( gpio_get_value( pinData->gpio ) == 0 )
{
pinData->pinState = PIN_LOW;
}
else
{
pinData->pinState = PIN_HIGH;
}
}
else
{
if (( pinData = find_pin( monitor->gpio )) == NULL )
{
DEBUG( Error, "GPIO %d isn't being monitored\n", monitor->gpio );
rc = -ENXIO;
goto out;
}
 
// We've found the gpio being monitored - turn things off.
 
free_irq( gpio_to_irq( pinData->gpio ), pinData );
 
del_timer_sync( &pinData->debounceTimer );
list_del( &pinData->list );
 
kfree( pinData );
}
 
out:
 
spin_unlock_irqrestore( &gPinListLock, flags );
 
return rc;
 
} // gpio_event_monitor
 
/****************************************************************************
*
* gpio_event_ioctl
*
* Called to process ioctl requests
*
*****************************************************************************/
 
int gpio_event_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg )
{
GPIO_FileData_t *fileData;
 
DEBUG( Trace, "type: '%c' cmd: 0x%x\n", _IOC_TYPE( cmd ), _IOC_NR( cmd ));
 
fileData = file->private_data;
 
switch ( cmd )
{
case GPIO_EVENT_IOCTL_MONITOR_GPIO:
{
GPIO_EventMonitor_t monitor;
 
if ( copy_from_user( &monitor, (void *)arg, sizeof( monitor )) != 0 )
{
return -EFAULT;
}
return gpio_event_monitor( &monitor );
}
 
case GPIO_EVENT_IOCTL_SET_READ_MODE:
{
fileData->readMode = (GPIO_EventReadMode_t)arg;
break;
}
 
case TCGETS:
{
// When cat opens this device, we get this ioctl
return -ENOTTY;
}
 
default:
{
DEBUG( Error, "Unrecognized ioctl: '0x%x'\n", cmd );
return -ENOTTY;
}
}
 
return 0;
 
} // gpio_event_ioctl
 
/****************************************************************************
*
* gpio_event_open
*
****************************************************************************/
 
static int gpio_event_open( struct inode *inode, struct file *file )
{
unsigned long flags;
GPIO_FileData_t *fileData;
 
DEBUG( Trace, "gpio_event_open called, major = %d, minor = %d\n", MAJOR( inode->i_rdev ), MINOR( inode->i_rdev ));
 
// Allocate a per-open data structure
 
if (( fileData = kcalloc( 1, sizeof( *fileData ), GFP_KERNEL )) == NULL )
{
return -ENOMEM;
}
 
INIT_LIST_HEAD( &fileData->list );
 
init_waitqueue_head( &fileData->waitQueue );
 
spin_lock_init( &fileData->queueLock );
 
fileData->getIndex = 0;
fileData->putIndex = 0;
fileData->numEvents = 0;
fileData->bufBytes = 0;
 
fileData->readMode = GPIO_EventReadModeAscii;
 
file->private_data = fileData;
 
spin_lock_irqsave( &gFileListLock, flags );
{
list_add_tail( &fileData->list, &gFileList );
}
spin_unlock_irqrestore( &gFileListLock, flags );
return 0;
 
} // gpio_event_open
 
/****************************************************************************
*
* gpio_event_read
*
****************************************************************************/
 
static ssize_t gpio_event_read( struct file *file, char *buffer, size_t spaceRemaining, loff_t *ppos )
{
int rc;
ssize_t bytesCopied = 0;
ssize_t bytesToCopy;
GPIO_FileData_t *fileData = file->private_data;
 
DEBUG( Trace, "gpio_event_read called, major = %d, minor = %d\n", MAJOR( file->f_dentry->d_inode->i_rdev ), MINOR( file->f_dentry->d_inode->i_rdev ));
 
if ( spaceRemaining == 0 )
{
return 0;
}
 
// First of all, return any unread data from the previous call
 
if ( fileData->bufBytes > 0 )
{
if ( spaceRemaining < fileData->bufBytes )
{
bytesCopied = spaceRemaining;
}
else
{
bytesCopied = fileData->bufBytes;
}
 
if ( copy_to_user( &buffer[0], &fileData->buffer[0], bytesCopied ) != 0 )
{
return -EFAULT;
}
if ( fileData->bufBytes > bytesCopied )
{
memmove( &fileData->buffer[ 0 ], &fileData->buffer[ bytesCopied ], fileData->bufBytes - bytesCopied );
}
fileData->bufBytes -= bytesCopied;
 
if ( fileData->bufBytes > 0 )
{
// We copied some data, but not all of it. Return early.
 
return bytesCopied;
}
}
 
do
{
if ((( file->f_flags & O_NONBLOCK ) != 0 ) && ( fileData->numEvents == 0 ))
{
// File was opened non-blocking and no more data is available
// We don't want to wait for an event, so exit from the loop
 
break;
}
 
rc = wait_event_interruptible( fileData->waitQueue, ( fileData->numEvents > 0 ));
if ( rc != 0 )
{
return rc;
}
 
if ( fileData->readMode == GPIO_EventReadModeBinary )
{
gpio_event_dequeue_event( fileData, (GPIO_Event_t *)&fileData->buffer[0] );
 
fileData->bufBytes = sizeof( GPIO_Event_t );
}
else
{
GPIO_Event_t gpioEvent;
 
gpio_event_dequeue_event( fileData, &gpioEvent );
 
// ASCII Mode output:
//
// nn E tttttttt.tttttt
//
// Where nn is the base-10 GPIO number
// E is R or F (for rising or falling edge)
// tttttttt.tttttt is the timestamp with microsecond resolution
 
fileData->bufBytes = snprintf( fileData->buffer, sizeof( fileData->buffer ),
"%2d %c %ld.%06ld\n",
gpioEvent.gpio,
(( gpioEvent.edgeType == GPIO_EventRisingEdge ) ? 'R' : 'F' ),
gpioEvent.time.tv_sec,
gpioEvent.time.tv_usec );
}
 
if ( spaceRemaining >= fileData->bufBytes )
{
bytesToCopy = fileData->bufBytes;
}
else
{
bytesToCopy = spaceRemaining;
}
 
if ( copy_to_user( &buffer[ bytesCopied ], &fileData->buffer[0], bytesToCopy ) != 0 )
{
return -EFAULT;
}
spaceRemaining -= bytesToCopy;
bytesCopied += bytesToCopy;
fileData->bufBytes -= bytesToCopy;
 
if ( fileData->bufBytes > 0 )
{
// We couldn't copy all of the data out of the buffer. Move the
// remaining data to the beginning of the buffer and exit.
 
memmove( &fileData->buffer[ 0 ], &fileData->buffer[ bytesToCopy ], fileData->bufBytes );
return bytesCopied;
}
} while (( fileData->numEvents > 0 ) && ( spaceRemaining > 0 ));
 
if ((( file->f_flags & O_NONBLOCK ) != 0 ) && ( bytesCopied == 0 ))
{
// File was opened non-blocking and we didn't copy any data.
 
return -EAGAIN;
}
 
return bytesCopied;
 
} // gpio_event_read
 
/****************************************************************************
*
* gpio_event_poll - used by select & poll
*
****************************************************************************/
 
static unsigned int gpio_event_poll(struct file *file, poll_table *wait)
{
unsigned long flags;
GPIO_FileData_t *fileData = file->private_data;
unsigned int mask = 0;
 
poll_wait( file, &fileData->waitQueue, wait );
 
spin_lock_irqsave( &fileData->queueLock, flags );
{
if (( fileData->bufBytes > 0 ) || ( fileData->numEvents > 0 ))
{
mask |= POLLIN | POLLRDNORM; // readable
}
}
spin_unlock_irqrestore( &fileData->queueLock, flags );
 
return mask;
 
} // gpio_event_poll
 
/****************************************************************************
*
* gpio_event_release
*
****************************************************************************/
 
static int gpio_event_release( struct inode *inode, struct file *file )
{
unsigned long flags;
GPIO_FileData_t *fileData = file->private_data;
 
DEBUG( Trace, "gpio_event_release called\n" );
 
spin_lock_irqsave( &gFileListLock, flags );
{
list_del( &fileData->list );
}
spin_unlock_irqrestore( &gFileListLock, flags );
 
kfree( fileData );
 
return 0;
 
} // gpio_event_release
 
/****************************************************************************
*
* File Operations (these are the device driver entry points)
*
****************************************************************************/
 
struct file_operations gpio_event_fops =
{
owner: THIS_MODULE,
ioctl: gpio_event_ioctl,
open: gpio_event_open,
poll: gpio_event_poll,
release: gpio_event_release,
read: gpio_event_read,
};
 
/****************************************************************************
*
* gpio_event_init
*
* Called to perform module initialization when the module is loaded
*
****************************************************************************/
 
static int __init gpio_event_init( void )
{
int rc;
 
DEBUG( Trace, "called\n" );
 
printk( gBanner );
 
// Get a major number
 
if (( rc = alloc_chrdev_region( &gGpioEventDevNum, 0, 1, GPIO_EVENT_DEV_NAME )) < 0 )
{
printk( KERN_WARNING "sample: Unable to allocate major, err: %d\n", rc );
return rc;
}
DEBUG( Trace, "allocated major:%d minor:%d\n", MAJOR( gGpioEventDevNum ), MINOR( gGpioEventDevNum ));
 
// Register our proc entries.
 
gProcGpioEvent = create_proc_entry( "gpio-event", S_IFDIR | S_IRUGO | S_IXUGO, NULL );
if ( gProcGpioEvent == NULL )
{
return -ENOMEM;
}
gProcPins = create_proc_entry( "pins", 0444, gProcGpioEvent );
if ( gProcPins != NULL )
{
gProcPins->proc_fops = &pins_proc_ops;
}
 
#if ( LINUX_VERSION_CODE <= KERNEL_VERSION( 2, 6, 20 ))
gSysCtlHeader = register_sysctl_table( gSysCtl, 0 );
if ( gSysCtlHeader != NULL )
{
gSysCtlHeader->ctl_table->child->de->owner = THIS_MODULE;
}
#else
gSysCtlHeader = register_sysctl_table( gSysCtl );
#endif
 
// Register our device. The device becomes "active" as soon as cdev_add
// is called.
 
cdev_init( &gGpioEventCDev, &gpio_event_fops );
gGpioEventCDev.owner = THIS_MODULE;
 
if (( rc = cdev_add( &gGpioEventCDev, gGpioEventDevNum, 1 )) != 0 )
{
printk( KERN_WARNING "sample: cdev_add failed: %d\n", rc );
return rc;
}
 
// Create a class, so that udev will make the /dev entry
 
gGpioEventClass = class_create( THIS_MODULE, GPIO_EVENT_DEV_NAME );
if ( IS_ERR( gGpioEventClass ))
{
printk( KERN_WARNING "sample: Unable to create class\n" );
return -1;
}
 
device_create( gGpioEventClass, NULL, gGpioEventDevNum, NULL, GPIO_EVENT_DEV_NAME );
 
return 0;
 
} // gpio_event_init
 
/****************************************************************************
*
* gpio_event_exit
*
* Called to perform module cleanup when the module is unloaded.
*
****************************************************************************/
 
static void __exit gpio_event_exit( void )
{
struct list_head *next;
struct list_head *pin;
GPIO_EventMonitor_t monitor;
 
DEBUG( Trace, "called\n" );
 
// If there are any pins which are currently being monitored, then we
// need to unmonitor them.
 
memset( &monitor, 0, sizeof( monitor ));
 
list_for_each_safe( pin, next, &gPinList )
{
GPIO_PinData_t *pinData = list_entry( pin, GPIO_PinData_t, list );
 
monitor.gpio = pinData->gpio;
 
gpio_event_monitor( &monitor );
}
 
// Deregister our driver
 
device_destroy( gGpioEventClass, gGpioEventDevNum );
class_destroy( gGpioEventClass );
 
cdev_del( &gGpioEventCDev );
 
if ( gSysCtlHeader != NULL )
{
unregister_sysctl_table( gSysCtlHeader );
}
remove_proc_entry( "pins", gProcGpioEvent );
remove_proc_entry( "gpio-event", NULL );
unregister_chrdev_region( gGpioEventDevNum, 1 );
 
} // gpio_event_exit
 
/****************************************************************************/
 
module_init(gpio_event_init);
module_exit(gpio_event_exit);
 
MODULE_AUTHOR("Dave Hylands");
MODULE_DESCRIPTION("GPIO Event Driver");
MODULE_LICENSE("Dual BSD/GPL");
 
/linux/oe/user.collection/recipes/gpio-event-module-org/gpio-event-module-1.0.0.bb
0,0 → 1,32
DESCRIPTION = "Linux Driver for Detecting GPIO events"
SECTION = "base"
PRIORITY = "optional"
LICENSE = "GPL"
RDEPENDS = "kernel (${KERNEL_VERSION})"
DEPENDS = "virtual/kernel"
 
PR = "r2"
 
INITSCRIPT_NAME = "gpio-event"
INITSCRIPT_PARAMS = "defaults 40"
 
SVN_REPO_URI = "http://svn.hylands.org/linux/gpio-event"
SRC_URI = "svn://svn.hylands.org/linux/gpio-event"
 
S = "${WORKDIR}"
 
inherit module update-rc.d
 
do_install() {
install -d ${D}${base_libdir}/modules/${KERNEL_VERSION}/extra/
install -m 0644 ${WORKDIR}/gpio-event.ko ${D}${base_libdir}/modules/${KERNEL_VERSION}/extra/
 
install -d ${D}${sysconfdir}/init.d/
install -m 0755 ${WORKDIR}/gpio-event.init ${D}${sysconfdir}/init.d/gpio-event
}
 
PACKAGES = "${PN}"
 
FILES_${PN} = "${base_libdir}/modules/"
FILES_${PN} += "${sysconfdir}/"
 
/linux/oe/user.collection/recipes/tasks/task-gpio-all.bb
0,0 → 1,11
DESCRIPTION = "All user space GPIO manipulation packages"
 
inherit task
 
PR = "r1"
 
RRECOMMENDS_${PN} = " \
gpio-app gpio-lib gpio-module \
gpio-event-app gpio-event-module \
"
 
/linux/oe/user.collection/recipes/gpio-event-app/gpio-event-app.bb
0,0 → 1,25
DESCRIPTION = "User App for Detecting GPIO events"
SECTION = "base"
PRIORITY = "optional"
LICENSE = "GPL"
DEPENDS = "gpio-event-module"
 
PV="svn${SRCDATE}"
PR = "r2"
 
SVN_REPO_URI = "http://svn.hylands.org/linux/gpio-event/app"
SRC_URI = "svn://svn.hylands.org/linux/gpio-event/;module=app;proto=http"
 
S = "${WORKDIR}/app"
 
TARGET_CC_ARCH += "${LDFLAGS}"
 
do_install() {
install -d ${D}${bindir}/
install -m 0755 ${S}/gpio-event ${D}${bindir}/
}
 
PACKAGES = "${PN}"
 
FILES_${PN} = "${bindir}/gpio-event"
 
/linux/oe/user.collection/recipes/helloworld/files/hello.c
0,0 → 1,10
#include <stdio.h>
 
int main( int argc, char **argv )
{
(void)argc;
(void)argv;
 
printf( "Hello World\n" );
return 0;
}
/linux/oe/user.collection/recipes/helloworld/helloworld_1.0.0.bb
0,0 → 1,23
DESCRIPTION = "hello world sample program"
 
PR = "r0"
 
DEPENDS = ""
 
SRC_URI = " \
file://hello.c \
"
 
S = "${WORKDIR}"
 
do_compile () {
${CC} ${CFLAGS} ${LDFLAGS} -o hello hello.c
}
 
do_install () {
install -d ${D}${bindir}/
install -m 0755 ${S}/hello ${D}${bindir}/
}
 
FILES_${PN} = "${bindir}/hello"
 
/linux/oe/user.collection/recipes/gpio-app/gpio-app.bb
0,0 → 1,25
DESCRIPTION = "User App for manipulating GPIO pins"
SECTION = "base"
PRIORITY = "optional"
LICENSE = "GPL"
DEPENDS = "gpio-lib"
 
PV="svn${SRCDATE}"
PR = "r0"
 
SVN_REPO_URI = "http://svn.hylands.org/linux/gpio/app"
SRC_URI = "svn://svn.hylands.org/linux/gpio/;module=app;proto=http"
 
S = "${WORKDIR}/app"
 
TARGET_CC_ARCH += "${LDFLAGS}"
 
do_install() {
install -d ${D}${bindir}/
install -m 0755 ${S}/gpio ${D}${bindir}/
}
 
PACKAGES = "${PN}"
 
FILES_${PN} = "${bindir}/gpio"