设备驱动-【转载】v4l2子系统驱动架构 - ioctl

下一篇:v4l2 应用流程调用流程

https://www.cnblogs.com/zhangzhiwei122/p/16156750.html 

 

原文链接:https://blog.csdn.net/lxllinux/article/details/81745452

  

ioctl详解

进入ioctl都是从cdev->ops->ioctl进入的,一般的驱动cdev都是驱动自己初始化的,在v4l2架构中,cdev都已经初始化完成,不需要驱动开发者来初始化,下面是v4l2的cdev->ops结构体v4l2_fops :

  1.  
    static const struct file_operations v4l2_fops = {
  2.  
    .owner = THIS_MODULE,
  3.  
    .read = v4l2_read,
  4.  
    .write = v4l2_write,
  5.  
    .open = v4l2_open,
  6.  
    .get_unmapped_area = v4l2_get_unmapped_area,
  7.  
    .mmap = v4l2_mmap,
  8.  
    .unlocked_ioctl = v4l2_ioctl,
  9.  
    #ifdef CONFIG_COMPAT
  10.  
    .compat_ioctl = v4l2_compat_ioctl32,
  11.  
    #endif
  12.  
    .release = v4l2_release,
  13.  
    .poll = v4l2_poll,
  14.  
    .llseek = no_llseek,
  15.  
    };

可以看到ioctl有两个,一个unlocked_ioctl,另一个是compat_ioctl 兼容性的,其中的区别这里不多说,我们来看函数v4l2_ioctl:

  1.  
    static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  2.  
    {
  3.  
    struct video_device *vdev = video_devdata(filp);
  4.  
    int ret = -ENODEV;
  5.  
     
  6.  
    if (vdev->fops->unlocked_ioctl) {
  7.  
    struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
  8.  
     
  9.  
    if (lock && mutex_lock_interruptible(lock))
  10.  
    return -ERESTARTSYS;
  11.  
    if (video_is_registered(vdev))
  12.  
    ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);--正常进入函数|video_device->fops
  13.  
    if (lock)
  14.  
    mutex_unlock(lock);
  15.  
    } else
  16.  
    ret = -ENOTTY;
  17.  
     
  18.  
    return ret;
  19.  
    }

从上可以看到要调用vdev->fops->unlocked_ioctl(filp, cmd, arg);具体驱动实现不同,以davinci平台为例来介绍,在vpfe_capture.c文件中,初始化过程中有

        vfd->fops       = &vpfe_fops;
 
  1.  
    static const struct v4l2_file_operations vpfe_fops = {
  2.  
    .owner = THIS_MODULE,
  3.  
    .open = vpfe_open,
  4.  
    .release = vpfe_release,
  5.  
    .unlocked_ioctl = video_ioctl2,
  6.  
    .mmap = vpfe_mmap,
  7.  
    .poll = vpfe_poll
  8.  
    };

那么是要调用video_ioctl2:

  1.  
    long video_ioctl2(struct file *file,
  2.  
    unsigned int cmd, unsigned long arg)
  3.  
    {
  4.  
    return video_usercopy(file, cmd, arg, __video_do_ioctl);--这个函数最终是要调用__video_do_ioctl,当然他会做其他事,有兴趣的可以去了解
  5.  
    }
  1.  
    static long __video_do_ioctl(struct file *file,
  2.  
    unsigned int cmd, void *arg)
  3.  
    {
  4.  
    struct video_device *vfd = video_devdata(file);
  5.  
    const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;----|video_device->ioctl_ops(注意 video_device这几个函数的调用,要明白他们的作用)
  6.  
    bool write_only = false;
  7.  
    struct v4l2_ioctl_info default_info;
  8.  
    const struct v4l2_ioctl_info *info;
  9.  
    void *fh = file->private_data;
  10.  
    struct v4l2_fh *vfh = NULL;
  11.  
    int dev_debug = vfd->dev_debug;
  12.  
    long ret = -ENOTTY;
  13.  
     
  14.  
    if (ops == NULL) {
  15.  
    pr_warn("%s: has no ioctl_ops.\n",
  16.  
    video_device_node_name(vfd));
  17.  
    return ret;
  18.  
    }
  19.  
     
  20.  
    if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
  21.  
    vfh = file->private_data;
  22.  
     
  23.  
    if (v4l2_is_known_ioctl(cmd)) {------------------------检查命令是否可用
  24.  
    info = &v4l2_ioctls[_IOC_NR(cmd)];
  25.  
     
  26.  
    if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
  27.  
    !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
  28.  
    goto done;
  29.  
     
  30.  
    if (vfh && (info->flags & INFO_FL_PRIO)) {
  31.  
    ret = v4l2_prio_check(vfd->prio, vfh->prio);
  32.  
    if (ret)
  33.  
    goto done;
  34.  
    }
  35.  
    } else {
  36.  
    default_info.ioctl = cmd;
  37.  
    default_info.flags = 0;
  38.  
    default_info.debug = v4l_print_default;
  39.  
    info = &default_info;
  40.  
    }
  41.  
     
  42.  
    write_only = _IOC_DIR(cmd) == _IOC_WRITE;
  43.  
    if (info->flags & INFO_FL_STD) {-----进入正题,这几个if会判断cmd的类型,然后调用不同的函数,这里留个尾巴,详情见下一节
  44.  
    typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
  45.  
    const void *p = vfd->ioctl_ops;
  46.  
    const vidioc_op *vidioc = p + info->u.offset;
  47.  
    /*调用驱动自定义的函数*/
  48.  
    ret = (*vidioc)(file, fh, arg);------------调用video_device->ioctl_ops
  49.  
    } else if (info->flags & INFO_FL_FUNC) {
  50.  
    /*调用架构定义的函数ops,ops最终还是要调用驱动自定义的函数*/
  51.  
    ret = info->u.func(ops, file, fh, arg);----调用video_device->ioctl_ops
  52.  
    } else if (!ops->vidioc_default) {
  53.  
    ret = -ENOTTY;
  54.  
    } else {
  55.  
    ret = ops->vidioc_default(file, fh,
  56.  
    vfh ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
  57.  
    cmd, arg);----调用video_device->ioctl_ops->vidioc_default
  58.  
    }
  59.  
     
  60.  
    done:
  61.  
    if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) {
  62.  
    if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) &&
  63.  
    (cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF))
  64.  
    return ret;
  65.  
     
  66.  
    v4l_printk_ioctl(video_device_node_name(vfd), cmd);
  67.  
    if (ret < 0)
  68.  
    pr_cont(": error %ld", ret);
  69.  
    if (!(dev_debug & V4L2_DEV_DEBUG_IOCTL_ARG))
  70.  
    pr_cont("\n");
  71.  
    else if (_IOC_DIR(cmd) == _IOC_NONE)
  72.  
    info->debug(arg, write_only);
  73.  
    else {
  74.  
    pr_cont(": ");
  75.  
    info->debug(arg, write_only);
  76.  
    }
  77.  
    }
  78.  
     
  79.  
    return ret;
  80.  
    }

