重頭寫一個v4l2的虛擬驅動_1,重頭v4l2虛擬_1
簡介
因為在qcom平台上和linux原生都是用的v4l2架構作為camera的驅動架構,所以本著學習記錄的筆記,做了如下文檔記錄。該文檔是學習《衛東山老師視頻教程第三期》的個人學習筆記,非常感謝老師的資料。該記錄僅供學習交流,如有侵犯到大家利益,還望海涵,請聯絡博主刪除。
註冊video_device
代碼示範
首先是驅動程式的入口、出口以及license,然後第一步就要分配,設定和註冊一個video_device結構(基於核心版本:linux-3.13.1)。
#include <linux/module.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/pci.h>#include <linux/random.h>#include <linux/version.h>#include <linux/mutex.h>#include <linux/videodev2.h>#include <linux/dma-mapping.h>#include <linux/interrupt.h>#include <linux/kthread.h>#include <linux/highmem.h>#include <linux/freezer.h>#include <media/videobuf-vmalloc.h>#include <media/v4l2-device.h>#include <media/v4l2-ioctl.h> #define DRIVER_NAME "myvivi_video" static struct video_device *myvivi_device;struct v4l2_devicemyvivi_v4l2_dev; static void myvivi_release(struct video_device *vdev){} static const struct v4l2_file_operations myvivi_fops = { .owner = THIS_MODULE,}; static int myvivi_init(void){ int error; /* 1. 分配一個video_device結構體 */ myvivi_device = video_device_alloc(); /* 2. 設定 */ strlcpy(myvivi_v4l2_dev.name, DRIVER_NAME, sizeof(myvivi_v4l2_dev.name)); error = v4l2_device_register(NULL, &myvivi_v4l2_dev); myvivi_device->v4l2_dev = &myvivi_v4l2_dev; myvivi_device->release = myvivi_release;myvivi_device->fops = &myvivi_fops; /* 2.1 */ error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1); return error;} static void myvivi_exit(void){ video_unregister_device(myvivi_device); video_device_release(myvivi_device);} module_init(myvivi_init);module_exit(myvivi_exit); MODULE_LICENSE("GPL");
代碼講解
首先是分配一個video_device_alloc來分配一個v4l2_device結構,接著填充設定這個結構的v4l2_dev參數、操作函數fops以及釋放函數release,在代碼中為了簡潔,所有fops和release函數都設定為了空函數。 接著註冊了該v4l2裝置,注意這裡的video_register_device函數,第一個參數表示我們需要註冊的video_device 結構,VFL_TYPE_GRABBER表示是映像採集裝置,最後的-1參數表示,註冊時候使用第一個閒置裝置名稱,如果/dev/video0已經被使用了,那麼這次註冊就是作為/dev/video1.如果參數不是-1,比如是5,那麼註冊的裝置檔案為:/dev/video5. 最後是驅動的出口,對應的操作也就是登出掉之前註冊的v4l2裝置,然後銷毀該裝置結構。 如此,一個最簡單的v4l2裝置就完成了。
注意
我使用的核心版本是linux-3.13.1,在該版本中,必須填充了v4l2_device結構的v4l2_dev參數、fops操作函數以及釋放函數release之後,調用video_register_device函數進行註冊才能註冊成功,否則驅動程式,會報出null 指標或者參數之類的錯誤。 同時在載入該驅動之前,需要保證核心中依賴的v4l2驅動模組已經是載入好了的,v4l2一般對應有如下幾個模組:videodev.ko videobuf-vmalloc.ko videobuf-core.ko v4l2-common.ko 驅動依賴哪些模組,可以使用sudo modinfo myvivi.ko 來查看到。
效果示範
最後insmod載入該驅動模組之後,會產生video*的裝置節點,效果如下:
接著安裝工具:sudo apt-get install xawtv 然後使用該工具後會提示說:
Failed to query video capabilities: Inappropriate ioctl for devicelibv4l2: error getting capabilities: Inappropriate ioctl for deviceFailed to query video capabilities: Inappropriate ioctl for devicelibv4l2: error getting capabilities: Inappropriate ioctl for devicevid-open-auto: failed to open a capture device at /dev/video0vid-open: could not find a suitable videodevno video grabber device available
表示為video裝置
在前面使用xawtv的時候,提示說沒有找到適合的video裝置,所以接著這一步就是將我們之前寫的v4l2註冊驅動表示為video裝置。
代碼講解
添加代碼如下:
...............static const struct v4l2_file_operations myvivi_fops = { .owner = THIS_MODULE, .ioctl = video_ioctl2, /* V4L2 ioctl handler */}; static int myvivi_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap){ strcpy(cap->driver, "myvivi"); strcpy(cap->card, "myvivi"); cap->version = 0x0001; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; /*表明是個視頻捕捉裝置和通過ioctl來訪問*/ return 0;} static const struct v4l2_ioctl_ops myvivi_ioctl_ops = { // 表示它是一個網路攝影機裝置 .vidioc_querycap = myvivi_vidioc_querycap,}; static int myvivi_init(void){ int error; /* 1. 分配一個video_device結構體 */ myvivi_device = video_device_alloc(); /* 2. 設定 */ strlcpy(myvivi_v4l2_dev.name, DRIVER_NAME, sizeof(myvivi_v4l2_dev.name)); error = v4l2_device_register(NULL, &myvivi_v4l2_dev); myvivi_device->v4l2_dev = &myvivi_v4l2_dev; myvivi_device->release = myvivi_release; myvivi_device->fops = &myvivi_fops; myvivi_device->ioctl_ops = &myvivi_ioctl_ops; ..............}..............
新加入初始化了一個myvivi_ioctl_ops操作函數結構,在其中填充了函數myvivi_vidioc_querycap,在應用程式中,會調用該函數來確實該驅動裝置類型。在該函數中V4L2_CAP_VIDEO_CAPTURE表示該驅動裝置是一個視頻擷取裝置,V4L2_CAP_STREAMING表示是通過ioctl來進行擷取資料。 同時在調用myvivi_vidioc_querycap函數的時候還需要調用到myvivi_fops中的ioctl函數,所以還需要填充該ioctl。這裡使用v4l2提供的ioctl標準操作函數video_ioctl2。
效果示範
接著繼續用xawtv工具來測試該驅動。效果如下:
這裡標明xawtv找到了/dev/video0是一個video裝置。
設定支援的video格式
前面一步中,xawtv已經識別到了該裝置驅動是video裝置,接著這一步在驅動中設定該驅動支援的video格式。
代碼講解
static struct v4l2_format myvivi_format;/* 列舉支援哪種格式 */static int myvivi_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,struct v4l2_fmtdesc *f){ if (f->index >= 1) /*我們這裡設定為只支援一種格式,所以設定f->index如果大於等於1,就返回錯誤*/return -EINVAL; strcpy(f->description, "4:2:2, packed, YUYV");f->pixelformat = V4L2_PIX_FMT_YUYV;return 0;} /* 返回當前所使用的格式 */static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,struct v4l2_format *f){memcpy(f, &myvivi_format, sizeof(myvivi_format));return (0);} /* 測試驅動程式是否支援某種格式 */static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv,struct v4l2_format *f){unsigned int maxw, maxh; if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)return -EINVAL; /*該裝置支援的最大寬度和高度*/maxw = 1024;maxh = 768; /* 調整format的最大最小width, height,和對齊 * 計算bytesperline, sizeimage */v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,&f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline =(f->fmt.pix.width * 16) >> 3; /*設定色彩深度為16*/f->fmt.pix.sizeimage =f->fmt.pix.height * f->fmt.pix.bytesperline; /*設定映像size*/ return 0;} static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,struct v4l2_format *f){int ret = myvivi_vidioc_try_fmt_vid_cap(file, NULL, f);if (ret < 0)return ret; memcpy(&myvivi_format, f, sizeof(myvivi_format)); return ret;} static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {// 表示它是一個網路攝影機裝置.vidioc_querycap = myvivi_vidioc_querycap, /* 用於列舉、獲得、測試、設定網路攝影機的資料的格式 */.vidioc_enum_fmt_vid_cap = myvivi_vidioc_enum_fmt_vid_cap,.vidioc_g_fmt_vid_cap = myvivi_vidioc_g_fmt_vid_cap,.vidioc_try_fmt_vid_cap = myvivi_vidioc_try_fmt_vid_cap,.vidioc_s_fmt_vid_cap = myvivi_vidioc_s_fmt_vid_cap, };
如上所示,一共增加了4個函數。 myvivi_vidioc_enum_fmt_vid_cap:用來設定支援的格式,這裡設定為只支援一種格式 V4L2_PIX_FMT_YUYV。 myvivi_vidioc_g_fmt_vid_cap:用來獲得支援的video格式資訊,格式資訊儲存在結構myvivi_format中。 myvivi_vidioc_try_fmt_vid_cap:用來測試傳入的某個video格式是否被該裝置驅動支援,同時設定了該格式下的video的width、height和image size等資訊。 myvivi_vidioc_s_fmt_vid_cap:首先用myvivi_vidioc_try_fmt_vid_cap,判斷傳入的某種格式是否支援,如果支援就儲存到myvivi_format結構中。
效果示範
同樣是用xawtv工具來示範下效果,不過這裡看不出太明顯。
這裡可以看到,xawtv有讀取到了一些格式相關資訊。