从vivi学习V4L2架构(一):vivi驱动初始化

6 篇文章 0 订阅
5 篇文章 0 订阅

基于linux kernel-3.0版本代码分析

drivers\media\video\vivi.c

1.struct vivi_dev结构体重点关注对象

核心结构体struct vivi_dev, 其中内嵌的三个结构体需要重点关注struct v4l2_device v4l2_dev, struct v4l2_ctrl_handler ctrl_handler, struct video_device *vfd

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 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;

	spinlock_t                 slock;
	struct mutex		   mutex;

	/* various device info */
	struct video_device        *vfd;

	struct vivi_dmaqueue       vidq;

	/* Several counters */
	unsigned 		   ms;
	unsigned long              jiffies;
	unsigned		   button_pressed;

	int			   mv_count;	/* Controls bars movement */

	/* Input Number */
	int			   input;

	/* video capture */
	struct vivi_fmt            *fmt;
	unsigned int               width, height;
	struct vb2_queue	   vb_vidq;
	enum v4l2_field		   field;
	unsigned int		   field_count;

	u8 			   bars[9][3];
	u8 			   line[MAX_WIDTH * 4];
};

2.驱动初始化

vivi_init();

        ret = vivi_create_instance(i);

static int __init vivi_create_instance(int inst)
{
	struct vivi_dev *dev;
	struct video_device *vfd;
	struct v4l2_ctrl_handler *hdl;
	struct vb2_queue *q;
	int ret;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL); //给struct vivi_dev申请空间
	if (!dev)
		return -ENOMEM;

	snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
			"%s-%03d", VIVI_MODULE_NAME, inst);     //给v4l2_dev.name赋值为vivi-000
	ret = v4l2_device_register(NULL, &dev->v4l2_dev); //注册v4l2_dev,其实就是初始化v4l2_dev的一些成员变量
	if (ret)
		goto free_dev;

	dev->fmt = &formats[0];
	dev->width = 640;
	dev->height = 480;
	hdl = &dev->ctrl_handler;
	v4l2_ctrl_handler_init(hdl, 11);  //初始化ctrl_handler,并给11个v4l2_ctrl分配空间
    //下面就是对11个struct v4l2_ctrl类型的ctrl进行初始化
	dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
	dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
	dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_CONTRAST, 0, 255, 1, 16);
	dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_SATURATION, 0, 255, 1, 127);
	dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
			V4L2_CID_HUE, -128, 127, 1, 0);
	dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
	dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
	dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
	dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
	dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
	dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
	if (hdl->error) {
		ret = hdl->error;
		goto unreg_dev;
	}
	dev->v4l2_dev.ctrl_handler = hdl; //v4l2_dev.ctrl_handler指向hdl

	/* initialize locks */
	spin_lock_init(&dev->slock);

	/* initialize queue */
	q = &dev->vb_vidq; 
	memset(q, 0, sizeof(dev->vb_vidq)); //初始化vb2_queue
	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //设置vb2_queue的类型是捕获类型
	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
	q->drv_priv = dev;
	q->buf_struct_size = sizeof(struct vivi_buffer); //一帧数据所需要的buffer大小,如果赋值为0的话就使用vb2_buffer类型大小
	q->ops = &vivi_video_qops; //对于vb2_queue的一些回调函数
	q->mem_ops = &vb2_vmalloc_memops; //内存分配回收mmap等的回调函数

	vb2_queue_init(q); //这里其实就是检查关键成员变量有没有赋值,也就是上面初始化的。然后初始化一些vb2_queue里面的一些链表头,spin lock, waitqueue的头

	mutex_init(&dev->mutex);

	/* init video dma queues */
	INIT_LIST_HEAD(&dev->vidq.active); //初始化vivi_dmaqueue
	init_waitqueue_head(&dev->vidq.wq);

	ret = -ENOMEM;
	vfd = video_device_alloc(); //为video_device申请空间
	if (!vfd)
		goto unreg_dev;

	*vfd = vivi_template; //初始化video_device主要将成员变量name, fops, ioctl_ops, release赋值
	vfd->debug = debug; //debug变量用于打印调试信息
	vfd->v4l2_dev = &dev->v4l2_dev; //video_device的v4l2_dev执行struct v4l2_device
	set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); //设置video_device使用V4L2_FL_USE_FH_PRIO

	/*
	 * Provide a mutex to v4l2 core. It will be used to protect
	 * all fops and v4l2 ioctls.
	 */
	vfd->lock = &dev->mutex;

	ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); //注册video_device后面会详细分析
	if (ret < 0)
		goto rel_vdev;

	video_set_drvdata(vfd, dev); //video_device->dev->p->driver_data执向vivi_dev

	/* Now that everything is fine, let's add it to device list */
	list_add_tail(&dev->vivi_devlist, &vivi_devlist);

	if (video_nr != -1)
		video_nr++;

	dev->vfd = vfd; //vivi_dev->vfd指向video_device
	v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
		  video_device_node_name(vfd));
	return 0;

