linuxlinux-kernelkernelinterrupt

Reading a device from a 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 an 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 the current segment descriptor
        fs = get_fs();
        // Set the segment descriptor associated to kernel space
        set_fs(get_ds());
        // Read the file
        f->f_op->read(f, buf, 128, &f->f_pos);
        // Restore the segment descriptor
        set_fs(fs);
        // See what we read from the 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 the system clock
    omap_dm_timer_set_source(timer_ptr, OMAP_TIMER_SRC_SYS_CLK);

    // Set prescaler 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 the 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

    // Set up 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 access 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 an interrupt routine?

P.S.: It's running on an Overo (ARM), the Linux distribution is Poky, and the kernel version is 3.5.7.


Solution

  • After reading the answer of Vivek S 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.