detecting interrupt on GPIO in kernel module - linux

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 GPIO 54
#define GPIO_INT_NAME "gpio_int"
#define GPIO_HIGH gpio_get_value(GPIO)
#define GPIO_LOW (gpio_get_value(GPIO) == 0)
short int irq_any_gpio = 0;
int count =0;
enum { falling, rising } type;
static irqreturn_t r_irq_handler(int irq, void *dev_id)
{
count++;
printk(KERN_DEBUG "interrupt received (irq: %d)\n", irq);
if (irq == gpio_to_irq(GPIO))
{
type = GPIO_LOW ? falling : rising;
if(type == falling)
{
printk("gpio pin is low\n");
}
else
printk("gpio pin is high\n");
}
return IRQ_HANDLED;
}
void r_int_config(void) {
if (gpio_request(GPIO, GPIO_INT_NAME ))
{
printk("GPIO request failure: %s\n", GPIO_INT_NAME );
return;
}
if ( (irq_any_gpio = gpio_to_irq(GPIO)) < 0 ) {
printk("GPIO to IRQ mapping failure %s\n",GPIO_INT_NAME );
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_INT_NAME, NULL))
{
printk("Irq Request failure\n");
return;
}
return;
}
void r_int_release(void) {
free_irq(gpio_to_irq(GPIO), NULL);
gpio_free(GPIO);;
return;
}
int init_module(void)
{
printk("<1>Hello World\n");
r_int_config();
return 0;
}
On calling insmod interrupt_test.ko, i get the following message
[ 76.594543] Hello World
[ 76.597137] Mapped int 214
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).
How do I solve this ? What's causing the problem?

IMO your module lacks some basic kernel module programming prerequisites. At first, you have no includes. For toggling GPIO pins use the following.
#include <linux/init.h> // initialization macros
#include <linux/module.h> // dynamic loading of modules into the kernel
#include <linux/kernel.h> // kernel stuff
#include <linux/gpio.h> // GPIO functions/macros
#include <linux/interrupt.h> // interrupt functions/macros
You should use MODULE_LICENSE("Your license") otherwise the kernel may get tainted and your initialization and exit functions should be marked with __init and __exit macros like:
void __init init_module(void) {...}
void __exit r_int_release(void) {...} // exit code should cleanup all stuff
Furthermore the kernel needs to know which functions to call on module load and exit. Therefore use:
module_init(init_module); // Do some better naming
module_exit(r_int_release);
Now to the IRQ. IRQs must be assigned to GPIOs and you must specify an IRQ handler. So what you need to do within your modules init code is:
static unsigned int yourGPIO = 49; // This is Pin 23 on the P9 header
static unsigned int irqNumber;
static irq_handler_t irqHandler(unsigned int irq, void *dev_id, struct pt_regs *regs);
// This is the IRQ Handler prototype
static int __init init_module(void)
{
int result = 0;
gpio_request(yourGPIO, "fancy label"); // Request a GPIO pin from the driver
// 'yourGPIO' is expected to be an unsigned int, i.e. the GPIO number
gpio_direction_input(yourGPIO); // Set GPIO as input
gpio_set_debounce(yourGPIO, 50); // Set a 50ms debounce, adjust to your needs
gpio_export(yourGPIO); // The GPIO will appear in /sys/class/gpio
...
Without a GPIO pin requested from the driver, the following will fail
...
irqNumber = gpio_to_irq(yourGPIO); // map your GPIO to an IRQ
result = request_irq(irqNumber, // requested interrupt
(irq_handler_t) irqHandler, // pointer to handler function
IRQF_TRIGGER_RISING, // interrupt mode flag
"irqHandler", // used in /proc/interrupts
NULL); // the *dev_id shared interrupt lines, NULL is okay
return result;
}
Last but not least implement your handler function and load the module.
For more reference I recommend reading Derek Molloys Blog on derekmolloy.ie

Related

What is the relationship between an Interrupt Service Routine and a Device Driver that registers to an IRQ?