ioctl命令分类

ioctl命令在文件v4l2-ioctl.c文件中实现,上面分析的函数__video_do_ioctl就在此文件中,在分析这个函数的过程中留了个尾巴,下面来分析一下

从上面的分析过程可以看出最终都是要调用video_device->ioctl_ops,而且调用时还需判断是INFO_FL_STD还是INFO_FL_FUNC,怎么判断呢,v4l2-ioctl.c有定义:

IOCTL_INFO_FNC(命令, 实现函数, 打印信息(debug使用), flag),同样 
IOCTL_INFO_STD(命令, 实现函数, 打印信息(debug使用), flag)

  1.  
    static struct v4l2_ioctl_info v4l2_ioctls[] = {
  2.  
    IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
  3.  
    IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
  4.  
    IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0),
  5.  
    IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
  6.  
    IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
  7.  
    IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
  8.  
    IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
  9.  
    IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
  10.  
    IOCTL_INFO_FNC(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
  11.  
    IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
  12.  
    IOCTL_INFO_STD(VIDIOC_EXPBUF, vidioc_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
  13.  
    IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
  14.  
    IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
  15.  
    IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
  16.  
    IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
  17.  
    IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
  18.  
    IOCTL_INFO_STD(VIDIOC_G_STD, vidioc_g_std, v4l_print_std, 0),
  19.  
    IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
  20.  
    IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
  21.  
    IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
  22.  
    IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
  23.  
    IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
  24.  
    IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
  25.  
    IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
  26.  
    IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
  27.  
    IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
  28.  
    IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
  29.  
    IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
  30.  
    IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
  31.  
    IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
  32.  
    IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, 0),
  33.  
    IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO),
  34.  
    IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
  35.  
    IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
  36.  
    IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
  37.  
    IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
  38.  
    IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
  39.  
    IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
  40.  
    IOCTL_INFO_FNC(VIDIOC_S_MODULATOR, v4l_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
  41.  
    IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
  42.  
    IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
  43.  
    IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
  44.  
    IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
  45.  
    IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
  46.  
    IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)),
  47.  
    IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)),
  48.  
    IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
  49.  
    IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
  50.  
    IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
  51.  
    IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
  52.  
    IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
  53.  
    IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
  54.  
    IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
  55.  
    IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
  56.  
    IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
  57.  
    IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
  58.  
    IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
  59.  
    IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
  60.  
    IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
  61.  
    IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
  62.  
    IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
  63.  
    IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
  64.  
    IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
  65.  
    IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
  66.  
    IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
  67.  
    IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
  68.  
    IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
  69.  
    IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
  70.  
    IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
  71.  
    IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
  72.  
    IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
  73.  
    IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
  74.  
    IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
  75.  
    IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
  76.  
    IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
  77.  
    IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
  78.  
    IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0),
  79.  
    IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
  80.  
    IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
  81.  
    IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
  82.  
    IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
  83.  
    IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
  84.  
    };

