重頭寫一個v4l2的虛擬驅動_3,重頭v4l2虛擬_3
簡介
因為在qcom平台上和linux原生都是用的v4l2架構作為camera的驅動架構,所以本著學習記錄的筆記,做了如下文檔記錄。該文檔是學習《衛東山老師視頻教程第三期》的個人學習筆記,非常感謝老師的資料。該記錄僅供學習交流,如有侵犯到大家利益,還望海涵,請聯絡博主刪除。
poll/select
在前一篇中我們說到,應用程式和驅動通過select/poll機制來進行互動,從而做到不停地qbuffer和dequebuffer。所以驅動中還需要實現它的poll函數:
static unsigned int myvivi_poll(struct file *file, struct poll_table_struct *wait){ return videobuf_poll_stream(file, &myvivi_vb_vidqueue, wait);} static const struct v4l2_file_operations myvivi_fops = { ............. .poll = myvivi_poll, .............};
在videobuf_poll_stream函數中,如果隊列myvivi_vb_vidqueue中沒有資料,那麼進程就會隊列的第一個buffer上面,利用queuebuf->done進入休眠。
定時器類比資料
最後使用定時器來類比產生資料。 首先在驅動中添加定時器:
static struct timer_list myvivi_timer; static int myvivi_open(struct file *file){ ............ myvivi_timer.expires = jiffies + 1; add_timer(&myvivi_timer); ........}static int myvivi_close(struct file *file){ del_timer(&myvivi_timer); ...........} static int myvivi_init(void){ ............ init_timer(&myvivi_timer); myvivi_timer.function = myvivi_timer_function;}
另外還需要一個之前提到過的本地隊列和在myvivi_buffer_prepare做準備工作:
static struct list_head myvivi_vb_local_queue; static int myvivi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field){ /* 0. 設定videobuf */ vb->size = myvivi_format.fmt.pix.sizeimage; vb->bytesperline = myvivi_format.fmt.pix.bytesperline; vb->width = myvivi_format.fmt.pix.width; vb->height = myvivi_format.fmt.pix.height; vb->field = field; vb->state = VIDEOBUF_PREPARED; return 0;} static void myvivi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb){ vb->state = VIDEOBUF_QUEUED; /* 把videobuf放入本地一個隊列尾部 * 定時器處理函數就可以從本地隊列取出videobuf */ list_add_tail(&vb->queue, &myvivi_vb_local_queue);}static int myvivi_init(void){ ............. INIT_LIST_HEAD(&myvivi_vb_local_queue); .............}
在驅動入口函數中初始化了定時器myvivi_timer和本地隊列myvivi_vb_local_queue,接著在open函數中add_timer和在release函數中刪除定時器。然後在myvivi_buffer_prepare函數中填充了資料的大小、寬度之類資訊到videobuf中,將videobuf放到本地隊列myvivi_vb_local_queue中。
定時器函數
最後就是在定時器函數中來類比資料。
static void myvivi_timer_function(unsigned long data){ struct videobuf_buffer *vb; void *vbuf; struct timeval ts; /* 1. 構造資料: 從隊列頭部取出第1個videobuf, 填充資料*/ /* 1.1 從本地隊列取出第1個videobuf */ if (list_empty(&myvivi_vb_local_queue)) { goto out; } vb = list_entry(myvivi_vb_local_queue.next, struct videobuf_buffer, queue); /* Nobody is waiting on this buffer, return */ if (!waitqueue_active(&vb->done)) goto out; /* 1.2 填充資料 */ vbuf = videobuf_to_vmalloc(vb); /*得到它的虛擬位址*/ memset(vbuf, 0x88, vb->size); vb->field_count++; do_gettimeofday(&ts); vb->ts = ts; vb->state = VIDEOBUF_DONE; /* 1.3 把videobuf從本地隊列中刪除 */ list_del(&vb->queue); /* 2. 喚醒進程: 喚醒videobuf->done上的進程 */ wake_up(&vb->done); out: /* 3. 修改timer的逾時時間 : 30fps, 1秒裡有30幀資料 * 每1/30 秒產生一幀資料 */ mod_timer(&myvivi_timer, jiffies + HZ/30);}
之前有說到,myvivi_buffer_queue將queuebuf放入到了本地隊列myvivi_vb_local_queue中。定時器函數就從本地隊列中取出第一個queuebuf,如果本地隊列為空白,就直接退出,如果沒用進程通過poll被阻塞在vb->done上,也直接退出。 接著利用videobuf_to_vmalloc或得在本地隊列上得到的queuebuf虛擬位址,用最簡單的msmset將這一幀資料全部填充為0x88,大小為之前myvivi_buffer_prepare填充進來的vb->size = myvivi_format.fmt.pix.sizeimage; 在往後,累加已經產生的幀數和填充目前時間,最重要的設定當前的videobuf_buffer狀態為VIDEOBUF_DONE,否者被阻塞休眠的進程即使被喚醒了也不會正常返回。接著本地隊列上刪除掉當前的videobuf,然後喚醒poll上休眠等待的進程。 最後的HZ/30,表示一秒鐘定時器函數運行大約30次,也就是類比video的幀率為30fps
效果示範
最後也是使用xawtv來做效果示範如下:
通過修改定時器函數中memset中的填充資料值,就可以改變xawtv中顯示出來的顏色。