I am toggling the input into a GPIO line on my BeagleBone from high to low every 500 ms using an Atmel uC. I have registered a handler for this in my Linux Kernel Module, but the handler is not being called for some reason.
My module code is -
#define MY_MAJOR 89
#define MY_MINOR 0
#define GPIO_ANY_GPIO 54
// text below will be seen in 'cat /proc/interrupt' command
#define GPIO_ANY_GPIO_DESC "MyInterrupt"
#define GPIO_ANY_GPIO_DEVICE_DESC "mydevice"
volatile unsigned int val;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sid");
MODULE_DESCRIPTION("A Simple Character Device Driver module");
static int my_open(struct inode *, struct file *);
static ssize_t my_read(struct file *, char *, size_t, loff_t *);
static ssize_t my_write(struct file *, const char *, size_t, loff_t *);
static int my_close(struct inode *, struct file *);
static void my_tasklet_handler(unsigned long );
static char *msg=NULL;
unsigned long flags;
struct file_operations my_fops = {
read : my_read,
write : my_write,
open : my_open,
release : my_close,
owner : THIS_MODULE
};
struct cdev my_cdev;
short int irq_any_gpio = 0;
unsigned int last_interrupt_time = 0;
static uint64_t epochMilli;
unsigned int millis (void)
{
struct timeval tv ;
uint64_t now ;
do_gettimeofday(&tv) ;
now = (uint64_t)tv.tv_sec * (uint64_t)1000 + (uint64_t)(tv.tv_usec / 1000) ;
return (uint32_t)(now - epochMilli) ;
}
DECLARE_TASKLET(mytasklet, my_tasklet_handler, 0);
static void my_tasklet_handler(unsigned long flag)
{
tasklet_disable(&mytasklet);
printk(KERN_NOTICE "Interrupt for device was triggered !.\n");
tasklet_enable(&mytasklet);
}
static irqreturn_t r_irq_handler(int irq, void *dev_id, struct pt_regs *regs) {
unsigned int interrupt_time = millis();
if (interrupt_time - last_interrupt_time < 1000)
return IRQ_HANDLED;
last_interrupt_time = interrupt_time;
local_irq_save(flags);
tasklet_schedule(&mytasklet);
local_irq_restore(flags);
return IRQ_HANDLED;
}
void r_int_config(void) {
struct timeval tv ;
do_gettimeofday(&tv) ;
epochMilli = (uint64_t)tv.tv_sec * (uint64_t)1000 + (uint64_t)(tv.tv_usec / 1000) ;
if (gpio_request(GPIO_ANY_GPIO, GPIO_ANY_GPIO_DESC)) {
printk("GPIO request failure: %s\n", GPIO_ANY_GPIO_DESC);
return;
}
if ( (irq_any_gpio = gpio_to_irq(GPIO_ANY_GPIO)) < 0 ) {
printk("GPIO to IRQ mapping failure %s\n", GPIO_ANY_GPIO_DESC);
return;
}
printk(KERN_NOTICE "Mapped int %d\n", irq_any_gpio);
if (request_irq(irq_any_gpio,
(irq_handler_t ) r_irq_handler,
IRQF_TRIGGER_HIGH,
GPIO_ANY_GPIO_DESC,
GPIO_ANY_GPIO_DEVICE_DESC)) {
printk("Irq Request failure\n");
return;
}
return;
}
void r_int_release(void) {
free_irq(irq_any_gpio, GPIO_ANY_GPIO_DEVICE_DESC);
gpio_free(GPIO_ANY_GPIO);
return;
}
int init_module(void)
{
dev_t devno;
unsigned int count;
int err;
printk("<1>Hello World\n");
devno = MKDEV(MY_MAJOR, MY_MINOR);
register_chrdev_region(devno, 1, "mydevice");
cdev_init(&my_cdev, &my_fops);
my_cdev.owner = THIS_MODULE;
count = 1;
err = cdev_add(&my_cdev, devno, count);
printk("'mknod /dev/mydevice c %d 0'.\n", MY_MAJOR);
msg = (char *)kmalloc(8, GFP_KERNEL);
if (msg !=NULL)
printk("malloc allocator address: 0x%x\n", msg);
if (err < 0)
{
printk("Device Add Error\n");
return -1;
}
r_int_config();
return 0;
}
void cleanup_module(void)
{
dev_t devno;
printk("<1> Goodbye\n");
devno = MKDEV(MY_MAJOR, MY_MINOR);
r_int_release();
if (msg){
/* release the malloc */
kfree(msg);
}
tasklet_kill(&mytasklet);
unregister_chrdev_region(devno, 1);
cdev_del(&my_cdev);
}
static int my_open(struct inode *inod, struct file *fil)
{
int major;
int minor;
major = imajor(inod);
minor = iminor(inod);
printk("\n*****Some body is opening me at major %d minor %d*****\n",major, minor);
return 0;
}
static ssize_t my_read(struct file *filp, char *buff, size_t len, loff_t *off)
{
int major;
short count;
major = MAJOR(filp->f_dentry->d_inode->i_rdev);
count = copy_to_user(buff, msg, len);
printk("*****Some body is reading me at major %d*****\n",major);
printk("*****Number of bytes read :: %d *************\n",len);
return 0;
}
static ssize_t my_write(struct file *filp, const char *buff, size_t len, loff_t *off)
{
int minor;
short count;
memset(msg, 0, 100);
count =copy_from_user(msg, buff, len);
minor = MAJOR(filp->f_dentry->d_inode->i_rdev);
printk("*****Some body is writing me at major %d*****\n",minor);
printk("*****Number of bytes written :: %d **********\n",len);
return 0;
}
static int my_close(struct inode *inod, struct file *fil)
{
#if 0
printk(KERN_ALERT "Device closed\n");
return 0;
#endif
int minor;
minor = MAJOR(fil->f_dentry->d_inode->i_rdev);
printk("*****Some body is closing me at major %d*****\n",minor);
return 0;
}
Upon doing - #insmod interrupt.ko I get -
[ 76.594543] Hello World
[ 76.597137] Mapped int 214
Showing that i am getting an irq for the gpio.
But now when I start toggling the input into this gpio pin, the interrupt handler doesn't get called and the message "interrupt received" is not being displayed.
Also, cat /proc/interrupts gives me -
214: 0 GPIO MyInterrupt
Showing that the interrupt handler is not being called.
How do I solve this ? What's causing the problem?