上面的分析中一直说到video_device->ioctl_ops,那么ioctl_ops有多少函数呢。。很多见下: 
算了还是不粘贴了自己去代码中看吧,这里只粘一点:

  1.  
    struct v4l2_ioctl_ops {
  2.  
    /* ioctl callbacks */
  3.  
     
  4.  
    /* VIDIOC_QUERYCAP handler */
  5.  
    int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap);
  6.  
     
  7.  
    /* VIDIOC_ENUM_FMT handlers */
  8.  
    int (*vidioc_enum_fmt_vid_cap) (struct file *file, void *fh,
  9.  
    struct v4l2_fmtdesc *f);
  10.  
    int (*vidioc_enum_fmt_vid_overlay) (struct file *file, void *fh,
  11.  
    struct v4l2_fmtdesc *f);
  12.  
    int (*vidioc_enum_fmt_vid_out) (struct file *file, void *fh,
  13.  
    struct v4l2_fmtdesc *f);
  14.  
    int (*vidioc_enum_fmt_vid_cap_mplane)(struct file *file, void *fh,
  15.  
    struct v4l2_fmtdesc *f);
  16.  
    int (*vidioc_enum_fmt_vid_out_mplane)(struct file *file, void *fh,
  17.  
    struct v4l2_fmtdesc *f);
  18.  
    int (*vidioc_enum_fmt_sdr_cap) (struct file *file, void *fh,
  19.  
    struct v4l2_fmtdesc *f);
  20.  
    int (*vidioc_enum_fmt_sdr_out) (struct file *file, void *fh,
  21.  
    struct v4l2_fmtdesc *f);
  22.  
  23.  
  24.  
  25.  
    /* DV Timings IOCTLs */
  26.  
    int (*vidioc_s_dv_timings) (struct file *file, void *fh,
  27.  
    struct v4l2_dv_timings *timings);
  28.  
    int (*vidioc_g_dv_timings) (struct file *file, void *fh,
  29.  
    struct v4l2_dv_timings *timings);
  30.  
    int (*vidioc_query_dv_timings) (struct file *file, void *fh,
  31.  
    struct v4l2_dv_timings *timings);
  32.  
    int (*vidioc_enum_dv_timings) (struct file *file, void *fh,
  33.  
    struct v4l2_enum_dv_timings *timings);
  34.  
    int (*vidioc_dv_timings_cap) (struct file *file, void *fh,
  35.  
    struct v4l2_dv_timings_cap *cap);
  36.  
    int (*vidioc_g_edid) (struct file *file, void *fh, struct v4l2_edid *edid);
  37.  
    int (*vidioc_s_edid) (struct file *file, void *fh, struct v4l2_edid *edid);
  38.  
     
  39.  
    int (*vidioc_subscribe_event) (struct v4l2_fh *fh,
  40.  
    const struct v4l2_event_subscription *sub);
  41.  
    int (*vidioc_unsubscribe_event)(struct v4l2_fh *fh,
  42.  
    const struct v4l2_event_subscription *sub);
  43.  
     
  44.  
    /* For other private ioctls */
  45.  
    long (*vidioc_default) (struct file *file, void *fh,
  46.  
    bool valid_prio, unsigned int cmd, void *arg);
  47.  
    };

ioctl调用总图

 

 

---------------------------------------------------------------------------------------------------------------------------------------

在应用程序获取视频数据的流程中,都是通过 ioctl 命令与驱动程序进行交互,常见的 ioctl 命令有:

 

  1.  
    VIDIOC_QUERYCAP /* 获取设备支持的操作 */
  2.  
     
  3.  
    VIDIOC_G_FMT /* 获取设置支持的视频格式 */
  4.  
     
  5.  
    VIDIOC_S_FMT /* 设置捕获视频的格式 */
  6.  
     
  7.  
    VIDIOC_REQBUFS /* 向驱动提出申请内存的请求 */
  8.  
     
  9.  
    VIDIOC_QUERYBUF /* 向驱动查询申请到的内存 */
  10.  
     
  11.  
    VIDIOC_QBUF /* 将空闲的内存加入可捕获视频的队列 */
  12.  
     
  13.  
    VIDIOC_DQBUF /* 将已经捕获好视频的内存拉出已捕获视频的队列 */
  14.  
     
  15.  
    VIDIOC_STREAMON /* 打开视频流 */
  16.  
     
  17.  
    VIDIOC_STREAMOFF /* 关闭视频流 */
  18.  
     
  19.  
    VIDIOC_QUERYCTRL /* 查询驱动是否支持该命令 */
  20.  
     
  21.  
    VIDIOC_G_CTRL /* 获取当前命令值 */
  22.  
     
  23.  
    VIDIOC_S_CTRL /* 设置新的命令值 */
  24.  
     
  25.  
    VIDIOC_G_TUNER /* 获取调谐器信息 */
  26.  
     
  27.  
    VIDIOC_S_TUNER /* 设置调谐器信息 */
  28.  
     
  29.  
    VIDIOC_G_FREQUENCY /* 获取调谐器频率 */
  30.  
     
  31.  
    VIDIOC_S_FREQUENCY /* 设置调谐器频率 */

 

1、struct v4l2_capability 与 VIDIOC_QUERYCAP
VIDIOC_QUERYCAP 命令通过结构 v4l2_capability 获取设备支持的操作模式

  1.  
    struct v4l2_capability {
  2.  
     
  3.  
    __u8 driver[16]; /* i.e. "bttv" */
  4.  
     
  5.  
    __u8 card[32]; /* i.e. "Hauppauge WinTV" */
  6.  
     
  7.  
    __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
  8.  
     
  9.  
    __u32 version; /* should use KERNEL_VERSION() */
  10.  
     
  11.  
    __u32 capabilities; /* Device capabilities */
  12.  
     
  13.  
    __u32 reserved[4];
  14.  
     
  15.  
    };

 

其中域 capabilities 代表设备支持的操作模式,常见的值有 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING 表示是一个视频捕捉设备并且具有数据流控制模式;另外 driver 域需要和 struct video_device 中的 name 匹配。
2、struct v4l2_format 与 VIDIOC_G_FMT、VIDIOC_S_FMT、VIDIOC_TRY_FMT

