3. 实现VIDIOC_QUERYCAP查询设备能力

实现VIDIOC_QUERYCAP查询设备能力 学习卡片

任务说明

实现V4L2设备驱动的ioctl处理函数,支持VIDIOC_QUERYCAP命令,向应用层返回设备驱动名称(driver)、总线信息(bus_info)、驱动版本号(version)及设备能力标志(capabilities)。本案例以虚拟摄像头驱动为最小实现,不涉及真实硬件。

核心实现

#include <linux/videodev2.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>

#define VIRTUAL_CAM_DRIVER_NAME    "virtual_cam"
#define VIRTUAL_CAM_BUS_INFO       "platform:virtual_cam"

static int virtcam_querycap(struct file *file, void *fh,
                            struct v4l2_capability *cap)
{
    /* 驱动名称:必须以\0结尾,且不超过16字节(含\0) */
    strscpy(cap->driver, VIRTUAL_CAM_DRIVER_NAME, sizeof(cap->driver));

    /* 总线信息:描述设备挂载位置,不超过32字节 */
    strscpy(cap->bus_info, VIRTUAL_CAM_BUS_INFO, sizeof(cap->bus_info));

    /* 内核版本号:使用LINUX_VERSION_CODE宏自动生成 */
    cap->version = LINUX_VERSION_CODE;

    /* 设备能力:仅声明支持视频采集和流式I/O */
    cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;

    /* 设备特定能力(可选):此处与capabilities保持一致 */
    cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;

    return 0;
}

/* 关联到v4l2_ioctl_ops结构体 */
static const struct v4l2_ioctl_ops virtcam_ioctl_ops = {
    .vidioc_querycap = virtcam_querycap,
};

/* 最小video_device注册示例 */
static struct video_device virtcam_vdev = {
    .name = "Virtual Camera",
    .vfl_dir = VFL_DIR_RX,
    .ioctl_ops = &virtcam_ioctl_ops,
    .release = video_device_release_empty,
};

static int __init virtcam_init(void)
{
    return video_register_device(&virtcam_vdev, VFL_TYPE_VIDEO, -1);
}

static void __exit virtcam_exit(void)
{
    video_unregister_device(&virtcam_vdev);
}

module_init(virtcam_init);
module_exit(virtcam_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Virtual camera driver for VIDIOC_QUERYCAP demo");

关键说明

  • strscpy确保字符串安全拷贝并强制\0结尾,避免缓冲区溢出。
  • cap->version直接使用内核版本宏,应用层可通过该字段判断驱动适配的内核API版本。
  • capabilities与device_caps的区别:前者表示整个设备支持的能力,后者表示当前打开的设备节点支持的能力。本案例两者一致。
  • 若需支持VIDIOC_QUERYCAP以外的ioctl,只需在virtcam_ioctl_ops中继续添加对应的回调函数。

编译与加载

# 编译(需内核头文件与build目录)
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

# 加载模块
sudo insmod virtcam.ko

# 查看生成的设备节点(通常为 /dev/video0)
ls -l /dev/video*

验证方法

使用v4l2-ctl工具查询设备能力:

v4l2-ctl -d /dev/video0 --all

通过条件:输出中应包含:

  • Driver name: virtual_cam
  • Bus info: platform:virtual_cam
  • Version: 与当前内核版本对应的数值(如6.8.0)
  • Device Caps: Video Capture | Streaming

若v4l2-ctl不可用,也可用以下C程序直接调用ioctl验证:

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

int main(void) {
    int fd = open("/dev/video0", O_RDWR);
    if (fd < 0) { perror("open"); return 1; }

    struct v4l2_capability cap;
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
        perror("VIDIOC_QUERYCAP");
        close(fd);
        return 1;
    }

    printf("driver: %s\n", cap.driver);
    printf("bus_info: %s\n", cap.bus_info);
    printf("version: %u.%u.%u\n",
           (cap.version >> 16) & 0xFF,
           (cap.version >> 8) & 0xFF,
           cap.version & 0xFF);
    printf("capabilities: 0x%08x\n", cap.capabilities);

    close(fd);
    return 0;
}

编译运行:gcc test_querycap.c -o test_querycap && ./test_querycap,程序应打印上述字段且无错误返回。