Well, I lied... I thought I'd forget about this tonight and worry about it tomorrow since in my haste and over-worked state accidentally "reported" instead of "quoted" for a reply...
I have something that appears to be working.
The good news is the userland app goes nicely to sleep while waiting and doesn't hog the CPU thrashing.
The bad news is without completely re-writing some strange method and cleaning up the code, it is very possible to press the button very quickly and not read the value you expect. Your userland app will return from poll, call read, and if you've let go, get the "wrong" value from what you're expecting. This means you have to hold the button down for a tiny amount longer than the debounce interval.
I also changed the functionality of the read function since technically, the poll method is supposed to determine whether data can be read. With the original implementation, you can always read, not just on transitions. Since the /dev/sence device's open call can only be opened once, that one process should read from /dev/switch. Unfortunately it seems the way the stock control panel is coded up, it doesn't like this method of read and doesn't open the device with O_NONBLOCK to conform.
Here's the patch of my HACK changes:
--- a/drivers/char/chumby_sense1.c 2011-01-19 15:55:38.000000000 -0800
+++ b/drivers/char/chumby_sense1.c 2012-12-30 22:06:14.000000000 -0800
@@ -172,6 +172,9 @@
#define FIXEDPOINT_NORM 1000
+static wait_queue_head_t gWaitQ;
+static bool switch_changed = false;
+
struct sense1data {
unsigned char bent;
unsigned char isDebounce;
@@ -207,7 +210,7 @@
size_t count, loff_t * ppos);
//static ssize_t chumby_sense1_write(struct file *file, const unsigned char *buf,
// size_t count, loff_t *ppos );
-//static unsigned int chumby_sense1_poll(struct file * filp, struct poll_table_struct * wait);
+static unsigned int chumby_sense1_poll(struct file * filp, struct poll_table_struct * wait);
static unsigned int gDebounce = 10;
@@ -249,6 +252,7 @@
.read = chumby_sense1_read,
.open = chumby_sense1_open,
.release = chumby_sense1_release,
+ .poll = chumby_sense1_poll,
};
///////////// code /////////////
@@ -353,6 +357,13 @@
data->lastTransitionTime = data->currentTransitionTime;
data->lastTransitionState = state;
data->isDebounce = 0;
+
+ // wake up our poll wq
+ if ( !switch_changed )
+ {
+ switch_changed = true;
+ wake_up(&gWaitQ);
+ }
} else {
// just wait until our turn comes
}
@@ -696,6 +707,9 @@
gSysCtlHeader->ctl_table->child->de->owner = THIS_MODULE;
#endif
+ // We must init our wait queue before the add_timer below
+ init_waitqueue_head(&gWaitQ);
+
///// initialize periodic task queue
sense1task_data.bent = 0;
sense1task_data.lastTransitionTime = jiffies;
@@ -746,6 +760,16 @@
char retval = 0;
size_t retlen = 1;
+ if (file->f_flags & O_NONBLOCK)
+ {
+ if ( switch_changed != true )
+ return -EBUSY;
+ }
+
+ wait_event_interruptible(gWaitQ, switch_changed == true);
+
+ switch_changed = false;
+
retval = sense1task_data.bent;
// CHUMSENSE1_DEBUG(Trace, "sense1 read exit with: %d\n", retval);
@@ -755,6 +779,18 @@
return retlen;
}
+static unsigned int chumby_sense1_poll(struct file * filp, struct poll_table_struct * wait)
+{
+ unsigned int mask = 0;
+
+ poll_wait(filp, &gWaitQ, wait);
+
+ if ( switch_changed )
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
static void __exit chumby_sense1_exit(void)
{
dev_t devno = MKDEV(sense1_major, sense1_minor);
Linux Guy - Occasional Chumby Hacker