rel_vdev:
	video_device_release(vfd);
unreg_dev:
	v4l2_ctrl_handler_free(hdl);
	v4l2_device_unregister(&dev->v4l2_dev);
free_dev:
	kfree(dev);
	return ret;
}

3.video_device注册函数video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);分析

static inline int __must_check video_register_device(struct video_device *vdev,
		int type, int nr)
{
	return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
}

int __video_register_device(struct video_device *vdev, int type, int nr,
		int warn_if_nr_in_use, struct module *owner)
{
	int i = 0;
	int ret;
	int minor_offset = 0;
	int minor_cnt = VIDEO_NUM_DEVICES;
	const char *name_base;

	/* A minor value of -1 marks this video device as never
	   having been registered */
	vdev->minor = -1;

	/* the release callback MUST be present */
	WARN_ON(!vdev->release);
	if (!vdev->release)
		return -EINVAL;

	/* v4l2_fh support */
	spin_lock_init(&vdev->fh_lock);
	INIT_LIST_HEAD(&vdev->fh_list);

	/* Part 1: check device type */
	switch (type) {
	case VFL_TYPE_GRABBER:
		name_base = "video"; //这里的name_base就是后面会在/dev/创建/dev/videoX
		break;
	case VFL_TYPE_VBI:
		name_base = "vbi";
		break;
	case 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;
	}

	vdev->vfl_type = type;
	vdev->cdev = NULL;
	if (vdev->v4l2_dev) { 
		if (vdev->v4l2_dev->dev) //vivi的v4l2_dev->dev为NULL, 下面不走
			vdev->parent = vdev->v4l2_dev->dev;
		if (vdev->ctrl_handler == NULL)
			vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; //将video_device->ctrl_handler指向v4l2_device->ctrl_handler也就是前面初始化的
		/* If the prio state pointer is NULL, then use the v4l2_device
		   prio state. */
		if (vdev->prio == NULL)
			vdev->prio = &vdev->v4l2_dev->prio; //走这里
	}

... ... //中间省略了点代码

	/* Pick a device node number */
	mutex_lock(&videodev_lock);
	nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
	if (nr == minor_cnt)
		nr = devnode_find(vdev, 0, minor_cnt);
	if (nr == minor_cnt) {
		printk(KERN_ERR "could not get a free device node number\n");
		mutex_unlock(&videodev_lock);
		return -ENFILE;
	}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
	/* 1-on-1 mapping of device node number to minor number */
	i = nr;
#else
	/* The device node number and minor numbers are independent, so
	   we just find the first free minor number. */
	for (i = 0; i < VIDEO_NUM_DEVICES; i++)
		if (video_device[i] == NULL)
			break;
	if (i == VIDEO_NUM_DEVICES) {
		mutex_unlock(&videodev_lock);
		printk(KERN_ERR "could not get a free minor\n");
		return -ENFILE;
	}
#endif
    //上面代码就是找一个没有使用的次设备号
	vdev->minor = i + minor_offset;
	vdev->num = nr;
	devnode_set(vdev);

	/* Should not happen since we thought this minor was free */
	WARN_ON(video_device[vdev->minor] != NULL);
	vdev->index = get_index(vdev); //由于vdev->parent为NULL这个函数直接返回0
	mutex_unlock(&videodev_lock);

	/* Part 3: Initialize the character device */
	vdev->cdev = cdev_alloc(); //为cdev分配空间
	if (vdev->cdev == NULL) {
		ret = -ENOMEM;
		goto cleanup;
	}
	vdev->cdev->ops = &v4l2_fops; //cdev的ops赋值,这个v4l2_fops很关键,应用和这个ops直接交互。通过这个ops调用到具体的video_device中的fops和ioctrl_ops
	vdev->cdev->owner = owner;
	ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); //注册字符设备
	if (ret < 0) {
		printk(KERN_ERR "%s: cdev_add failed\n", __func__);
		kfree(vdev->cdev);
		vdev->cdev = NULL;
		goto cleanup;
	}

	/* Part 4: register the device with sysfs */
	vdev->dev.class = &video_class;
	vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
	if (vdev->parent)
		vdev->dev.parent = vdev->parent;
	dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
	ret = device_register(&vdev->dev); //在sysfs下面注册设备
	if (ret < 0) {
		printk(KERN_ERR "%s: device_register failed\n", __func__);
		goto cleanup;
	}
	/* Register the release callback that will be called when the last
	   reference to the device goes away. */
	vdev->dev.release = v4l2_device_release;

	if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
		printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
			name_base, nr, video_device_node_name(vdev));

	/* Increase v4l2_device refcount */
	if (vdev->v4l2_dev)
		v4l2_device_get(vdev->v4l2_dev);
    //vivi驱动v4l2_device->mdev为NULL,所以下面entity注册没有走
