Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linux device driver buffering strategy

Lets assume that I have an external device that is constantly pushing data into a small buffer in my driver. I'm using a wait queue where an interrupt handler wakes up a waiting user process (similar to LDD (3rd edition) - Implementing a Handler).

irq_handler_t irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{

 flag = 1;
 wake_up_interruptible(&wq);

return  IRQ_HANDLED;
}

ssize_t my_read(struct file *dev, char __user *buf, size_t count, loff_t *f_pos)

{

        wait_event_interruptible(wq, flag != 0);
        flag = 0;
        copy_to_user(usr_buf, drv_buf, count);

}


/***********************User program***********************/

while(1)
{    
    read(fid, buffer, size);

    //do stuff with data

}

The user program calls read and it waits till the interrupt gets new data from the external device. Since the external device may push data at a faster than this code can execute, what mechanisms can I use to ensure data is not overwritten before the user program copies it? Would a ring buffer like structure work here? Its not clear how to implement it.

Thanks

like image 665
jsynccurry Avatar asked Jan 29 '17 01:01

jsynccurry


Video Answer


1 Answers

Yes, a ring buffer would work.

You simply have to fill the buffer from the interrupt handler and you will read it from the my_read callback.

A really naive and really really inefficient implementation could be (untested):

static irqreturn_t irq_handler(int irq, void *dev_id)
{
        struct my_dev *dev = dev_id;

        buf[buf_wr] = read_device(dev);
        buf_wr++;

        if (buf_wr >= BUFSIZE)
                buf_wr = 0;

        wake_up(&wq);
        return IRQ_HANDLED;
}

static ssize_t my_read(struct file *file, char __user *ubuf,
                             size_t sz, loff_t *ppos)
{
        int n, ret;

        ret = wait_event_interruptible(wq,
                                buf_wr != buf_rd);
        if (ret)
                return ret;

        n = buf_wr - buf_rd;
        if (n < 0)
               n += BUFSIZE;

        n = min(count, n);
        ret = copy_to_user(ubuf, buf, n);
        buf_rd += n;

        if (buf_rd >= BUFSIZE)
                buf_rd -= BUFSIZE;

        if (ret)
                return ret;

        *ppos += n;
        return 1;
}

You may also want to use DMA or mmap or both to get something more efficient.

like image 74
Alexandre Belloni Avatar answered Oct 05 '22 23:10

Alexandre Belloni