v4l 子系统

v4l 子系统介绍

V4L 子系统是 Linux 内核中的一个框架,用于支持视频设备的驱动程序和应用程序。它提供了一个统一的接口,使得应用程序能够与各种不同类型的视频设备(如摄像头、TV 卡等)进行通信。

V4L 子系统的主要组件包括驱动程序、设备节点、V4L2 库和 V4L2 应用程序。驱动程序负责将视频设备的输入数据转换成 V4L2 格式的数据,并将其提供给应用程序使用。设备节点是通过系统中的设备文件来表示的,它们允许应用程序通过 V4L2 接口与视频设备进行通信。V4L2 库是一个用户空间的库,用于提供一些常用的 V4L2 函数,这些函数可以被应用程序调用来操作视频设备。V4L2 应用程序是使用 V4L2 接口来捕获、处理和显示视频数据的程序。

总的来说,V4L 子系统提供了一个灵活的、可扩展的、标准化的视频接口,使得 Linux 系统能够方便地支持各种不同类型的视频设备。

V4l子系统框架介绍

V4L(Video for Linux)子系统是 Linux 内核中用于支持视频设备的驱动程序和应用程序的框架。它提供了一个统一的接口,使得应用程序能够与各种不同类型的视频设备(如摄像头、TV 卡等)进行通信。

V4L 子系统的框架主要包括以下几个方面:

  1. 设备驱动程序:V4L 驱动程序是一个核心部件,它是用于访问视频设备的。驱动程序必须将设备输入数据转换成 V4L2(Video for Linux 2)格式的数据,并将其提供给应用程序使用。驱动程序的实现基于 Linux 内核中的 V4L 驱动框架,它提供了一组标准接口,用于在内核中管理视频设备。

  2. 设备节点:Linux 内核将视频设备表示为设备文件(例如,/dev/video0),这些文件用于在用户空间中访问设备。应用程序可以打开设备节点并使用 V4L2 接口与驱动程序进行通信。

  3. V4L2 库:V4L2 库是一个用户空间的库,用于提供一些常用的 V4L2 函数。这些函数可以被应用程序调用来操作视频设备。V4L2 库包含了大量的函数和数据结构,用于配置视频设备、捕获视频流、控制视频设备参数等操作。

  4. V4L2 应用程序:V4L2 应用程序是使用 V4L2 接口来捕获、处理和显示视频数据的程序。它们通过打开设备节点并使用 V4L2 库中的函数与视频设备进行通信。常见的 V4L2 应用程序包括视频捕获应用程序、视频编辑应用程序、视频播放应用程序等。

总的来说,V4L 子系统提供了一个灵活、可扩展的、标准化的视频接口,使得 Linux 系统能够方便地支持各种不同类型的视频设备,并提供了完整的视频处理功能。

代码例子

OV5640 摄像头为例子

设备树代码:

/dts-v1/;
/plugin/;

/ {
    
    
    model = "my_board";

    v4l2_dev: v4l2-dev {
    
    
        compatible = "v4l2-dev";
        status = "okay";
        reg = <0>;
    };

    ov5640: camera@3c {
    
    
        compatible = "ovti,ov5640";
        reg = <0x3c>;
        clock-names = "xclk";
        clocks = <&cam_mclk>;
        pinctrl-names = "default";
        pinctrl-0 = <&camera_pins>;
        reset-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>;
        powerdown-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
        vdd-supply = <&ldo2_reg>;
        mclk = <24000000>;
        mclk_source = <0>;
        status = "okay";
    };
};


驱动例子

#include <linux/module.h>
#include <linux/platform_device.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/videobuf2-core.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

#define OV5640_NAME "ov5640"
#define OV5640_I2C_ADDRESS 0x3c

struct ov5640 {
    
    
    struct v4l2_subdev subdev;
    struct media_pad pad;
    struct v4l2_ctrl_handler ctrl_handler;
    struct i2c_client *client;
    struct mutex lock;
    bool streaming;
};

static const struct v4l2_ctrl_config ov5640_ctrls[] = {
    
    
    {
    
    
        .ops = &v4l2_ctrl_ops_menu,
        .id = V4L2_CID_EXPOSURE_AUTO,
        .name = "Exposure",
        .type = V4L2_CTRL_TYPE_MENU,
        .menu_skip_mask = 0x01,
        .menu = {
    
    
            .items = (const char * const []){
    
    "Auto", "Manual", NULL},
            .mask = 0x02,
        },
    },
    {
    
    
        .ops = &v4l2_ctrl_ops_menu,
        .id = V4L2_CID_POWER_LINE_FREQUENCY,
        .name = "Power Line Frequency",
        .type = V4L2_CTRL_TYPE_MENU,
        .menu_skip_mask = 0x01,
        .menu = {
    
    
            .items = (const char * const []){
    
    "Disabled", "50 Hz", "60 Hz", NULL},
            .mask = 0x03,
        },
    },
};

