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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With