V4L2-PCI驱动程序样例分析(一)

本文详细分析了Linux内核中的V4L2-PCI驱动程序样例,包括驱动头文件、数据结构定义、PCI驱动框架、.probe和.remove函数的执行流程。内容涉及PCI设备的启用、DMA设置、中断分配、v4l2设备注册以及资源释放等关键步骤。
摘要由CSDN通过智能技术生成

V4L2-PCI驱动程序样例分析

一、简介

本文基于linux内核提供的一个PCI驱动程序模板进行分析,驱动程序文件为v4l2-pci-skeleton.c,放在linux/samples/v4l目录下。从该驱动程序中,将看到一个PCI驱动程序的总体框架和组成结构,同时该驱动基于V4L2设计,从而也能了解到如何基于V4L2开发视频捕获驱动。

👉👀 本文分析其驱动程序的整体结构和.probe.remove两个驱动入口函数的具体执行过程。

二、驱动头文件和基础数据机构定义

首先头文件包含如下:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/videodev2.h>
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>

然后对模块的基本功能、作者、LICENSE许可进行描述:

MODULE_DESCRIPTION("V4L2 PCI Skeleton Driver");
MODULE_AUTHOR("Hans Verkuil");
MODULE_LICENSE("GPL v2");

接着定义一个struct skeleton结构体,用于封装、描述在驱动设计中所需要使用到的数据结构、锁、过程参数等,struct skeleton定义如下:

struct skeleton {
   struct pci_dev *pdev; //代表PCI设备
   struct v4l2_device v4l2_dev; //顶层v4l2设备结构
   struct video_device vdev; //video设备节点
   struct v4l2_ctrl_handler ctrl_handler; //v4l2框架的控件处理结构
   struct mutex lock; //Ioctl序列化互斥锁
   v4l2_std_id std; //当前SDTV标准
   struct v4l2_dv_timings timings; //当前高清电视(HDTV)时序
   struct v4l2_pix_format format; //当前像素格式
   unsigned input; //当前视频输入。(0表示SDTV、1表示HDTV)

   struct vb2_queue queue; //Vb2视频采集队列

   spinlock_t qlock; //控制访问buf_list和sequence的自旋锁
   struct list_head buf_list; //为DMA排队的缓冲区列表
   unsigned field; //当前缓冲区的字段(TOP/BOTTOM/other)
   unsigned sequence; //帧序列计数器
};

接着定义一个buffer链表:

struct skel_buffer {
   struct vb2_v4l2_buffer vb;
   struct list_head list;
};

三、PCI驱动框架骨架

V4L2-pci驱动以PCI驱动框架实现,故需按照PCI驱动设计框架进行驱动封装:创建struct pci_driver实例skeleton_driver ,并指定其.name.probe.remove.id_table

static struct pci_driver skeleton_driver = {
	.name = KBUILD_MODNAME,
	.probe = skeleton_probe,
	.remove = skeleton_remove,
	.id_table = skeleton_pci_tbl,
};

最后使用module_pci_driver()skeleton_driver导出为模块:

module_pci_driver(skeleton_driver);

四、.probe详解

.probe是驱动程序的入口,在V4L2-pci驱动中,.probe被设置为skeleton_probe(),该函数中执行的操作较多,下文将分步描述:

1、定义probe过程所需要的结构
	static const struct v4l2_dv_timings timings_def =
		V4L2_DV_BT_CEA_1280X720P60;
	struct skeleton *skel;
	struct video_device *vdev;
	struct v4l2_ctrl_handler *hdl;
	struct vb2_queue *q;
	int ret;
2、使能PCI设备
	ret = pci_enable_device(pdev);
	if (ret)
		return ret;
3、设置DMA
	ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
	if (ret) {
		dev_err(&pdev->dev, "no suitable DMA available.\n");
		goto disable_pci;
	}
4、创建新的驱动实例
	skel = devm_kzalloc(&pdev->dev, sizeof(struct skeleton), GFP_KERNEL);
	if (!skel) {
		ret = -ENOMEM;
		goto disable_pci;
	}
5、分配中断
	ret = devm_request_irq(&pdev->dev, pdev->irq,
			       skeleton_irq, 0, KBUILD_MODNAME, skel);
	if (ret) {
		dev_err(&pdev->dev, "request_irq failed\n");
		goto disable_pci;
	}
  
  //将传入的pci_driver结构指针赋值给驱动程序下的pdev。
	skel->pdev = pdev;