通常用 VIDIOC_S_FMT 命令通过结构 v4l2_format 初始化捕获视频的格式,如果要改变格式则用 VIDIOC_TRY_FMT 命令:

 

  1.  
    struct v4l2_format {
  2.  
     
  3.  
    enum v4l2_buf_type type;
  4.  
     
  5.  
    union {
  6.  
     
  7.  
    struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
  8.  
     
  9.  
    struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
  10.  
     
  11.  
    struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
  12.  
     
  13.  
    struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
  14.  
     
  15.  
    __u8 raw_data[200]; /* user-defined */
  16.  
     
  17.  
    } fmt;
  18.  
     
  19.  
    };
  20.  
     
  21.  
    其中
  22.  
     
  23.  
    enum v4l2_buf_type {
  24.  
     
  25.  
    V4L2_BUF_TYPE_VIDEO_CAPTURE = 1,
  26.  
     
  27.  
    V4L2_BUF_TYPE_VIDEO_OUTPUT = 2,
  28.  
     
  29.  
    V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,
  30.  
     
  31.  
    ...
  32.  
     
  33.  
    V4L2_BUF_TYPE_PRIVATE = 0x80,
  34.  
     
  35.  
    };
  36.  
     
  37.  
     
  38.  
    struct v4l2_pix_format {
  39.  
     
  40.  
    __u32 width;
  41.  
     
  42.  
    __u32 height;
  43.  
     
  44.  
    __u32 pixelformat;
  45.  
     
  46.  
    enum v4l2_field field;
  47.  
     
  48.  
    __u32 bytesperline; /* for padding, zero if unused */
  49.  
     
  50.  
    __u32 sizeimage;
  51.  
     
  52.  
    enum v4l2_colorspace colorspace;
  53.  
     
  54.  
    __u32 priv; /* private data, depends on pixelformat */
  55.  
     
  56.  
    };

 

常见的捕获模式为 V4L2_BUF_TYPE_VIDEO_CAPTURE 即视频捕捉模式,在此模式下 fmt 联合体采用域 v4l2_pix_format:其中 width 为视频的宽、height 为视频的高、pixelformat 为视频数据格式(常见的值有 V4L2_PIX_FMT_YUV422P | V4L2_PIX_FMT_RGB565)、bytesperline 为一行图像占用的字节数、sizeimage 则为图像占用的总字节数、colorspace 指定设备的颜色空间。
3、struct v4l2_requestbuffers 与 VIDIOC_REQBUFS

VIDIOC_REQBUFS 命令通过结构 v4l2_requestbuffers 请求驱动申请一片连续的内存用于缓存视频信息:

​​​​​​​

  1.  
    struct v4l2_requestbuffers {
  2.  
     
  3.  
    __u32 count;
  4.  
     
  5.  
    enum v4l2_buf_type type;
  6.  
     
  7.  
    enum v4l2_memory memory;
  8.  
     
  9.  
    __u32 reserved[2];
  10.  
     
  11.  
    };
  12.  
     
  13.  
    其中
  14.  
     
  15.  
    enum v4l2_memory {
  16.  
     
  17.  
    V4L2_MEMORY_MMAP = 1,
  18.  
     
  19.  
    V4L2_MEMORY_USERPTR = 2,
  20.  
     
  21.  
    V4L2_MEMORY_OVERLAY = 3,
  22.  
     
  23.  
    };

 

count 指定根据图像占用空间大小申请的缓存区个数,type 为视频捕获模式,memory 为内存区的使用方式。

4、struct v4l2_buffer与 VIDIOC_QUERYBUF

VIDIOC_QUERYBUF 命令通过结构 v4l2_buffer 查询驱动申请的内存区信息:

​​​​​​​

  1.  
    struct v4l2_buffer {
  2.  
     
  3.  
    __u32 index;
  4.  
     
  5.  
    enum v4l2_buf_type type;
  6.  
     
  7.  
    __u32 bytesused;
  8.  
     
  9.  
    __u32 flags;
  10.  
     
  11.  
    enum v4l2_field field;
  12.  
     
  13.  
    struct timeval timestamp;
  14.  
     
  15.  
    struct v4l2_timecode timecode;
  16.  
     
  17.  
    __u32 sequence;
  18.  
     
  19.  
     
  20.  
    /* memory location */
  21.  
     
  22.  
    enum v4l2_memory memory;
  23.  
     
  24.  
    union {
  25.  
     
  26.  
    __u32 offset;
  27.  
     
  28.  
    unsigned long userptr;
  29.  
     
  30.  
    } m;
  31.  
     
  32.  
    __u32 length;
  33.  
     
  34.  
    __u32 input;
  35.  
     
  36.  
    __u32 reserved;
  37.  
     
  38.  
    };

 

index 为缓存编号,type 为视频捕获模式,bytesused 为缓存已使用空间大小,flags 为缓存当前状态(常见值有 V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE,分别代表当前缓存已经映射、缓存可以采集数据、缓存可以提取数据),timestamp 为时间戳,sequence为缓存序号,memory 为缓存使用方式,offset 为当前缓存与内存区起始地址的偏移,length 为缓存大小,reserved 一般用于传递物理地址值。
另外 VIDIOC_QBUF 和 VIDIOC_DQBUF 命令都采用结构 v4l2_buffer 与驱动通信:VIDIOC_QBUF 命令向驱动传递应用程序已经处理完的缓存,即将缓存加入空闲可捕获视频的队列,传递的主要参数为 index;VIDIOC_DQBUF 命令向驱动获取已经存放有视频数据的缓存,v4l2_buffer 的各个域几乎都会被更新,但主要的参数也是 index,应用程序会根据 index 确定可用数据的起始地址和范围。

 