#if defined(CONFIG_MEDIA_CONTROLLER)
	/* Part 5: Register the entity. */
	if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
	    vdev->vfl_type != VFL_TYPE_SUBDEV) {
		vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
		vdev->entity.name = vdev->name;
		vdev->entity.v4l.major = VIDEO_MAJOR;
		vdev->entity.v4l.minor = vdev->minor;
		ret = media_device_register_entity(vdev->v4l2_dev->mdev,
			&vdev->entity);
		if (ret < 0)
			printk(KERN_WARNING
			       "%s: media_device_register_entity failed\n",
			       __func__);
	}
#endif
	/* Part 6: Activate this minor. The char device can now be used. */
	set_bit(V4L2_FL_REGISTERED, &vdev->flags); //看上面英文注释,就是现在可以使用/dev/videoX这个字符设备了
	mutex_lock(&videodev_lock);
	video_device[vdev->minor] = vdev; //把video_device放入到以次设备为数组下标的全局数组video_device
	mutex_unlock(&videodev_lock);

	return 0;

cleanup:
	mutex_lock(&videodev_lock);
	if (vdev->cdev)
		cdev_del(vdev->cdev);
	devnode_clear(vdev);
	mutex_unlock(&videodev_lock);
	/* Mark this video device as never having been registered. */
	vdev->minor = -1;
	return ret;
}
EXPORT_SYMBOL(__video_register_device);

4.v4l2_ctrl的初始化

现在回过头来看一下v4l2_ctrl的初始化,以brigtness为例

  

dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
          V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);

struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
			const struct v4l2_ctrl_ops *ops,
			u32 id, s32 min, s32 max, u32 step, s32 def)
{
	const char *name;
	enum v4l2_ctrl_type type;
	u32 flags;

    //下面v4l2_ctrl_fill主要做了1.根据id给name、type、flag有必要也会给min,max,step,def也赋值
	v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
	if (type == V4L2_CTRL_TYPE_MENU) {
		handler_set_err(hdl, -EINVAL);
		return NULL;
	}
	return v4l2_ctrl_new(hdl, ops, id, name, type,
				    min, max, step, def, flags, NULL, NULL);
}


static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
			const struct v4l2_ctrl_ops *ops,
			u32 id, const char *name, enum v4l2_ctrl_type type,
			s32 min, s32 max, u32 step, s32 def,
			u32 flags, const char * const *qmenu, void *priv)
{
	struct v4l2_ctrl *ctrl;
	unsigned sz_extra = 0;

... ... //省略了些条件值判断代码

	if (type == V4L2_CTRL_TYPE_BUTTON)
		flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
	else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
		flags |= V4L2_CTRL_FLAG_READ_ONLY;
	else if (type == V4L2_CTRL_TYPE_STRING)
		sz_extra += 2 * (max + 1);

    //给v4l2_ctrl分配空间
	ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
	if (ctrl == NULL) {
		handler_set_err(hdl, -ENOMEM);
		return NULL;
	}


	INIT_LIST_HEAD(&ctrl->node);
	ctrl->handler = hdl; //重点ctrl->handler指向了v4l2_ctrl_handler
	ctrl->ops = ops; //ops赋值为vivi_ctrl_ops
	ctrl->id = id;   //id为V4L2_CID_BRIGHTNESS
	ctrl->name = name;  //"Brightness"
	ctrl->type = type;  //V4L2_CTRL_TYPE_INTEGER
	ctrl->flags = flags; //V4L2_CTRL_FLAG_SLIDER
	ctrl->minimum = min;  //0
	ctrl->maximum = max;  //255
	ctrl->step = step;    //1
	ctrl->qmenu = qmenu;  //NULL
	ctrl->priv = priv;    //NULL
	ctrl->cur.val = ctrl->val = ctrl->default_value = def; //127

	if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
		ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
		ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
		if (ctrl->minimum)
			memset(ctrl->cur.string, ' ', ctrl->minimum);
	}
	if (handler_new_ref(hdl, ctrl)) {
		kfree(ctrl);
		return NULL;
	}
	mutex_lock(&hdl->lock);
    //把v4l2_ctrl的node节点,加入到v4l2_ctrl_handler的ctrls链表中去
	list_add_tail(&ctrl->node, &hdl->ctrls); 
	mutex_unlock(&hdl->lock);
	return ctrl;
}