I have recently be reading up on how to write Linux Device Drivers and came across a Device Driver which basically "handles" IRQ1 (a keyboard event). The code itself is shown below:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <asm/io.h>
/* This function services keyboard interrupts */
irq_handler_t irq_handler (int irq, void *dev_id, struct pt_regs *regs) {
static unsigned char scancode;
/* Read keyboard status */
scancode = inb (0x60);
if ((scancode == 0x01) || (scancode == 0x81)) {
printk("You pressed Esc !\n");
}
return (irq_handler_t) IRQ_HANDLED;
}
/* Initialize the module and Register the IRQ handler */
static int __init keybrd_int_register(void) {
int result;
/* Request IRQ 1, the keyboard IRQ */
result = request_irq (
1,
(irq_handler_t) irq_handler,
IRQF_SHARED,
"keyboard_stats_irq",
(void *)(irq_handler)
);
if (result) {
printk(KERN_INFO "can't get shared interrupt for keyboard\n");
}
return result;
}
/* Remove the interrupt handler */
static void __exit keybrd_int_unregister(void) {
free_irq(1, (void *)(irq_handler)); /* can't pass NULL, this is a shared interrupt handler! */
}
MODULE_LICENSE ("GPL");
module_init(keybrd_int_register);
module_exit(keybrd_int_unregister);
I would like to understand whether this Device Driver is considered an "Interrupt Service Routine"? Correct me if I'm wrong, but the flow of events is as follows:
Press key on Keyboard
Keyboard Triggers IRQ1
Operating System looks up ALL the Interrupt Service Routines "registered" to IRQ1 (which includes the Device Driver we wrote above) and triggers the code in these Interrupt Service Routines (including the code in Device Driver above).
Does this mean tat the Device Driver IS the Interrupt Service Routine?
A follow up question would be, how would you actually write a real Keyboard Driver (not one that just naively prints out some value)? How do you tell the Operating System that a particular key was pressed and that it should inform the current running application that this key was pressed? What does the C code look like for this?

Writing interrupt handler for external interrupt (IRQ 0 - 5) of ARM processor

I am trying to write an interrupt handler for LS1021A external interrupt IRQ5 using NXP QorIQ SDK 2.0. I have created a kernel module to handle the interrupt but the reset_irq function is always returning -22 (Invalid argument).
This is my code:
static unsigned int irqNum;
static irq_handler_t irq5_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){
printk(KERN_INFO "Interrupt received\n");
return (irq_handler_t) IRQ_HANDLED;
}
static int __init irq_init(void){
int result = 0;
irqNum = 201; // IRQ number of IRQ5
result = request_irq(irqNum, (irq_handler_t) irq5_handler, IRQF_TRIGGER_RISING, "irq5_handler", NULL);
printk(KERN_INFO "request_irq result is %d\n", result);
return result;
}
module_init(irq_init);
Is creating a kernel module the right approach to handle the interrupt?
Do I need to perform any actions like initializing the processor pin before requesting the interrupt (request_irq)?

How to handle an interrupt from the GPIO Expander(PCA953X)

I use a GPIO Expander like PCA9575.
But I don't know how to handle an interrupt from the expander..
From the gpio-pca953x.c, the following code is the ISR for the expander.
static irqreturn_t pca953x_irq_handler(int irq, void *devid)
{
struct pca953x_chip *chip = devid;
uint16_t pending;
uint16_t level;
pending = pca953x_irq_pending(chip);
if (!pending)
return IRQ_HANDLED;
do {
level = __ffs(pending);
handle_nested_irq(level + chip->irq_base);
pending &= ~(1 << level);
} while (pending);
return IRQ_HANDLED;
}
If Interrupt is activated from GPIO(Input) pin 3 of expander, this routine jumps into the GPIO pin 3 service routine from this ISR. Isn't it right?
If so, I have the following question..
How to register the GPIO Pin 3 service routine??
If not, How to handle the the GPIO Pin 3 service routine??
Thanks for your reading.

Why doesn't this call to `poll` block correctly on a sysfs device attribute file?