5、enum v4l2_buf_type 与 VIDIOC_STREAMON、VIDIOC_STREAMOFF

这两个命令使用的只是一个整形数据,即 v4l2_buf_type,一般只要指定其值为 V4L2_BUF_TYPE_VIDEO_CAPTURE 即可。

6、struct v4l2_queryctrl 与 VIDIOC_QUERYCTRL

VIDIOC_QUERYCTRL 命令通过结构 v4l2_queryctrl 查询驱动是否支持该 id 代表的命令,并返回该命令的各种参数:

​​​​​​​

  1.  
    struct v4l2_queryctrl {
  2.  
     
  3.  
    __u32 id; /* 命令编号 */
  4.  
     
  5.  
    enum v4l2_ctrl_type type; /* 命令值的类型 */
  6.  
     
  7.  
    __u8 name[32]; /* 命令名称*/
  8.  
     
  9.  
    __s32 minimum; /* 最小的命令值 */
  10.  
     
  11.  
    __s32 maximum; /* 最大的命令值 */
  12.  
     
  13.  
    __s32 step; /* 命令值变化的步长 */
  14.  
     
  15.  
    __s32 default_value; /* 默认的命令值 */
  16.  
     
  17.  
    __u32 flags; /* 命令的标志 */
  18.  
     
  19.  
    __u32 reserved[2]; /* 命令值的位图表示 */
  20.  
     
  21.  
    };
  22.  
     
  23.  
    其中
  24.  
     
  25.  
    enum v4l2_ctrl_type {
  26.  
     
  27.  
    V4L2_CTRL_TYPE_INTEGER = 1, /* 整形 */
  28.  
     
  29.  
    V4L2_CTRL_TYPE_BOOLEAN = 2, /* 真值 */
  30.  
     
  31.  
    V4L2_CTRL_TYPE_MENU = 3, /* 菜单 */
  32.  
     
  33.  
    V4L2_CTRL_TYPE_BUTTON = 4, /* 无值 */
  34.  
     
  35.  
    V4L2_CTRL_TYPE_INTEGER64 = 5, /* 后面三种不常用 */
  36.  
     
  37.  
    V4L2_CTRL_TYPE_CTRL_CLASS = 6,
  38.  
     
  39.  
    V4L2_CTRL_TYPE_STRING = 7,
  40.  
     
  41.  
    };
  42.  
     
  43.  
    命令的标志取值如下:
  44.  
     
  45.  
    /* Control flags */
  46.  
     
  47.  
    #define V4L2_CTRL_FLAG_DISABLED 0x0001
  48.  
     
  49.  
    #define V4L2_CTRL_FLAG_GRABBED 0x0002
  50.  
     
  51.  
    #define V4L2_CTRL_FLAG_READ_ONLY 0x0004
  52.  
     
  53.  
    #define V4L2_CTRL_FLAG_UPDATE 0x0008
  54.  
     
  55.  
    #define V4L2_CTRL_FLAG_INACTIVE 0x0010
  56.  
     
  57.  
    #define V4L2_CTRL_FLAG_SLIDER 0x0020
  58.  
     
  59.  
    #define V4L2_CTRL_FLAG_WRITE_ONLY 0x0040
  60.  
     
  61.  
     
  62.  
    /* Query flag, to be ORed with the control ID */
  63.  
     
  64.  
    #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000

 

id 是命令的编号,常见的命令有两种:一种以 V4L2_CID_BASE 为起始值,是公用命令;一种以 V4L2_CID_PRIVATE_BASE 为起始值,是私有命令。在一般的应用中命令值可见如下:

​​​​​​​

  1.  
    V4L2_CID_CONTRAST (V4L2_CID_BASE+1) /* 对比度调节 */
  2.  
     
  3.  
    V4L2_CID_SATURATION (V4L2_CID_BASE+2) /* 饱和度调节 */
  4.  
     
  5.  
    V4L2_CID_AUDIO_VOLUME (V4L2_CID_BASE+5) /* 音量调节 */
  6.  
     
  7.  
    V4L2_CID_AUDIO_MUTE (V4L2_CID_BASE+9) /* 静音设置 */
  8.  
     
  9.  
    V4L2_CID_DO_WHITE_BALANCE (V4L2_CID_BASE+13) /* 白平衡调节 */
  10.  
     
  11.  
    V4L2_CID_GAMMA (V4L2_CID_BASE+16) /* 伽马值调节 */
  12.  
     
  13.  
    V4L2_CID_EXPOSURE (V4L2_CID_BASE+17) /* 曝光度调节 */
  14.  
     
  15.  
     
  16.  
    V4L2_CID_PRIVATE_ATXX_FLASH (V4L2_CID_PRIVATE_BASE + 2) /* 闪光灯控制 */
  17.  
     
  18.  
    V4L2_CID_PRIVATE_ATXX_FRAME (V4L2_CID_PRIVATE_BASE + 12) /* 帧率调节 */

 

 type 为命令值的类型(总共有7中类型的值),name 是命令的名称,reserved 则是命令值的位图表示,驱动会将所有的命令值都以 bit 的形式写到 64 位的域中,上层应用查询时可以根据位图判断命令支持的值。

7、struct v4l2_control 与 VIDIOC_G_CTRL、VIDIOC_S_CTRL