static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
			   struct v4l2_ctrl *ctrl)
{
	struct v4l2_ctrl_ref *ref;
	struct v4l2_ctrl_ref *new_ref;
	u32 id = ctrl->id;
	u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1; //通过V4L2_CID_BRIGHTNESS id获取到了V4L2_CID_USER_CLASS
	int bucket = id % hdl->nr_of_buckets;	/* which bucket to use */
    //如果是第一次添加v4l2_ctrl所以下面if条件成立,先将V4L2_CID_USER_CLASS创建v4l2_ctrl加入到hdl->ctrl_refs ctrl_refs是按id升序插入
	/* Automatically add the control class if it is not yet present. */
	if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
		if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
			return hdl->error;

	if (hdl->error)
		return hdl->error;

    //v4l2_ctrl实际上是通过struct v4l2_ctrl_ref组织挂载到v4l2_ctrl_handler->ctrl_refs链表上
	new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
	if (!new_ref)
		return handler_set_err(hdl, -ENOMEM);
	new_ref->ctrl = ctrl;
	if (ctrl->handler == hdl) {
    //这里条件为true所以会往下走,看代码实际上ctrl->cluster也是指向自己的
		/* By default each control starts in a cluster of its own.
		   new_ref->ctrl is basically a cluster array with one
		   element, so that's perfect to use as the cluster pointer.
		   But only do this for the handler that owns the control. */
		ctrl->cluster = &new_ref->ctrl;
		ctrl->ncontrols = 1;
	}

	INIT_LIST_HEAD(&new_ref->node);

	mutex_lock(&hdl->lock);

	/* Add immediately at the end of the list if the list is empty, or if
	   the last element in the list has a lower ID.
	   This ensures that when elements are added in ascending order the
	   insertion is an O(1) operation. */
    //上面英文注释就是如果ctrl_refs为空或id大于链表最后一个ctrl的id,那么直接将
    //new_ref加入到hdl->ctrl_refs链表尾, 直接跳转到insert_in_hash标号
	if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
		list_add_tail(&new_ref->node, &hdl->ctrl_refs);
		goto insert_in_hash;
	}

    //遍历hdl->ctrl_refs链表,按升序找到合适的位置插入
	/* Find insert position in sorted list */
	list_for_each_entry(ref, &hdl->ctrl_refs, node) {
		if (ref->ctrl->id < id)
			continue;
		/* Don't add duplicates */
		if (ref->ctrl->id == id) {
			kfree(new_ref);
			goto unlock;
		}
		list_add(&new_ref->node, ref->node.prev);
		break;
	}

insert_in_hash:
    //这里我看下来就是每次把new_ref->next指向上次buckets[bucket]数组里存放的v4l2_ctrl_ref 
    //并把先加入的new_ref赋值给buckets[bucket]
	/* Insert the control node in the hash */
	new_ref->next = hdl->buckets[bucket];
	hdl->buckets[bucket] = new_ref;

unlock:
	mutex_unlock(&hdl->lock);
	return 0;
}

总结一下handler_new_ref这个函数这里存在好几条链表,一条就是v4l2_ctrl通过v4l2_ctrl_ref
来组织挂载到hdl->ctrl_refs链表上并且这个链表是按升序排列。还有几条链表是按v4l2_ctrl->id
对 hdl->nr_of_buckets求余,余数相同的v4l2_ctrl通过v4l2_ctrl_ref->next串联起来。
//以vivi驱动为例11个v4l2_ctrl有2个bucket, 假设id为1,2,3,4四个ctrl, 那么2,4会通过next
//串联起来形成一条链表,1,3串联起来形成一条链表

总结vivi驱动初始化一共有这么几点:

1、struct vivi_dev内嵌入struct v4l2_device, struct v4l2_ctrl_handler, struct video_device, , struct vb2_queue这么几个核心结构体

2.对v4l2_device进行初始化并注册

3.初始化v4l2_ctrl_handler, 并创建v4l2_ctrl控件加入到v4l2_ctrl_handler中去

4.vb2_queue的初始化和一些回调函数赋值

5.video_device的初始化并注册, 其中video_device中的fops和ioctl_ops回调函数初始化是需要关注的重点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值