.步驟二: 註冊一個 /dev/video0,並用應用程式去讀取測試
我們延續上一篇文章中的 v4l2step1.c將之更改為 v4l2step2.c,並對 Makefile作相對應的修改
將MODULE_NAME = v4l2step1 改為 MODULE_NAME = v4l2step2,讓他可以順利 compile成功。
在這邊因為會呼叫到V4L2的 api v4l2_device_register,video_register_device,所以必須要加上必要的 header檔案
---
#include <linux/videodev2.h>
#include <media/videobuf2-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-common.h>
---
註冊一個 /dev/video0我們需要呼叫 v4l2_device_register 與 video_set_drvdata。
在v4l2_device_register部分,根據函式定義-int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
需要兩個結構,分別為 struct device 與 struct v4l2_device。
我宣告一個 ar struct來包含這兩個結構如下,
---
struct ar {
struct v4l2_device v4l2_dev;
struct video_device vdev;
};
---
並宣告一個靜態結構變數 static struct ar ardev;給 v4l2_device_register 使用。
在 vivi_init函式中宣告兩個 結構指標變數分別指到 video_device 與 v4l2_device 結構
並給予 v4l2_device結構中 name ,並呼叫函式 v4l2_device_register(NULL, v4l2_dev); 就可以註冊 V4L2 driver了。
接下來要註冊 /dev/video0 device,設定 ar 中 video_device 結構裡的 name ,並將 video_device 結構中v4l2_dev 指到 v4l2_dev 位址
設定 video_device 結構裡的 fops , ioctl_ops , flags 與 release
其中的 fops必須指到一個 v4l2_file_operations 結構,當應用程式將 /dev/video open , release, ioctl 時會呼叫相對應的函式
---
static const struct v4l2_file_operations ar_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open, // open /dev/video0 will enter here
.release = v4l2_fh_release, // close fd will enter here
.unlocked_ioctl = video_ioctl2, // need for ioctl
};
---
而其中的 ioctl_ops 則會指到一個 v4l2_ioctl_ops 結構,必須將支援的 ioctl command 函式implement出來。
舉例當 /dev/video0 被應用程式open後,並下達 VIDIOC_QUERYCAP ioctl指令時,就會呼叫 ar_querycap 函式。
目前在 ar_querycap 函式我填一些 struct v4l2_capability 需要的東西回去給應用程式。
----
static const struct v4l2_ioctl_ops ar_ioctl_ops = {
.vidioc_querycap = ar_querycap, // ioctl VIDIOC_QUERYCAP will enter here
};
----
填完 video_device 結構裡的 fops , ioctl_ops , flags 與 release後
就可以呼叫 video_register_device(&ar->vdev, VFL_TYPE_GRABBER, -1) 跟作業系統註冊一個 /dev/video0了
這邊為了驗證 應用程式與 driver 之間的互動,我寫個一個簡單的 應用程式來驗證,應用程式名稱為 vidtest.c
完整 source code如下:
--vidtest.c--
#include <errno.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
static int xioctl(int fd,int request, void *arg)
{
int r;
do r=ioctl(fd,request,arg);
while(-1 ==r && EINTR == errno);
return r;
}
int print_caps(int fd)
{
struct v4l2_capability caps={};
perror("print_caps enter");
if(-1 == xioctl(fd,VIDIOC_QUERYCAP,&caps))
{
perror("Querying cap fail");
return 1;
}
printf( "Driver Caps:\n"
" Driver: \"%s\"\n"
" Card: \"%s\"\n"
" Bus: \"%s\"\n"
" Version: %d.%d\n"
" Capabilities: %08x\n",
caps.driver,
caps.card,
caps.bus_info,
(caps.version>>16)&&0xff,
(caps.version>>24)&&0xff,
caps.capabilities);
perror("print_caps enter");
return 0;
}
int main()
{
int fd;
fd=open("/dev/video0",O_RDWR);
if(fd ==-1)
{
perror("opening video device fail");
return 1;
}
perror("opening video device success");
if(print_caps(fd))
{
return 1;
}else{
}
perror("close fd");
close(fd);
return 0;
}
-------
可以用 gcc vidtest.c -o vidtest 去編譯出 vidtest 執行檔。
驗證方式:
.將 v4l2step2.ko driver 載起來
Note: 注意,要載入一個/dev/video0 裝置確定 videodev.ko 有載入,因這兩個driver有相依性。
你可以用 lsmod 確定一下有無 videodev module,如果沒有就是表示你的 ubuntu kernel 在編譯時是將 videodev 設定為 module的
===
mark@R3600:~/v4l2$ lsmod
Module Size Used by
videodev 98259 0
v4l2_compat_ioctl32 17128 1 videodev
snd_hda_codec_hdmi 32530 1
....
===
這時候有兩個方法讓他載入,第一個方法是重新編譯 kernel( make menuconfig) 將 videodev 設定為 [一定載入]
第二個方法,則是切換目錄到 cd lib/modules/3.2.0-31-generic/kernel/drivers/media/video/ ,確定有 videodev.ko
使用指令 sudo modprobe videodev 手動載入他,再用 lsmod 確定一下有無載入 videodev.
.檢查 /dev/video0 是否存在,確定是否正確載入 driver.
.執行 vidtest 會秀出 ioctl VIDIOC_QUERYCAP 結果,如下:
---result--
mark@R3600:~/v4l2$ ./vidtest
opening video device success: Success
print_caps enter: Success
Driver Caps:
Driver: "Mark vivi Drive"
Card: "Mark test Card"
Bus: "Mark's Platform"
Version: 1.0
Capabilities: 01000001
print_caps enter: Success
close fd: Success
------------
完整 v4l2step2.c source code如下
---v4l2step2.c---
#include <linux/module.h>
#include <linux/videodev2.h>
#include <media/videobuf2-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-common.h>
//ar structure include v4l2_device
struct ar {
struct v4l2_device v4l2_dev;
struct video_device vdev;
};
//static ardev var
static struct ar ardev;
static int ar_querycap(struct file *file, void *priv,
struct v4l2_capability *vcap)
{
struct ar *ar = video_drvdata(file);
/*
fill
struct v4l2_capability {
__u8 driver[16];
__u8 card[32];
__u8 bus_info[32];
__u32 version;
__u32 capabilities;
__u32 device_caps;
__u32 reserved[3];
};
*/
strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "Mark test Card", sizeof(vcap->card));
strlcpy(vcap->bus_info, "Mark test BUS", sizeof(vcap->bus_info));
vcap->capabilities =V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; // report capabilities
printk(KERN_INFO "[mark]%s, %d, \n", __func__,__LINE__);
return 0;
}
/****************************************************************************
*
* Video4Linux Module functions
*
****************************************************************************/
static const struct v4l2_file_operations ar_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open, // open /dev/video0 will enter here
.release = v4l2_fh_release, // close fd will enter here
.unlocked_ioctl = video_ioctl2, // need for ioctl
};
static const struct v4l2_ioctl_ops ar_ioctl_ops = {
.vidioc_querycap = ar_querycap, // ioctl VIDIOC_QUERYCAP will enter here
};
static int __init vivi_init(void)
{
struct ar *ar;
struct v4l2_device *v4l2_dev;
int ret;
ar = &ardev;
v4l2_dev = &ar->v4l2_dev;
//init v4l2 name , version
strlcpy(v4l2_dev->name, "arv", sizeof(v4l2_dev->name));
v4l2_info(v4l2_dev, "Colour AR VGA driver %s\n", "0.0.1"); // output V4l2 info
ret = v4l2_device_register(NULL, v4l2_dev);
if (ret < 0) {
printk(KERN_INFO "Could not register v4l2_device\n");
return ret;
}
//setup video
strlcpy(ar->vdev.name, "Mark vivi Driver", sizeof(ar->vdev.name));
ar->vdev.v4l2_dev = v4l2_dev; // set V4l2_device address to video_device
ar->vdev.fops = &ar_fops; //v4l2_file_operations
ar->vdev.ioctl_ops = &ar_ioctl_ops; //v4l2_ioctl_ops
ar->vdev.release = video_device_release_empty;
set_bit(V4L2_FL_USE_FH_PRIO, &ar->vdev.flags);
video_set_drvdata(&ar->vdev, ar);
if (video_register_device(&ar->vdev, VFL_TYPE_GRABBER, -1) != 0) {
/* return -1, -ENFILE(full) or others */
printk(KERN_INFO "[mark]%s, %d, video_register_device FAIL \n", __func__,__LINE__);
ret = -ENODEV;
goto out_dev;
}
printk(KERN_INFO "[mark]%s, %d, module inserted\n", __func__,__LINE__);
return 0;
out_dev:
v4l2_device_unregister(&ar->v4l2_dev);
video_unregister_device(&ar->vdev);
return ret;
}
static void __exit vivi_exit(void)
{
struct ar *ar;
ar = &ardev;
printk(KERN_INFO "[mark]%s, %d, module remove\n", __func__,__LINE__);
video_unregister_device(&ar->vdev);
v4l2_device_unregister(&ar->v4l2_dev);
}
module_init(vivi_init);
module_exit(vivi_exit);
MODULE_DESCRIPTION("Mark test module");
MODULE_AUTHOR("Mark Yang");
MODULE_LICENSE("GPL");
-----------------
- Oct 22 Thu 2015 15:59
V4L2 driver Step by Step(part2)
文章標籤
全站熱搜
留言列表
發表留言