static const struct v4l2_subdev_ops ov5640_ops = {
    
    
    .core = {
    
    
        .g_ctrl = v4l2_subdev_g_ctrl,
        .s_ctrl = v4l2_subdev_s_ctrl,
    },
    .video = {
    
    
        .g_mbus_config = v4l2_subdev_g_mbus_config,
        .s_stream = v4l
.s_stream = ov5640_s_stream,
};

static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
    
    
.enum_mbus_code = ov5640_enum_mbus_code,
.get_fmt = ov5640_get_fmt,
.set_fmt = ov5640_set_fmt,
.enum_frame_size = ov5640_enum_frame_size,
.enum_frame_interval = ov5640_enum_frame_interval,
.get_selection = ov5640_get_selection,
.set_selection = ov5640_set_selection,
};

static const struct v4l2_subdev_video_ops ov5640_video_ops = {
    
    
.s_std_output = ov5640_s_std_output,
};

static const struct v4l2_subdev_sensor_ops ov5640_sensor_ops = {
    
    
.s_stream = ov5640_s_stream,
.s_power = ov5640_set_power,
};

static const struct v4l2_subdev_core_ops ov5640_core_ops = {
    
    
.g_ctrl = ov5640_g_ctrl,
.s_ctrl = ov5640_s_ctrl,
};

static const struct v4l2_subdev_ops ov5640_subdev_ops = {
    
    
.core = &ov5640_core_ops,
.pad = &ov5640_pad_ops,
.video = &ov5640_video_ops,
.sensor = &ov5640_sensor_ops,
};

static const struct media_entity_operations ov5640_subdev_entity_ops = {
    
    
.link_validate = v4l2_subdev_link_validate,
};

static const struct v4l2_async_notifier_operations ov5640_async_ops = {
    
    
.alloc = ov5640_async_alloc,
.free = ov5640_async_free,
};

static int ov5640_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
    
    
struct ov5640 *ov5640;
struct v4l2_subdev *sd;
struct v4l2_ctrl_handler *ctrls;
int ret;
ov5640 = devm_kzalloc(&client->dev, sizeof(*ov5640), GFP_KERNEL);
if (!ov5640)
    return -ENOMEM;

ov5640->client = client;
ov5640->xvclk = devm_clk_get(&client->dev, "xvclk");
if (IS_ERR(ov5640->xvclk)) {
    
    
    dev_err(&client->dev, "Failed to get xvclk\n");
    return PTR_ERR(ov5640->xvclk);
}

ret = clk_set_rate(ov5640->xvclk, 24000000);
if (ret < 0) {
    
    
    dev_err(&client->dev, "Failed to set xvclk rate\n");
    return ret;
}

ov5640->subdev.ctrl_handler = &ov5640->ctrls;
ctrls = &ov5640->ctrls;
v4l2_ctrl_handler_init(ctrls, 6);

ov5640->link_freq = v4l2_ctrl_new_int_menu(ctrls, &ov5640_ctrl_ops,
                            V4L2_CID_LINK_FREQ,
                            ARRAY_SIZE(ov5640_link_freqs) - 1,
                            0, ov5640_link_freqs);

if (ov5640->link_freq)
    ov5640->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;

ov5640->pixel_rate = v4l2_ctrl_new_std(ctrls, &ov5640_ctrl_ops,
                        V4L2_CID_PIXEL_RATE, 0,
                        OV5640_PIXEL_RATE_MAX,
OV5640_PIXEL_RATE_STEP, OV5640_PIXEL_RATE_DEFAULT);

if (ctrls->error) {
    
    
    ret = ctrls->error;
    dev_err(&client->dev, "Failed to initialize controls\n");
    goto err_free_handler;
}

ov5640->subdev.owner = THIS_MODULE;
ov5640->subdev.ops = &ov5640_subdev_ops;
ov5640->subdev.internal_ops = &ov5640_internal_ops;
ov5640->subdev.entity.ops = &ov5640_subdev_entity_ops;
ov5640->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
snprintf(ov5640->subdev.name, sizeof(ov5640->subdev.name),
         "%s 0-0078", client->name);