VIDIOC_S_CTRL 或 VIDIOC_G_CTRL 命令通过结构 v4l2_control 设置或者获取 id 命令的值:

  1.  
     
  2.  
    struct v4l2_control {
  3.  
     
  4.  
    __u32 id;
  5.  
     
  6.  
    __s32 value;
  7.  
     
  8.  
    };

​​​​​​​这个结构只有 2 个域,id 是命令编号,value 则是命令的值。

8、struct v4l2_tuner 与 VIDIOC_G_TUNER、VIDIOC_S_TUNER

VIDIOC_S_TUNER 或 VIDIOC_G_TUNER 命令通过结构 v4l2_tuner 设置调谐器的信息:

​​​​​​​

  1.  
    struct v4l2_tuner {
  2.  
     
  3.  
    __u32 index; /* 调谐器编号,由应用程序设置 */
  4.  
     
  5.  
    __u8 name[32]; /* 调谐器名称 */
  6.  
     
  7.  
    enum v4l2_tuner_type type; /* 调谐器类型 */
  8.  
     
  9.  
    __u32 capability; /* 调谐器支持的操作 */
  10.  
     
  11.  
    __u32 rangelow; /* 最低频率值,单位为62.5Hz或者62.5KHz */
  12.  
     
  13.  
    __u32 rangehigh; /* 最高频率值 */
  14.  
     
  15.  
    __u32 rxsubchans; /* 接收的音频信号类型 */
  16.  
     
  17.  
    __u32 audmode; /* 当前音频播放形式 */
  18.  
     
  19.  
    __s32 signal; /* 信号强度 */
  20.  
     
  21.  
    __s32 afc; /* 自动频率控制 */
  22.  
     
  23.  
    __u32 reserved[4]; /* 保留备用 */
  24.  
     
  25.  
    };
  26.  
     
  27.  
    其中
  28.  
     
  29.  
    enum v4l2_tuner_type {
  30.  
     
  31.  
    V4L2_TUNER_RADIO = 1, /* 调频收音机 */
  32.  
     
  33.  
    V4L2_TUNER_ANALOG_TV = 2, /* 模拟电视高频头 */
  34.  
     
  35.  
    V4L2_TUNER_DIGITAL_TV = 3, /* 数字电视高频头 */
  36.  
     
  37.  
    };

 

其中域 type 有三种类型;capability 域一般为 V4L2_TUNER_CAP_LOW,表明频率调节的步长是62.5Hz,如果没有这个标志位则步长为62.5KHz;rangelow 与 rangehigh 是调谐器可以调频率的最高值和最低值,但都以步长为单位表示;rxsubchans 表示调谐器接收的音频信号类型,常见值有 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO  即单声道与立体声;audmode 表示以何种方式播放声音,常见值有 V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO,即以单声道还是立体声的方式播放;signal 为当前信号强度,一般取值范围为 0 - 65535。
9、struct v4l2_frequency 与 VIDIOC_G_FREQUENCY、VIDIOC_S_FREQUENCY

VIDIOC_S_FREQUENCY 或 VIDIOC_G_FREQUENCY 命令通过结构 v4l2_frequency 设置或获取当前频率值:

​​​​​​​

  1.  
    struct v4l2_frequency {
  2.  
     
  3.  
    __u32 tuner; /* 调谐器编号 */
  4.  
     
  5.  
    enum v4l2_tuner_type type; /* 调谐器类型 */
  6.  
     
  7.  
    __u32 frequency; /* 调谐器频率 */
  8.  
     
  9.  
    __u32 reserved[8];
  10.  
     
  11.  
    };

 

注意:frequency 的值是以62.5Hz 或者 62.5KHZ 为单位的。

 

附:_IO、_IOR、_IOW、_IOWR 宏的使用说明

驱动程序中 ioctl  函数传递的变量 cmd 是应用程序向驱动程序请求处理的命令。cmd 除了用于区别不同命令的数值,还可包含有助于处理的几种信息。cmd 的大小为 32 bit,共分 4 个域:

bit29 ~ bit31: 3bit  为 “读写” 区,作用是区分是读命令还是写命令。
bit16 ~ bit28:13bit 为 "数据大小" 区,表示 ioctl 中的 arg 变量传递的数据大小;有时候为 14bit 即将 bit29 覆盖。
bit8 ~ bit15:   8bit  为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。
bit0 ~ bit7:     8bit  为 "序号" 区,是区分命令的命令顺序序号。

魔数(magic number)
魔数范围为 0~255 。通常,用英文字符 'A' ~ 'Z' 或者 'a' ~ 'z' 来表示。设备驱动程序从传递进来的命令获取魔数,然后与自身处理的魔数想比较,如果相同则处理,不同则不处理。魔数是拒绝误使用的初步辅助参数。设备驱动程序可以通过宏 _IOC_TYPE (cmd) 来获取魔数。不同的设备驱动程序最好设置不同的魔数,但并不是要求绝对,也是可以使用其他设备驱动程序已用过的魔数。

基数(序号)
基数用于区别各种命令。通常,从 0开始递增,相同设备驱动程序上可以重复使用该值。例如,读和写命令中使用了相同的基数,设备驱动程序也能分辨出来,原因在于设备驱动程序区分命令时使用 switch ,且直接使用命令变量 cmd 值。创建命令的宏生成的值由多个域组合而成,所以即使是相同的基数,也会判断为不同的命令。设备驱动程序想要从命令中获取该基数,就使用宏 _IOC_NR (cmd)。

