gpio-test updated for interrupt tests v0.2
authorPeter Henn <Peter.Henn@web.de>
Fri, 2 Jan 2009 13:05:17 +0000 (14:05 +0100)
committerPeter Henn <Peter.Henn@web.de>
Fri, 2 Jan 2009 13:05:17 +0000 (14:05 +0100)
- Reorganize gpio initialisation
- no longer need to use gpio output
- push global structure onto the heap memory
- use tasklet and trigger it from interrupt service routine
- add test-gpio target to Makefile

Makefile
gpio_test.c

index 42afee9..b5ab670 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -125,6 +125,14 @@ else # We've got a kernel with seperate output, copy the config, and use O=
        $(MAKE) -C $(KSRC) M=$(PWD) MODVERDIR=$(MODVERDIR)) O=$(PWD)/tmp modules
 endif
 
+test-gpio: modules
+       rmmod lp || true
+       rmmod ppdev || true
+       rmmod gpio_test || true
+       rmmod gpio_parport || true
+       insmod gpio_parport.ko
+       insmod gpio_test.ko gpi=254 irq=7
+
 install: modules
        for mod; $(list-m); do modprobe -r $mod || true; done
        install -d $(INSTALLDIR)
index 90d0892..f4a5cf7 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
+#include <linux/workqueue.h>
 #include <linux/gpio.h>
 
 
 // MODULE PARAMETERS 
 //*****************************************************************************
 #define DRVNAME                    "gpio_test"   /* name of the driver */
-#define DRIVER_VERSION            "0.1"   /* helps identifing different versions of that driver */
-#define LABEL               NULL
+#define DRIVER_VERSION            "0.3"   /* helps identifing different versions of that driver */
+#define LABEL               DRVNAME
 
-static unsigned int gpi;
-static unsigned int gpo;
-static unsigned int val;
-static unsigned int irq = -1;
+static int gpi = -1;
+static int gpo = -1;
+static unsigned int val = 0;
+static int irq = -1;
 
-typedef struct {
+struct gpio_test {
         int out_cansleep;
         int in_cansleep;
         int irq;
         int cnt;
-} gpio_t_struct;
+        struct tasklet_struct work_tasklet;
+};
 
