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

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.

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)?

detecting interrupt on GPIO in kernel module

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

Reading a device from kernel interrupt

I'm aiming to have a kernel module that reads a device (ADC) at every T seconds.
I already have a working module that calls a interrupt each T seconds and I also have another module that reads a user space file (the ADC, for instance), which I got from this example. Both work fine separately.
The problem is that when I try to open and read any file from my interrupt routine the module crashes
[ 80.636932] Kernel panic - not syncing: Fatal exception in interrupt
My code is something like this:
static irqreturn_t timer_irq_handler(int irq, void *dev_id)
{
uint16_t value;
// reset the timer interrupt status
omap_dm_timer_write_status(timer_ptr, OMAP_TIMER_INT_OVERFLOW);
omap_dm_timer_read_status(timer_ptr);
omap_dm_timer_set_load(timer_ptr, 1, 0xFFFFFFFF - (time * gt_rate);
value = read_channel();
return IRQ_HANDLED;
}
uint16_t read_channel()
{
// Create variables
struct file *f;
char buf[128];
mm_segment_t fs;
int i;
// Init the buffer with 0
for(i=0;i \< 128;i++)
buf[i] = 0;
f = filp_open(device, O_RDONLY, 0);
if(f == NULL)
printk(KERN_ALERT "filp_open error!!.\n");
else{
// Get current segment descriptor
fs = get_fs();
// Set segment descriptor associated to kernel space
set_fs(get_ds());
// Read the file
f->f_op->read(f, buf, 128, &f->f_pos);
// Restore segment descriptor
set_fs(fs);
// See what we read from file
printk(KERN_INFO "buf:%s\n",buf);
}
filp_close(f,NULL);
return 0;
}
static int __init mq7_driver_init(void)
{
int ret = 0;
struct clk *gt_fclk;
timer_ptr = omap_dm_timer_request();
if(timer_ptr == NULL){
printk("No more gp timers available, bailing out\n");
return -1;
}
// set the clock source to system clock
omap_dm_timer_set_source(timer_ptr, OMAP_TIMER_SRC_SYS_CLK);
// set prescalar to 1:1
omap_dm_timer_set_prescaler(timer_ptr, 0);
// figure out what IRQ our timer triggers
timer_irq = omap_dm_timer_get_irq(timer_ptr);
// install our IRQ handler for our timer
ret = request_irq(timer_irq, timer_irq_handler, IRQF_DISABLED | IRQF_TIMER , "mq7_driver", timer_irq_handler);
if(ret){
printk("mq7_driver: request_irq failed (on irq %d), bailing out\n", timer_irq);
return ret;
}
// get clock rate in Hz
gt_fclk = omap_dm_timer_get_fclk(timer_ptr);
gt_rate = clk_get_rate(gt_fclk);
// set preload, and autoreload
// we set it to the clock rate in order to get 1 overflow every 3 seconds
omap_dm_timer_set_load(timer_ptr, 1, 0xFFFFFFFF - (5 * gt_rate)); // dobro do tempo
// setup timer to trigger our IRQ on the overflow event
omap_dm_timer_set_int_enable(timer_ptr, OMAP_TIMER_INT_OVERFLOW);
// start the timer!
omap_dm_timer_start(timer_ptr);
// get acess to gpio
ret = gpio_request(gpio, "mq7_driver sck");
if (ret) {
printk(KERN_ALERT "gpio_request %d failed\n",gpio);
gpio_free(gpio);
return -1;
}
gpio_direction_output(gpio, 0);
// Print adc number into address string
sprintf(device,"/sys/class/hwmon/hwmon0/device/in%d_input",adc);
return 0;
}
What is wrong with reading a file from a interrupt routine?
P.S.: It's running on a Overo (ARM), the distro is Poky and kernel version is 3.5.7.
After reading the answer of #VivekS in this post I took a look at Linux Device Drivers, chapter 10, which states:
A handler can't transfer data to or from user space, because it
doesn't execute in the context of a process. Handlers also cannot do
anything that would sleep, such as calling wait_event, allocating
memory with anything other than GFP_ATOMIC, or locking a semaphore.
Finally, handlers cannot call schedule.

Poll() to monitor a pin on BeagleBone Black. Continuous output even though pin not connected.

I want to trigger an event whenever there is a rising edge on one of the pins of Beaglebone Black.
Problem is, even though I havent connected that pin to anything, the output just goes on printing, interrupt occured, interrupt occured. I came across question Interrupts in Beaglebone
on stackoverflow and tried to follow the steps. There was a link to a Program which implements the functionality.
I read about poll() and I made slight changes in the program since I want to monitor just one pin. The changed code is :
int main(int argc, char **argv, char **envp)
{
struct pollfd fdset[1]; // fdset[2] changed to fdset[1] since I will monitor just 1 pin
int nfds = 1; // nfds changed from 2 to 1
int gpio_fd, timeout, rc;
char *buf[MAX_BUF];
unsigned int gpio;
int len;
if (argc < 2) {
printf("Usage: gpio-int <gpio-pin>\n\n");
printf("Waits for a change in the GPIO pin voltage level or input on stdin\n");
exit(-1);
}
gpio = atoi(argv[1]);
gpio_export(gpio);
gpio_set_dir(gpio, 0);
gpio_set_edge(gpio, "rising");
gpio_fd = gpio_fd_open(gpio);
timeout = POLL_TIMEOUT;
while (1) {
memset((void*)fdset, 0, sizeof(fdset));
fdset[0].fd = gpio_fd; // This is the pin to be monitored
fdset[0].events = POLLIN;
//fdset[1].fd = gpio_fd; // commented since I do not need this
//fdset[1].events = POLLPRI;
rc = poll(fdset, nfds, timeout);
if (rc < 0) {
printf("\npoll() failed!\n");
return -1;
}
if (rc == 0) {
printf(".");
}
if (fdset[0].revents & POLLIN) {
len = read(fdset[0].fd, buf, MAX_BUF);
printf("\npoll() GPIO %d interrupt occurred\n", gpio);
}
// ****Commented block****
//if (fdset[0].revents & POLLIN) {
// (void)read(fdset[0].fd, buf, 1);
// printf("\npoll() stdin read 0x%2.2X\n", (unsigned int) buf[0]);
//}
fflush(stdout);
}
gpio_fd_close(gpio_fd);
return 0;
}
Running Angstrom on Beaglebone black.
https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
If the pin can be configured as interrupt-generating interrupt
and if it has been configured to generate interrupts (see the
description of "edge"), you can poll(2) on that file and
poll(2) will return whenever the interrupt was triggered. If
you use poll(2), set the events POLLPRI and POLLERR. If you
use select(2), set the file descriptor in exceptfds. After
poll(2) returns, either lseek(2) to the beginning of the sysfs
file and read the new value or close the file and re-open it
to read the value.
You have not set events POLLPRI and POLLERR.
struct pollfd fdset[1];
memset((void*)fdset, 0, sizeof(fdset));
fdset[0].fd = fd;
fdset[0].events = POLLPRI | POLLERR;
...
poll()
...
lseek(fdset[0].fd, 0, SEEK_SET);
const ssize_t rc = read(fdset[0].fd, buf, MAX_BUF);
The above works on a BeagleBone Black Rev. C running Debian with linux 3.8.13-bone47.
Since you "havent connected that pin to anything", if it isn't internally tied low or high then it's just a floating input, which could cause the apparent interrupts you're seeing.

Resources