I have a simple sysfs device attribute which shows up under my sysfs directory, and on a call to read returns the value of a kernelspace variable. I want to call poll on this attribute to allow my userspace thread to block until the value shown by the attribute changes.
My problem is that poll doesn't seem to block on my attribute -- it keeps returning POLLPRI even though the value shown by the attribute does not change. In fact, I have no calls at all to sysfs_notify in the kernel module, yet the userspace call poll still does not block.
Perhaps I should be checking for a return value of something other than POLLPRI -- but according to the documentation in the Linux kernel, sysfs_poll should return POLLERR|POLLPRI:
/* ... When the content changes (assuming the
* manager for the kobject supports notification), poll will
* return POLLERR|POLLPRI ...
*/
Is there something I'm forgetting to do with poll?
The device attribute is located at: /sys/class/vilhelm/foo/blah.
I load a kernel module called foo which registers a device, and creates a class and this device attribute.
The userspace application called bar spawns a thread that calls poll on the device attribute, checking for POLLPRI.
If poll returns a positive number, POLLPRI has been returned.
Use fopen and fscan to read the value from the device attribute file.
If the value is 42, print FROM THREAD!!!.
The problem is that the message is printed nonstop when I'm expecting the call to poll to block indefinitely. The problem must lie with poll (the other calls successfully acquire the correct value of 42 from the device attribute).
userspace app - bar.c:
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <unistd.h>
static void handle_val(unsigned val, FILE *fp);
void * start_val_service(void *arg);
int main(void){
pthread_t val_serv;
pthread_create(&val_serv, NULL, &start_val_service, NULL);
pthread_exit(NULL);
return 0;
}
static void handle_val(unsigned val, FILE *fp){
switch(val){
case 42:
{
printf("FROM THREAD!!!\n");
break;
}
default:
break;
}
}
void * start_val_service(void *arg){
struct pollfd fds;
fds.fd = open("/sys/class/vilhelm/foo/blah", O_RDONLY);
fds.events = POLLPRI;
do{
int ret = poll(&fds, 1, -1);
if(ret > 0){
FILE *fp = fopen("/sys/class/vilhelm/foo/blah", "r");
unsigned val;
fscanf(fp, "%u", &val);
handle_val(val, fp);
fclose(fp);
}
}while(1);
close(fds.fd);
pthread_exit(NULL);
}
kernel module - foo.c:
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
static dev_t foo_dev;
static struct class *vilhelm;
static unsigned myvar = 42;
static ssize_t unsigned_dev_attr_show(struct device *dev, struct device_attribute *attr, char *buf);
struct unsigned_device_attribute{
struct device_attribute dev_attr;
unsigned *ptr;
};
static struct unsigned_device_attribute unsigned_dev_attr_blah = {
.dev_attr = __ATTR(blah, S_IRUGO, unsigned_dev_attr_show, NULL)
};
static int __init foo_init(void){
int retval = 0;
printk(KERN_INFO "HELLO FROM MODULE 1");
if(alloc_chrdev_region(&foo_dev, 0, 1, "vilhelm") < 0){
printk(KERN_ERR "foo: unable to register device");
retval = -1;
goto out_alloc_chrdev_region;
}
vilhelm = class_create(THIS_MODULE, "vilhelm");
if(IS_ERR(vilhelm)){
printk(KERN_ERR "foo: unable to create device class");
retval = PTR_ERR(vilhelm);
goto out_class_create;
}
struct device *foo_device = device_create(vilhelm, NULL, foo_dev, NULL, "foo");
if(IS_ERR(foo_device)){
printk(KERN_ERR "foo: unable to create device file");
retval = PTR_ERR(foo_device);
goto out_device_create;
}
unsigned_dev_attr_blah.ptr = &myvar;
retval = device_create_file(foo_device, &unsigned_dev_attr_blah.dev_attr);
if(retval){
printk(KERN_ERR "foo: unable to create device attribute files");
goto out_create_foo_dev_attr_files;
}
return 0;
out_create_foo_dev_attr_files:
device_destroy(vilhelm, foo_dev);
out_device_create:
class_destroy(vilhelm);
out_class_create:
unregister_chrdev_region(foo_dev, 1);
out_alloc_chrdev_region:
return retval;
}
static void __exit foo_exit(void){
printk(KERN_INFO "BYE FROM MODULE 1");
device_destroy(vilhelm, foo_dev);
class_destroy(vilhelm);
unregister_chrdev_region(foo_dev, 1);
}
static ssize_t unsigned_dev_attr_show(struct device *dev, struct device_attribute *attr, char *buf){
struct unsigned_device_attribute *tmp = container_of(attr, struct unsigned_device_attribute, dev_attr);
unsigned value = *(tmp->ptr);
return scnprintf(buf, PAGE_SIZE, "%u\n", value);
}
module_init(foo_init);
module_exit(foo_exit);
MODULE_LICENSE("GPL");
See also
Using the Linux sysfs_notify call
To quote some more from the comment you quoted:
Once poll/select indicates that the value has changed, you
need to close and re-open the file, or seek to 0 and read again.
But you do nothing with fds.fd.
Also, do a dummy read() before calling poll();
any newly opened file is considered changed.

Kernel Module to Ethernet Packet Echo