-static gpio_t_struct gt;
+static struct gpio_test *gt;
+
+
+static void gpio_tasklet(unsigned long data)
+{
+       struct gpio_test *g = (struct gpio_test *)data;
+       pr_info("%s: Tasklet triggered: %d\n", DRVNAME,  g->cnt);
+}
 
 static irqreturn_t gpio_test_irq_handler(int irq, void *dev_id)
 {
-        
+  struct gpio_test *g = (struct gpio_test *)dev_id;
   // todo use dev_id to get pointer to own driver structure data;
   // container can be memory init with GPK_KERNEL 
-        if (irq == gt.irq) {
-               gt.cnt++;
+        if (irq == g->irq) {
+               g->cnt++;
+               tasklet_hi_schedule(&g->work_tasklet);
+#if 0
                val = !val;
-               if (!gt.in_cansleep) {
-                       pr_info("%s: GPIO #%d in value %d with counter %d\n", DRVNAME, gpi, gpio_get_value(gpi), gt.cnt);
+               if (!g->in_cansleep) {
+                       pr_info("%s: GPIO #%d in value %d with counter %d\n", DRVNAME, gpi, gpio_get_value(gpi), g->cnt);
                }
-               if (!gt.out_cansleep) {
+               if ((gpo >= 0) && !g->out_cansleep) {
                        gpio_set_value(gpo, !val);
                }
+#endif
        }
         return IRQ_HANDLED;
 }
 
+
+
 /*
 ** just set here the value
 */
 static int __init gpio_test_init(void)
 {
         int err;
-        err = gpio_is_valid(gpo);
-       if (err<0) {
-               pr_err("%s: GPIO #%d for output is not usable\n", DRVNAME, gpo);
-               return err;
-       }
-       //ToDo: Test, how a text might be used as LABEL
-       err = gpio_request(gpo, LABEL);
-       if (err<0) {
-               pr_err("%s: GPIO #%d for output is can not be requested\n", DRVNAME, gpo);
-               return err;
-       }
-       gt.out_cansleep = gpio_cansleep(gpo);
-       pr_info("%s: GPIO #%d for output %s sleep\n", DRVNAME, gpo, ((gt.out_cansleep>0) ? "can" : "will not"));
-       err = gpio_direction_output(gpo, val);
-       if (err<0) {
-               pr_err("%s: GPIO #%d output direction is not possible\n", DRVNAME, gpo);
-                gpio_free(gpo);
-               return err;
+       gt = kzalloc(sizeof(*gt), GFP_KERNEL);
+       if (!gt)
+               return -ENOMEM;
+
+       /* ******* GPIO INPUT ******* */
+       if (gpi < 0) {
+               pr_err("%s: GPI input required\n", DRVNAME);
+               kfree(gt);
+               return -ENODEV;
        }
-
-        err = gpio_is_valid(gpi);
+       err = gpio_is_valid(gpi);
        if (err<0) {
                pr_err("%s: GPIO #%d for inout is not usable\n", DRVNAME, gpi);
-                gpio_free(gpo);
+               kfree(gt);
                return err;
        }
        err = gpio_request(gpi, LABEL);
        if (err<0) {
                pr_err("%s: GPIO #%d for input is can not be requested\n", DRVNAME, gpi);
-                gpio_free(gpo);
+               kfree(gt);
                return err;
        }
-       gt.in_cansleep = gpio_cansleep(gpi);
-       pr_info("%s: GPIO #%d for input %s sleep\n", DRVNAME, gpi, ((gt.in_cansleep>0) ? "can" : "will not"));
-       gt.irq = gpio_to_irq(gpi);
-       if (gt.irq<0) {
+       gt->in_cansleep = gpio_cansleep(gpi);
+       pr_info("%s: GPIO #%d for input %s sleep\n", DRVNAME, gpi, ((gt->in_cansleep>0) ? "can" : "will not"));
+       tasklet_init(&gt->work_tasklet,
+                    (void (*)(unsigned long))gpio_tasklet,
+                    (unsigned long)gt);
+
+       /* ******* GPIO INTERRUPT ******* */
+       gt->irq = gpio_to_irq(gpi);
+       if (gt->irq<0) {
                pr_info("%s: GPIO #%d IRQ is not supported\n", DRVNAME, gpi);
-               gt.irq = irq;
-               if (gt.irq >= 0) {
-                       pr_info("%s: we force interrupt to IRQ %d\n", DRVNAME, gt.irq);
+               gt->irq = irq;
+               if (gt->irq >= 0) {
+                       pr_info("%s: we force interrupt to IRQ %d\n", DRVNAME, gt->irq);
                }
        } else {
-               pr_info("%s: GPIO #%d IRQ %d is supported\n", DRVNAME, gpi, gt.irq);
+               pr_info("%s: GPIO #%d IRQ %d is supported\n", DRVNAME, gpi, gt->irq);
        }
-       /* gpio_direction_output must be call from task context, we use insmod as task context here */
+       /* gpio_direction_input must be call from task context, we use insmod as task context here */
        err = gpio_direction_input(gpi);
        if (err<0) {
                pr_err("%s: GPIO #%d input direction is not possible\n", DRVNAME, gpi);
-                gpio_free(gpo);
+               tasklet_kill(&gt->work_tasklet);
                 gpio_free(gpi);
+               kfree(gt);
                return err;
        }
-        gt.cnt=0;
        /* register irq handler */
+        gt->cnt=0;
        // ToDo: Test with other flag types: IRQF_SHARED, IRQF_TRIGGER_*
-       if (gt.irq >= 0) {
-               err = request_irq(gt.irq, gpio_test_irq_handler, IRQF_TRIGGER_NONE, "gpio_test_handler", NULL);
+       if (gt->irq >= 0) {
+               err = request_irq(gt->irq, gpio_test_irq_handler, IRQF_TRIGGER_NONE, "gpio_test_handler", (void *)gt);
+               if (err<0) {
+                       pr_err("%s: GPIO #%d IRQ %d can not be requested, got %d\n", DRVNAME, gpi, gt->irq, err);
+                       gt->irq = -1;
+               }
+       }
+
+
+       /* ******* GPIO OUTPUT ******* */
+       if (gpo >= 0) {
+               err = gpio_is_valid(gpo);
+               if (err<0) {
+                       pr_err("%s: GPIO #%d for output is not usable\n", DRVNAME, gpo);
+                       if (gt->irq >= 0) free_irq(gt->irq, (void *)gt);
+                       tasklet_kill(&gt->work_tasklet);
+                       gpio_free(gpi);
+                       kfree(gt);
+                       return err;
+               }
+               err = gpio_request(gpo, LABEL);
+               if (err<0) {
+                       pr_err("%s: GPIO #%d for output is can not be requested\n", DRVNAME, gpo);
+                       if (gt->irq >= 0) free_irq(gt->irq, (void *)gt);
+                       tasklet_kill(&gt->work_tasklet);
+                       gpio_free(gpi);
+                       kfree(gt);
+                       return err;
+               }
+               gt->out_cansleep = gpio_cansleep(gpo);
+               pr_info("%s: GPIO #%d for output %s sleep\n", DRVNAME, gpo, ((gt->out_cansleep>0) ? "can" : "will not"));
+               err = gpio_direction_output(gpo, val);
                if (err<0) {
-                       pr_err("%s: GPIO #%d IRQ %d can not be requested, got %d\n", DRVNAME, gpi, gt.irq, err);
-                       gt.irq = -1;
+                       pr_err("%s: GPIO #%d output direction is not possible\n", DRVNAME, gpo);
+                       if (gt->irq >= 0) free_irq(gt->irq, (void *)gt);
+                       tasklet_kill(&gt->work_tasklet);
+                       gpio_free(gpi);
+                       gpio_free(gpo);
+                       kfree(gt);
+                       return err;
                }
        }
         return 0;
@@ -143,14 +188,18 @@ device_initcall(gpio_test_init);
 */
 static void __exit gpio_test_exit(void)
 {
-        if (gt.out_cansleep) {
-               gpio_set_value_cansleep(gpo, !val);
-       } else {
-               gpio_set_value(gpo, !val);
-       }
-       if (gt.irq >= 0) free_irq(gt.irq, NULL);
-        gpio_free(gpo);
+       if (gt->irq >= 0) free_irq(gt->irq, (void *)gt);
+       tasklet_kill(&gt->work_tasklet);
         gpio_free(gpi);
+        if (gpo >= 0) {
+               if (gt->out_cansleep) {
+                      gpio_set_value_cansleep(gpo, !val);
+               } else {
+                      gpio_set_value(gpo, !val);
+               }
+               gpio_free(gpo);
+       }
+       kfree(gt);
 }
 module_exit(gpio_test_exit);