重頭寫一個v4l2的虛擬驅動_1,重頭v4l2虛擬_1

來源:互聯網
上載者:User

重頭寫一個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有讀取到了一些格式相關資訊。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.