I develop a linux kernel module to re-transmit some ethernet packet (do the echo). THe packet arrives, i check the ethernet destination address, and if it is for me, i re-transmit. If not i do nothing.
I used dev_pack_eth to define my protocol handler to recieve all ethernet packet (EHT_P_ALL) and dev_queue_xmit to transmit the skb buff received.
It works, the echo is functional but...
Sometimes, very often. the kernel crashs and i don't know why.
When i re-transmit the packet, i return NET_RX_Sucess.
When i don't re-transmit i use kfree_skb to free the skb buff received and return NET_RX_DROP.
I think the problem my be on this issues. Can you help me?
If needed i could post the kernel module code.
Best Regards!
------------Edit:Code added--------
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
#include <linux/skbuff.h>
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
#include <asm-generic/types.h>
/*Buscar as interfaces de rede*/
struct net_device *dev_eth0;
struct net_device *dev_eth1;
int contador;
static struct packet_type hook; /* Initialisation routine */
void handler_add_config (void);
void handler_remove(void);
void print_mac_hdr(struct ethhdr *eth);
static int hook_func( struct sk_buff *skb)
{
struct ethhdr *eth;
struct ethhdr aux;
eth= eth_hdr(skb)
print_mac_hdr(eth);
/*If destination isn't the same that dev_addr, the packet is not for me: do nothing*/
if(memcmp(eth->h_dest,skb->dev->dev_addr,ETH_ALEN)!=0)
{
printk("Não são iguais!!!\n");
}
else
{
/*Swap addr*/
memcpy(&(aux.h_dest),eth->h_dest,ETH_ALEN);
memcpy(eth->h_dest,eth->h_source,ETH_ALEN);
memcpy(eth->h_source,&(aux.h_dest),ETH_ALEN);
/*Re build ther hearders*/
skb->data = (unsigned char *)skb->mac_header;
skb->len += ETH_HLEN;
skb->pkt_type = PACKET_OUTGOING;
/*Send*/
if(dev_queue_xmit(skb)!= NET_XMIT_SUCCESS)
{
printk("Erro na transmissão\n");
}
else
{
printk("Trama retransmitida com sucesso\n");
return NET_RX_SUCCESS;
}
}
kfree_skb(skb);
return NET_RX_DROP;
}
/*Print eth headers*/
void print_mac_hdr(struct ethhdr *eth)
{
printk("Destino: %02x:%02x:%02x:%02x:%02x:%02x \n",eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]);
printk("Origem: %02x:%02x:%02x:%02x:%02x:%02x\n",eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5]);
printk("Proto: 0x%04x\n",ntohs(eth->h_proto));
}
/*Configure Protocol Handler*/
void handler_add_config (void)
{
hook.type = htons(ETH_P_ALL);
hook.func = (void *)hook_func;
hook.dev = NULL;
dev_add_pack(&hook);
printk("Handler Protocol adicionado!!!!\n");
}
/*Unregist protocol handler*/
void handler_remove(void)
{
dev_remove_pack(&hook);
printk("Handler Protocol removido!!!!\n");
synchronize_net();/*Sincronizar a rede!*/
}
/*Init module and protocol handler*/
static int __init hook_init(void)
{
printk("Hello:I'm the hook module!!!!\n");
contador =0;
dev_eth0=dev_get_by_name(&init_net,"eth0");
dev_eth1=dev_get_by_name(&init_net,"eth1");
handler_add_config();
return 0;
}
/*Remove module and protocol handler*/
static void __exit hook_exit(void)
{
printk("Hook module says Goodbye!!!!!\n");
handler_remove();
}
module_init(hook_init);
module_exit(hook_exit);
MODULE_LICENSE("GPL");
i think when u are returning return NET_RX_DROP; it causes problem because there are basically return types in hooks are ...
Return Code Meaning
NF_DROP Discard the packet.
NF_ACCEPT Keep the packet.
NF_STOLEN Forget about the packet.
NF_QUEUE Queue packet for userspace.
NF_REPEAT Call this hook function again.
& u are returning NET_RX_DROP so try to use NF_DROP.
Look at af_x25.c in net/x25 for a sample implementation of the same where they return 0 even on a drop. BTW didn't understand why are you incrementing skb->len when all you are doing is swapping the mac addresses ? I.e why the need to rebuild hdrs in that sense? Am i missing something here?
You are probably crashing because you are freeing the slngle copy of sk_buff by calling kfree_skb(skb);

Resources