vivi虚拟摄像头驱动代码分析记录---学习记录

一、安装摄像头应用程序(初体验vivi)

我们可以在线获取安装包,好像是高版本的ubuntu已经去掉了vivi这个功能。

//在保证自己的ubuntu能够联网的情况下执行这条命令:
1、linux@linux:~$ sudo apt-get install xawtv
//安装成功之后
2、linux@linux:~$ sudo modprobe -i vivi
//执行上面这条命令之后,可以通过ls命令查看dev目录下自动生成了video0这个字符驱动设备
linux@linux:~$ ls /dev/video0 -al
crw-rw----+ 1 root video 81, 0 Dec 10 04:14 /dev/video0
3、linux@linux:~$ xawtv
//执行这个摄像头应用程序xawtv

执行完xawtv摄像头应用程序之后就会出现这样的现象:
在这里插入图片描述

二、vivi虚拟摄像头驱动代码分析

如果下载了linux内核源码在内核顶层目录下的驱动文件drivers/media/video/vivi.c,如果你手中没有现成的内核源码,可以通过网页在线浏览,一般浏览器搜索lxr就会出现linux或者android中的,具体网址:https://lxr.missinglinkelectronics.com/linux/
1、分配一个对象

/**通过模式入口函数vivi_create_instance,根据驱动的框架,主要有四步:分配一个对象、
对象初始化、注册对象、注销对象。要想掌握摄像头的驱动框架,就必须确认这四个步骤具体
怎么实现的,然后根据别人的框架来写自己的驱动**/
static int __init vivi_create_instance(int inst)
 {
   struct vivi_dev *dev; /****这个其实不是分配的对象,而是已经被封
   装过的video_device,所以查看必须vivi_dev这个结构体如何实现的****/
   struct video_device *vfd;
   struct v4l2_ctrl_handler *hdl;
   struct vb2_queue *q;                                                             
   int ret;

 dev = kzalloc(sizeof(*dev), GFP_KERNEL);    //开辟空间分配内存
    if (!dev)
        return -ENOMEM;

    snprintf(dev->v4l2_dev.name,sizeof(dev->v4l2_dev.name),              "%s-%03d", VIVI_MODULE_NAME, inst);
    ret = v4l2_device_register(NULL, &dev->v4l2_dev);
    /*v4l2_device_registe对video_device进行初始化继承了device对象,
    这个就是再/sys下创建文件或者目录供用户查看*/
    if (ret)
        goto free_dev;  
//上边注册函数vivi_create_instance第一个结构体在这儿实现
struct vivi_dev {       
    struct list_head           vivi_devlist;
    struct v4l2_device     v4l2_dev;
    struct v4l2_ctrl_handler   ctrl_handler;

    /* controls */
    struct v4l2_ctrl       *brightness;   /*这些往下都是亮度等等的一些图像参数*/
    struct v4l2_ctrl       *contrast;
    struct v4l2_ctrl       *saturation;
    struct v4l2_ctrl       *hue;
    struct {
        /* autogain/gain cluster */
        struct v4l2_ctrl       *autogain;
        struct v4l2_ctrl       *gain;
    };
    struct v4l2_ctrl       *volume;
    struct v4l2_ctrl       *button;
    struct v4l2_ctrl       *boolean;                                                      
    struct v4l2_ctrl       *int32;
    struct v4l2_ctrl       *int64;
    struct v4l2_ctrl       *menu;
    struct v4l2_ctrl       *string;
struct v4l2_ctrl       *bitmask;
struct video_device        *vfd;   //注册一个对象的函数
vfd =video_device_alloc()    //给注册的对象分配内存
*vfd = vivi_template;   //初始化对象,直接赋值的vivi_template
}

2、对象初始化

static struct video_device vivi_template = {
    .name         = "vivi",       //摄像头名字比如:vivi或者ov3640
    .fops         = &vivi_fops,  //操作方法
    .ioctl_ops    = &vivi_ioctl_ops,   //ioctl函数的操作方法
    .release      = video_device_release,  //释放资源的函数
                                                                                          
    .tvnorms              = V4L2_STD_525_60,
    .current_norm         = V4L2_STD_NTSC_M,
};

3、注册对象

video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); /***ideo_register_device这个是注册对象的函数,发现在这个函数里只调用了下面的函数
__video_register_device****/
__video_register_device(vdev, type, nr, 1, vdev->fops->owner); //V4L2-dev.c
{
   switch (type) {    //type代表本次分配的设备类型
	case VFL_TYPE_GRABBER:   // VFL_TYPE_GRABBER摄像头设备
		name_base = "video";   
		break;
	case VFL_TYPE_VBI:   // VFL_TYPE_VBI有线电视设备
		name_base = "vbi";
		break;
	case VFL_TYPE_RADIO:  // VFL_TYPE_RADIO声音机的设备
		name_base = "radio";
		break;
	case VFL_TYPE_SUBDEV:
		name_base = "v4l-subdev";
		break;
	default:
		printk(KERN_ERR "%s called with unknown type: %d\n",
		       __func__, type);
		return -EINVAL;
	}
    switch (type) {   //用来指定次设备号的
	case VFL_TYPE_GRABBER:   //0-63
		minor_offset = 0;
		minor_cnt = 64;
		break;
	case VFL_TYPE_RADIO:  //64-127
		minor_offset = 64;
		minor_cnt = 64;
		break;
	case VFL_TYPE_VBI: //  224 - 257
		minor_offset = 224;
		minor_cnt = 32;
		break;
	default:
		minor_offset = 128;  // 128-191
		minor_cnt = 64;
		break;
	}
vdev->cdev = cdev_alloc();  //注册字符设备驱动
vdev->cdev->ops = &v4l2_fops;  /*回调vivi_fops,当vivi_fops里的ioctl函数调用的时候回调vivi_ioctl_ops*/
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);

4、注销对象

//注销的函数为video_unregister_device
void video_unregister_device(struct video_device *vdev)
{
	/* Check if vdev was ever registered at all */
	if (!vdev || !video_is_registered(vdev))
		return;

	mutex_lock(&videodev_lock);
	/* This must be in a critical section to prevent a race with v4l2_open.
	 * Once this bit has been cleared video_get may never be called again.
	 */
	clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
	mutex_unlock(&videodev_lock);
	device_unregister(&vdev->dev);
}

三、实现摄像头驱动的主要ioctl函数有这些

V4l2_ioctl_ops这个结构体里对这些主要的ioctl函数进行初始化

static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
/* vidioc_querycap就是表明身份,你是谁?比如:我是一个摄像头设备*/
	.vidioc_querycap 		= vidioc_querycap,
	
	/* 用于列举、获得、测试、设置摄像头的数据的格式 */
 	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
	.vidioc_g_fmt_vid_cap   = vidioc_g_fmt_vid_cap,
	.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
	.vidioc_s_fmt_vid_cap   = vidioc_s_fmt_vid_cap, 
	
	/* 缓冲区操作: 申请/查询/放入队列/取出队列 */
	.vidioc_reqbufs 	    = vidioc _reqbufs,
	.vidioc_querybuf 		= vidioc_querybuf,
	.vidioc_qbuf 			= vidioc_qbuf,
	.vidioc_dqbuf 	 		= vidioc_dqbuf,

	/*启动/停止*/
	.vidioc_streamon 		= vidioc_streamon,
	.vidioc_streamoff 		= vidioc_streamoff,
}

待续……

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值