下面我们看一下上述宏在内核中的原型:

​​​​​​​

  1.  
    /*
  2.  
     
  3.  
    * Our DIR and SIZE overlap in order to simulteneously provide
  4.  
     
  5.  
    * a non-zero _IOC_NONE (for binary compatibility) and
  6.  
     
  7.  
    * 14 bits of size as on i386. Here's the layout:
  8.  
     
  9.  
    *
  10.  
     
  11.  
    * 0xE0000000 DIR 3bit
  12.  
     
  13.  
    * 0x80000000 DIR = WRITE bit31
  14.  
     
  15.  
    * 0x40000000 DIR = READ bit30
  16.  
     
  17.  
    * 0x20000000 DIR = NONE bit29
  18.  
     
  19.  
    * 0x3FFF0000 SIZE (overlaps NONE bit) 13bit
  20.  
     
  21.  
    * 0x0000FF00 TYPE 8bit
  22.  
     
  23.  
    * 0x000000FF NR (CMD) 8bit
  24.  
     
  25.  
    */
  26.  
     
  27.  
    /* 各个域的长度 */
  28.  
     
  29.  
    #define _IOC_NRBITS 8
  30.  
     
  31.  
    #define _IOC_TYPEBITS 8
  32.  
     
  33.  
    #define _IOC_SIZEBITS 13 /* Actually 14, see below. */
  34.  
     
  35.  
    #define _IOC_DIRBITS 3
  36.  
     
  37.  
    /* 各个域的掩码 */
  38.  
     
  39.  
    #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
  40.  
     
  41.  
    #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
  42.  
     
  43.  
    #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
  44.  
     
  45.  
    #define _IOC_XSIZEMASK ((1 << (_IOC_SIZEBITS+1))-1)
  46.  
     
  47.  
    #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
  48.  
     
  49.  
    /* 各个域的偏移 */
  50.  
     
  51.  
    #define _IOC_NRSHIFT 0
  52.  
     
  53.  
    #define _IOC_TYPESHIFT (_IOC_NRSHIFT + _IOC_NRBITS) /* 8 */
  54.  
     
  55.  
    #define _IOC_SIZESHIFT (_IOC_TYPESHIFT + _IOC_TYPEBITS) /* 16 */
  56.  
     
  57.  
    #define _IOC_DIRSHIFT (_IOC_SIZESHIFT + _IOC_SIZEBITS) /* 29 */
  58.  
     
  59.  
    /* 读写域的值 */
  60.  
     
  61.  
    #define _IOC_NONE 1U
  62.  
     
  63.  
    #define _IOC_READ 2U
  64.  
     
  65.  
    #define _IOC_WRITE 4U
  66.  
     
  67.  
     
  68.  
    #define _IOC(dir,type,nr,size) \
  69.  
     
  70.  
    (((dir) << _IOC_DIRSHIFT) | \ /* 读写方向左移 29bit */
  71.  
     
  72.  
    ((type) << _IOC_TYPESHIFT) | \ /* 幻数左移 8bit */
  73.  
     
  74.  
    ((nr) << _IOC_NRSHIFT) | \ /* 命令序号 */
  75.  
     
  76.  
    ((size) << _IOC_SIZESHIFT)) /* 参数大小左移 16bit */
  77.  
     
  78.  
    /* 宏原型,这里将会根据传递的数据类型取其长度 */
  79.  
     
  80.  
    #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
  81.  
     
  82.  
    #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
  83.  
     
  84.  
    #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
  85.  
     
  86.  
    #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
  87.  
     
  88.  
    /* 获取各个域的值 */
  89.  
     
  90.  
    #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
  91.  
     
  92.  
    #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
  93.  
     
  94.  
    #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
  95.  
     
  96.  
    #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

 

这里特别说明一下 _IO 宏,该宏没有可传递的变量,只用于发送命令。这是因为变量需要可变数据,只作为命令(比如 reset)使用时,没有必要判断设备上的数据,因此设备驱动程序没有必要执行文件相关的处理。在 v4l2 中使用示例如下:

​​​​​​​

  1.  
    #define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability)
  2.  
     
  3.  
    #define VIDIOC_RESERVED _IO('V', 1)
  4.  
     
  5.  
    #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format)
  6.  
     
  7.  
    #define VIDIOC_STREAMON _IOW('V', 18, int)

 

