#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(>->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(>->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(>->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(>->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(>->work_tasklet);
+ gpio_free(gpi);
+ gpio_free(gpo);
+ kfree(gt);
+ return err;
}
}
return 0;
*/
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(>->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);