ov5640->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
ov5640->subdev.devnode = kasprintf(GFP_KERNEL, "v4l-subdev%d",
                    client->dev.devt);
if (!ov5640->subdev.devnode) {
    
    
    ret = -ENOMEM;
    dev_err(&client->dev, "Failed to create devnode\n");
    goto err_free_handler;
}

sd = &ov5640->subdev;
ret = media_entity_pads_init(&sd->entity, OV5640_PADS_NUM, ov5640_pads);
if (ret < 0) {
    
    
    dev_err(&client->dev, "Failed to initialize media entity pads\n");
    goto err_cleanup_devnode;
}

ret = v4l2_async_register_subdev(sd);
if (ret < 0) {
    
    
    dev_err(&client->dev, "Failed to register v4l2 subdev\n");
    goto err_media_entity_cleanup;
}

ov5640->sd = sd;
i2c_set_clientdata(client, ov5640);

ret = v4l2_async_notifier_register(&ov5640->notifier, client->dev.parent,
                       &ov5640_async_ops);
if (ret) {
    
    
    dev_err(&client->dev, "Failed to register async notifier\n");
    goto err_unregister_subdev;
}

return 0;

err_unregister_subdev:
v4l2_async_unregister_subdev(sd);
err_media_entity_cleanup:
media_entity_cleanup(&sd->entity);
err_cleanup_devnode:
kfree(ov5640->subdev.devnode);
err_free_handler:
v4l2_ctrl_handler_free(ctrls);
return ret;
}
static int ov5640_remove(struct i2c_client *client)
{
    
    
struct ov5640 *ov5640 = i2c_get_clientdata(client);
v4l2_async_notifier_unregister(&ov5640->notifier);
v4l2_async_unregister_subdev(ov5640->sd);
media_entity_cleanup(&ov5640->sd->entity);
kfree(ov5640->subdev.devnode);
v4l2_ctrl_handler_free(&ov5640->ctrls);

return 0;
}

static const struct of_device_id ov5640_of_match[] = {
    
    
{
    
     .compatible = "ovti,ov5640" },
{
    
     },
};
MODULE_DEVICE_TABLE(of, ov5640_of_match);

static const struct i2c_device_id ov5640_id[] = {
    
    
{
    
     "ov5640", 0 },
{
    
     },
};
MODULE_DEVICE_TABLE(i2c, ov5640_id);

static struct i2c_driver ov5640_i2c_driver = {
    
    
.driver = {
    
    
.name = "ov5640"
.owner = THIS_MODULE,
.of_match_table = ov5640_of_match,
},
.probe_new = ov5640_probe,
.remove = ov5640_remove,
.id_table = ov5640_id,
};

module_i2c_driver(ov5640_i2c_driver);

MODULE_DESCRIPTION("OmniVision OV5640 camera driver");
MODULE_AUTHOR("Author Name");
MODULE_LICENSE("GPL");

上述代码是一个针对 OmniVision OV5640 相机芯片的驱动程序,它使用 I2C 总线与系统进行通信,并为用户提供了一个 V4L2 接口来访问该相机。

代码中的 .of_match_table 成员指定了相机设备树节点所需的属性,它将匹配节点中的厂商 ID 和设备 ID,并将设备与此驱动程序进行匹配。如果匹配成功,将使用 probe_new 回调函数进行初始化。

ov5640_probe 函数在驱动程序初始化时被调用,它为设备进行初始化并在 V4L2 子系统中注册了设备。初始化过程中会创建并配置 V4L2 控件,包括像素格式、分辨率、帧率等。此外,驱动程序还会为相机分配和初始化缓冲区,以便后续的视频流捕获和处理。

驱动程序还提供了 .s_stream 回调函数,以启动和停止视频流捕获。在该函数中,驱动程序会开启或关闭相机的视频流输出,并将捕获到的帧传递给 V4L2 子系统进行处理。此外,该驱动还提供了 .g_ctrl.s_ctrl 回调函数,以支持对 V4L2 控件的读取和设置操作。

ov5640_remove 函数在驱动程序从系统中卸载时被调用,它将关闭相机的视频流输出,并释放分配的资源。最后,该驱动程序使用 module_i2c_driver 宏进行注册,并提供了 GPL 许可证。

猜你喜欢

转载自blog.csdn.net/qq_31057589/article/details/130511679
今日推荐