gpio: initial implementation of interupt handling on gpio
authorBrendan Le Foll <brendan.le.foll@intel.com>
Wed, 7 May 2014 13:48:21 +0000 (14:48 +0100)
committerBrendan Le Foll <brendan.le.foll@intel.com>
Wed, 7 May 2014 13:48:21 +0000 (14:48 +0100)
Signed-off-by: Brendan Le Foll <brendan.le.foll@intel.com>
CMakeLists.txt
api/gpio.h
examples/CMakeLists.txt
examples/isr_pin6.c [new file with mode: 0644]
src/gpio/gpio.c

index 23b6394..16256e4 100644 (file)
@@ -1,6 +1,8 @@
 cmake_minimum_required (VERSION 2.8)
 project (maa)
 
+FIND_PACKAGE (Threads)
+
 set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall")
 set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall")
 
index 6a141b6..6bc6ff8 100644 (file)
@@ -35,15 +35,20 @@ extern "C" {
 #endif
 
 #include <stdio.h>
+#include <pthread.h>
 
 #include "maa.h"
 /**
  * A strucutre representing a gpio pin.
  */
+
 typedef struct {
     /*@{*/
     int pin; /**< the pin number, as known to the os. */
     FILE *value_fp; /**< the file pointer to the value of the gpio */
+    void (* isr)(); /**< the interupt service request */
+    pthread_t thread_id; /**< the isr handler thread id */
+    int isr_value_fp; /**< the isr file pointer on the value */
     /*@}*/
 } maa_gpio_context;
 
@@ -65,6 +70,13 @@ typedef enum {
     MAA_GPIO_IN     = 1  /**< Input. */
 } gpio_dir_t;
 
+typedef enum {
+    MAA_GPIO_EDGE_NONE    = 0, /**< No interrupt on GPIO */
+    MAA_GPIO_EDGE_BOTH    = 1, /**< Interupt on rising & falling */
+    MAA_GPIO_EDGE_RISING  = 2, /**< Interupt on rising only */
+    MAA_GPIO_EDGE_FALLING = 3  /**< Interupt on falling only */
+} gpio_edge_t;
+
 /** Initialise gpio_context, based on board number
  *
  *  @param pin pin number read from the board, i.e IO3 is 3.
@@ -82,6 +94,37 @@ maa_gpio_context* maa_gpio_init(int pin);
  */
 maa_gpio_context* maa_gpio_init_raw(int gpiopin);
 
+/** Set the edge mode on the gpio
+ *
+ * @param dev The GPIO context
+ * @param mode The edge mode to set the gpio into
+ *
+ * @return maa result type.
+ */
+maa_result_t maa_gpio_edge_mode(maa_gpio_context *dev, gpio_edge_t mode);
+
+/** Set an interupt on pin
+ *
+ * @param dev The GPIO context
+ * @param mode The edge mode to set the gpio into
+ * @param fptr Function pointer to function to be called when interupt is
+ * triggered
+ *
+ * @return maa result type.
+ */
+maa_result_t
+maa_gpio_isr(maa_gpio_context *dev, gpio_edge_t edge, void (*fptr)(void));
+
+/** Stop the current interupt watcher on this GPIO, and set the GPIO edge mode
+ * to MAA_GPIO_EDGE_NONE.
+ *
+ * @param dev The GPIO context.
+ *
+ * @return maa result type.
+ */
+maa_result_t
+maa_gpio_isr_exit(maa_gpio_context *dev);
+
 /** Set GPIO Output Mode,
  *
  * @param dev The GPIO context
index 49de0b3..cfd5718 100644 (file)
@@ -3,11 +3,13 @@ add_executable (hellomaa hellomaa.c)
 add_executable (cycle-pwm3 cycle-pwm3.c)
 add_executable (blink-io8 blink-io8.c)
 add_executable (analogin_a0 analogin_a0.c)
+add_executable (isr_pin6 isr_pin6.c)
 
 include_directories(${PROJECT_SOURCE_DIR}/api ${PROJECT_SOURCE_DIR}/include)
 
-target_link_libraries (hellomaa maa)
-target_link_libraries (i2c_HMC5883L maa m)
-target_link_libraries (cycle-pwm3 maa)
-target_link_libraries (blink-io8 maa)
-target_link_libraries (analogin_a0 maa)
+target_link_libraries (hellomaa maa ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries (i2c_HMC5883L maa m ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries (cycle-pwm3 maa ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries (blink-io8 maa ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries (analogin_a0 maa ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries (isr_pin6 maa ${CMAKE_THREAD_LIBS_INIT})
diff --git a/examples/isr_pin6.c b/examples/isr_pin6.c
new file mode 100644 (file)
index 0000000..916bc20
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Author: Brendan Le Foll
+ * Copyright (c) 2014 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <unistd.h>
+
+#include "gpio.h"
+
+static volatile int counter = 0;
+static volatile int oldcounter = 0;
+
+void interrupt (void) {
+    ++counter;
+}
+
+int main ()
+{
+    maa_init();
+    maa_gpio_context* x;
+
+    x = maa_gpio_init(6);
+    if (x == NULL) {
+        return 1;
+    }
+
+    maa_gpio_dir(x, MAA_GPIO_IN);
+
+    gpio_edge_t edge = MAA_GPIO_EDGE_BOTH;
+   
+    maa_gpio_isr(x, edge, &interrupt);
+
+    for(;;) {
+        if(counter != oldcounter) {
+          fprintf(stdout, "timeout counter == %d\n", counter);
+          oldcounter = counter;
+        }
+        // got to relieve our poor CPU!
+        sleep(1);
+    }
+
+    maa_gpio_close(x);
+
+    return MAA_SUCCESS;
+}
index e49213a..ba5d590 100644 (file)
  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
+#include "gpio.h"
 
 #include <stdlib.h>
 #include <fcntl.h>
-#include <stdio.h>
+#include <string.h>
 #include <unistd.h>
-
-#include "gpio.h"
+#include <poll.h>
 
 #define SYSFS_CLASS_GPIO "/sys/class/gpio"
 #define MAX_SIZE 64
+#define POLL_TIMEOUT
 
-static int
+static maa_result_t
 maa_gpio_get_valfp(maa_gpio_context *dev)
 {
     char bu[MAX_SIZE];
     sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
 
     if ((dev->value_fp = fopen(bu, "r+b")) == NULL) {
-        return 1;
+        return MAA_ERROR_INVALID_RESOURCE;
     }
 
-    return 0;
+    return MAA_SUCCESS;
 }
 
 maa_gpio_context*
@@ -67,6 +68,7 @@ maa_gpio_init_raw(int pin)
     int length;
 
     maa_gpio_context* dev = (maa_gpio_context*) malloc(sizeof(maa_gpio_context));
+    memset(dev, 0, sizeof(maa_gpio_context));
     dev->pin = pin;
 
     if ((export_f = fopen(SYSFS_CLASS_GPIO "/export", "w")) == NULL) {
@@ -80,6 +82,123 @@ maa_gpio_init_raw(int pin)
     return dev;
 }
 
+static maa_result_t
+maa_gpio_wait_interrupt(int fd)
+{
+    unsigned char c;
+    struct pollfd pfd;
+
+    // setup poll on POLLPRI
+    pfd.fd = fd;
+    pfd.events = POLLPRI;
+
+    // do an initial read to clear interupt
+    read (fd, &c, 1);
+
+    if (fd <= 0) {
+        return MAA_ERROR_INVALID_RESOURCE;
+    }
+
+    // Wait for it forever
+    int x = poll (&pfd, 1, -1);
+
+    // do a final read to clear interupt
+    read (fd, &c, 1);
+
+    return MAA_SUCCESS;
+}
+
+static void*
+maa_gpio_interrupt_handler(void* arg)
+{
+    maa_gpio_context* dev = (maa_gpio_context*) arg;
+    maa_result_t ret;
+
+    // open gpio value with open(3)
+    char bu[MAX_SIZE];
+    sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
+    dev->isr_value_fp = open(bu, O_RDONLY);
+
+    for (;;) {
+        ret = maa_gpio_wait_interrupt(dev->isr_value_fp);
+        if (ret == MAA_SUCCESS) {
+            dev->isr();
+        } else {
+           // we must have got an error code so die nicely
+            close(dev->isr_value_fp);
+           return;
+        }
+    }
+}
+
+maa_result_t
+maa_gpio_edge_mode(maa_gpio_context *dev, gpio_edge_t mode)
+{
+    if (dev->value_fp != NULL) {
+         dev->value_fp = NULL;
+    }
+
+    char filepath[MAX_SIZE];
+    snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/edge", dev->pin);
+
+    FILE *edge;
+    if ((edge= fopen(filepath, "w")) == NULL) {
+        fprintf(stderr, "Failed to open edge for writing!\n");
+        return MAA_ERROR_INVALID_RESOURCE;
+    }
+
+    char bu[MAX_SIZE];
+    int length;
+    switch(mode) {
+        case MAA_GPIO_EDGE_NONE:
+            length = snprintf(bu, sizeof(bu), "none");
+            break;
+        case MAA_GPIO_EDGE_BOTH:
+            length = snprintf(bu, sizeof(bu), "both");
+            break;
+        case MAA_GPIO_EDGE_RISING:
+            length = snprintf(bu, sizeof(bu), "rising");
+            break;
+        case MAA_GPIO_EDGE_FALLING:
+            length = snprintf(bu, sizeof(bu), "falling");
+            break;
+        default:
+            fclose(edge);
+            return MAA_ERROR_FEATURE_NOT_IMPLEMENTED;
+    }
+    fwrite(bu, sizeof(char), length, edge);
+
+    fclose(edge);
+    dev->value_fp = NULL;
+    return MAA_SUCCESS;
+}
+
+maa_result_t
+maa_gpio_isr(maa_gpio_context *dev, gpio_edge_t mode, void (*fptr)(void))
+{
+    maa_gpio_edge_mode(dev, mode);
+    dev->isr = fptr;
+    pthread_create (&dev->thread_id, NULL, maa_gpio_interrupt_handler, (void *) dev);
+
+    return MAA_SUCCESS;
+}
+
+maa_result_t
+maa_gpio_isr_exit(maa_gpio_context *dev)
+{
+    maa_result_t ret = MAA_SUCCESS;
+    maa_gpio_edge_mode(dev, MAA_GPIO_EDGE_NONE);
+
+    if (pthread_kill(dev->thread_id) != 0) {
+       ret = MAA_ERROR_INVALID_HANDLE;
+    }
+    if (close(dev->isr_value_fp) != 0) {
+       ret = MAA_ERROR_INVALID_PARAMETER;
+    }
+
+    return ret;
+}
+
 maa_result_t
 maa_gpio_mode(maa_gpio_context *dev, gpio_mode_t mode)
 {
@@ -211,6 +330,8 @@ maa_gpio_unexport(maa_gpio_context *dev)
     fwrite(bu, sizeof(char), length, unexport_f);
     fclose(unexport_f);
 
+    maa_gpio_isr_exit(dev);
+
     return MAA_SUCCESS;
 }