v4l2 中对上述宏命令的处理在 video_ioctl2 函数中:​​​​​​​

  1.  
    static unsigned long cmd_input_size(unsigned int cmd)
  2.  
     
  3.  
    {
  4.  
     
  5.  
    #define CMDINSIZE(cmd, type, field) \
  6.  
     
  7.  
    case VIDIOC_##cmd: \
  8.  
     
  9.  
    return offsetof(struct v4l2_##type, field) + \ /* 域的偏移 */
  10.  
     
  11.  
    sizeof(((struct v4l2_##type *)0)->field); /* 域的长度 */
  12.  
     
  13.  
     
  14.  
    switch (cmd) {
  15.  
     
  16.  
    CMDINSIZE(ENUM_FMT, fmtdesc, type);
  17.  
     
  18.  
    CMDINSIZE(G_FMT, format, type);
  19.  
     
  20.  
    ...
  21.  
     
  22.  
    CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format);
  23.  
     
  24.  
    CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height);
  25.  
     
  26.  
    default:
  27.  
     
  28.  
    return _IOC_SIZE(cmd); /* 剩下的是需要全部拷贝的命令 */
  29.  
     
  30.  
    }
  31.  
     
  32.  
    }
  33.  
     
  34.  
     
  35.  
    long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg)
  36.  
     
  37.  
    {
  38.  
     
  39.  
    char sbuf[128]; /* 在栈中分配128个字节空间用来储存命令的参数 */
  40.  
     
  41.  
    void *mbuf = NULL;
  42.  
     
  43.  
    void *parg = NULL; /* 参数存放的首地址 */
  44.  
     
  45.  
    long err = -EINVAL;
  46.  
     
  47.  
    int is_ext_ctrl;
  48.  
     
  49.  
    size_t ctrls_size = 0;
  50.  
     
  51.  
    void __user *user_ptr = NULL;
  52.  
     
  53.  
     
  54.  
    ...
  55.  
     
  56.  
    /* 判断是否包含读写命令,如果是则将用户空间的参数值拷贝到内核 */
  57.  
     
  58.  
    if (_IOC_DIR(cmd) != _IOC_NONE) {
  59.  
     
  60.  
    /* 判断参数大小是否超过128字节 */
  61.  
     
  62.  
    if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
  63.  
     
  64.  
    parg = sbuf;
  65.  
     
  66.  
    } else {
  67.  
     
  68.  
    /* 如果超过128字节则从堆中申请 */
  69.  
     
  70.  
    mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
  71.  
     
  72.  
    if (NULL == mbuf)
  73.  
     
  74.  
    return -ENOMEM;
  75.  
     
  76.  
    parg = mbuf;
  77.  
     
  78.  
    }
  79.  
     
  80.  
     
  81.  
    err = -EFAULT;
  82.  
     
  83.  
    /* 如果包含写命令 */
  84.  
     
  85.  
    if (_IOC_DIR(cmd) & _IOC_WRITE) {
  86.  
     
  87.  
    /* 计算需要拷贝的有效数据长度,有的命令不需要全部拷贝 */
  88.  
     
  89.  
    unsigned long n = cmd_input_size(cmd);
  90.  
     
  91.  
    /* 从用户空间拷贝参数值 */
  92.  
     
  93.  
    if (copy_from_user(parg, (void __user *)arg, n))
  94.  
     
  95.  
    goto out;
  96.  
     
  97.  
     
  98.  
    /* 将剩下的空间清零 */
  99.  
     
  100.  
    if (n < _IOC_SIZE(cmd))
  101.  
     
  102.  
    memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
  103.  
     
  104.  
    } else {
  105.  
     
  106.  
    /* 如果是只读命令则将整个buffer清零 */
  107.  
     
  108.  
    memset(parg, 0, _IOC_SIZE(cmd));
  109.  
     
  110.  
    }
  111.  
     
  112.  
    }
  113.  
     
  114.  
     
  115.  
    ...
  116.  
     
  117.  
    /* 调用 v4l2_ioctl_ops 的成员函数处理命令 */
  118.  
     
  119.  
    err = __video_do_ioctl(file, cmd, parg);
  120.  
     
  121.  
    if (err == -ENOIOCTLCMD)
  122.  
     
  123.  
    err = -EINVAL;
  124.  
     
  125.  
    ...
  126.  
     
  127.  
    if (err < 0)
  128.  
     
  129.  
    goto out;
  130.  
     
  131.  
     
  132.  
    out_ext_ctrl:
  133.  
     
  134.  
    /* 如果包含读命令则将参数值拷贝到用户空间 */
  135.  
     
  136.  
    switch (_IOC_DIR(cmd)) {
  137.  
     
  138.  
    case _IOC_READ:
  139.  
     
  140.  
    case (_IOC_WRITE | _IOC_READ):
  141.  
     
  142.  
    if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
  143.  
     
  144.  
    err = -EFAULT;
  145.  
     
  146.  
    break;
  147.  
     
  148.  
    }
  149.  
     
  150.  
     
  151.  
    out:
  152.  
     
  153.  
    kfree(mbuf);
  154.  
     
  155.  
    return err;
  156.  
     
  157.  
    }
  158.  
     
  159.  
    EXPORT_SYMBOL(video_ioctl2);

 

然后我们在 struct v4l2_file_operations 中将 ioctl 成员设置为 video_ioctl2 即可。

 

Ioctl框架是由v4l2_ioctl.c文件实现,文件中定义结构体数组v4l2_ioctls,可以看做是ioctl指令和回调函数的关系表。用户空间调用系统调用ioctl,传递下来ioctl指令,然后通过查找此关系表找到对应回调函数

IOCTL调用的流程图如下:

 

 

  用户空间通过打开/dev/目录下的设备节点,获取到文件的file结构体,通过系统调用ioctl把cmd和arg传入到内核。通过一系列的调用后最终会调用到__video_do_ioctl函数,然后通过cmd检索v4l2_ioctls[],判断是INFO_FL_STD还是INFO_FL_FUNC。如果是INFO_FL_STD会直接调用到视频设备驱动中video_device->v4l2_ioctl_ops函数集。如果是INFO_FL_FUNC会先调用到v4l2自己实现的标准回调函数,然后根据arg再调用到video_device->v4l2_ioctl_ops或v4l2_fh->v4l2_ctrl_handler函数集。

posted @ 2022-04-17 18:37  张志伟122  阅读(543)  评论(0编辑  收藏  举报