首先我们来看问题的背景,上层的encoder/decoder的工作流程是这样的:
Work procedure 1. Open the uio0 device to get the fd 2. Get the VPU register base address, work buffer and SRAM buffer address, and mmap them to user space in order to access them in user space directly 3. Clock on 4. Catch a lock 5. Issue command via writing VPU register to let VPU working 6. Wait interrupt via poll system call, there is a interrupt when the return value of poll > 0, it means one frame decode/encode done 7. Clock off 8. Unlock 9. Repeat from #3 10. Close fd
从上述流程中可以看到,用户态程序通过写寄存器让vpu硬件开始工作,然后就调用poll阻塞,直到有中断产生(通常表示一帧处理结束),重新唤醒这个进程,poll调用返回,我们来看这个poll机制在driver中是如何实现的。
APP调用poll,陷入内核sys_poll函数,它接着调用do_sys_poll,这个函数也在fs/select.c中,我们忽略其他代码,
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout) { …… poll_initwait(&table); …… fdcount = do_poll(nfds, head, &table, timeout); …… }
static unsigned int uio_poll(struct file *filep, poll_table *wait) { struct uio_listener *listener = filep->private_data; struct uio_device *idev = listener->dev; s32 event_count; if (!idev->info->irq) return -EIO; poll_wait(filep, &idev->wait, wait); event_count = atomic_read(&idev->event); if (listener->event_count != event_count) { listener->event_count = event_count; return POLLIN | POLLRDNORM; } return 0; }
static irqreturn_t uio_interrupt(int irq, void *dev_id) { struct uio_device *idev = (struct uio_device *)dev_id; irqreturn_t ret = idev->info->handler(irq, idev->info); if (ret == IRQ_HANDLED) uio_event_notify(idev->info); return ret; }
void uio_event_notify(struct uio_info *info) { struct uio_device *idev = info->uio_dev; atomic_inc(&idev->event); wake_up_interruptible(&idev->wait); kill_fasync(&idev->async_queue, SIGIO, POLL_IN); }
if (listener->event_count != event_count) { listener->event_count = event_count; uio_event_sync(); return POLLIN | POLLRDNORM; }