6、填写与格式相关的初始设置
	skel->timings = timings_def;
	skel->std = V4L2_STD_625_50;
	skeleton_fill_pix_format(skel, &skel->format);
7、注册v4l2设备
	ret = v4l2_device_register(&pdev->dev, &skel->v4l2_dev);
	if (ret)
		goto disable_pci;
8、初始化互斥锁
mutex_init(&skel->lock);
9、添加控制器
	hdl = &skel->ctrl_handler;
	v4l2_ctrl_handler_init(hdl, 4);
	v4l2_ctrl_new_std(hdl, &skel_ctrl_ops,
			  V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
	v4l2_ctrl_new_std(hdl, &skel_ctrl_ops,
			  V4L2_CID_CONTRAST, 0, 255, 1, 16);
	v4l2_ctrl_new_std(hdl, &skel_ctrl_ops,
			  V4L2_CID_SATURATION, 0, 255, 1, 127);
	v4l2_ctrl_new_std(hdl, &skel_ctrl_ops,
			  V4L2_CID_HUE, -128, 127, 1, 0);
	if (hdl->error) {
		ret = hdl->error;
		goto free_hdl;
	}
	skel->v4l2_dev.ctrl_handler = hdl;
10、设置vb2相关参数并初始化vb2队列
	q = &skel->queue;
	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
	q->dev = &pdev->dev;
	q->drv_priv = skel;
	q->buf_struct_size = sizeof(struct skel_buffer);
	q->ops = &skel_qops;
	q->mem_ops = &vb2_dma_contig_memops;
	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
  
  //假设在启动之前这个DMA引擎至少需要两个缓冲区可用。用于确保直到有这么多缓冲区队列,start_streaming()操作才会被调用,
	q->min_buffers_needed = 2;
  
  //流ioctl的序列化锁,
	q->lock = &skel->lock;
  
  //由于此驱动程序只能做32位DMA,故必须确保vb2内核将在32位DMA内存中分配缓冲区。
	q->gfp_flags = GFP_DMA32;
  
  //初始化vb2队列
	ret = vb2_queue_init(q);
	if (ret)
		goto free_hdl;
11、初始化buf链表和自旋锁
	INIT_LIST_HEAD(&skel->buf_list);
	spin_lock_init(&skel->qlock);
12、初始化video_device结构数据并注册video_device
	vdev = &skel->vdev;
	strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
	/*
	 * There is nothing to clean up, so release is set to an empty release
	 * function. The release callback must be non-NULL.
	 */
	vdev->release = video_device_release_empty;
	vdev->fops = &skel_fops,
	vdev->ioctl_ops = &skel_ioctl_ops,
	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
			    V4L2_CAP_STREAMING;
	/*
	 * The main serialization lock. All ioctls are serialized by this
	 * lock. Exception: if q->lock is set, then the streaming ioctls
	 * are serialized by that separate lock.
	 */
	vdev->lock = &skel->lock;
	vdev->queue = q;
	vdev->v4l2_dev = &skel->v4l2_dev;
	/* Supported SDTV standards, if any */
	vdev->tvnorms = SKEL_TVNORMS;
	video_set_drvdata(vdev, skel);

	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
	if (ret)
		goto free_hdl;

五、.remove详解

.probe是驱动程序的出口,在V4L2-pci驱动中,.remove被设置为skeleton_remove(),该函数实现如下:

static void skeleton_remove(struct pci_dev *pdev)
{
	struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
	struct skeleton *skel = container_of(v4l2_dev, struct skeleton, v4l2_dev);

	video_unregister_device(&skel->vdev);
	v4l2_ctrl_handler_free(&skel->ctrl_handler);
	v4l2_device_unregister(&skel->v4l2_dev);
	pci_disable_device(skel->pdev);
}

从上述代码可知,执行remove时,具体执行流程如下:

  • (1)首先从struct pci_dev结构中反解出struct v4l2_device;接着从struct v4l2_device中反解出struct skeleton

  • (2)调用video_unregister_device()释放注册的video设备。

  • (3)调用v4l2_ctrl_handler_free()释放控件的处理程序。

  • (4)调用v4l2_device_unregister()注销v4l2设备资源。

  • (5)调用pci_disable_device()禁用PCI设备。

上述则是驱动程序在退出时执行的资源释放和清理操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iriczhao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值