Camera V4L2 아키텍처 분석

11443 단어
V4L2 관련 코드는 linux-3.4/drivers/media/video 디렉토리에 있습니다.
v4l2-dev.c 파일은 장치 노드를 제공하여 사용자층 데이터와 교류를 실현하고 장치 노드는/dev/디렉터리에서 비디오 0, 비디오 1 등의 이름으로 나타난다.등록 문자 장치의 문은 다음과 같습니다.
    /* Part 3: Initialize the character device */
    vdev->cdev = cdev_alloc();
    if (vdev->cdev == NULL) {
        ret = -ENOMEM;
        goto cleanup;
    }   
    vdev->cdev->ops = &v4l2_fops;
    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
", __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);
함수 비디오만 호출하면register_device 는 비디오 하나를device를 V4L2 스키마에 등록합니다.그러나 이것은 하나의 총강령일 뿐이다. Camera 구동은 주로 V4L2의 하위 시스템 기능을 실현하고 하위 시스템은 하나의 v4l2 를 사용한다.subdev 구조체가 설명하는데 시스템이 요구하는 관련 함수 조작을 실현해야만 마지막 단계는 V4L2에 등록할 수 있다.
서브시스템이 실현되는 방식은 플랫폼마다 차이가 있는데 여기서 분석한 것은 전지A23플랫폼의 코드이다.
sunxi-vfe/vfe.c 파일에platform 형식으로 앞뒤 카메라의 플랫폼 자원을 등록했다.일치 프로세스 무시, 최종 vfeprobe 함수가 호출됩니다.probe에서 함수 v4l2 가 보입니다i2c_new_subdev_board:
    /* Create the i2c client */
    if (info->addr == 0 && probe_addrs)
        client = i2c_new_probed_device(adapter, info, probe_addrs,
                           NULL);
    else
        client = i2c_new_device(adapter, info);
...............................
    /* Register with the v4l2_device which increases the module's
       use count as well. */
    if (v4l2_device_register_subdev(v4l2_dev, sd))                                                                                                             
        sd = NULL;
여기의client는 이전에 쓴 I2C 구동의 글을 분석한 결과 구동의 지식은 모두 서로 연결되어 있음을 알 수 있다.I2C 드라이버 기사의 분석을 통해 알 수 있듯이 client를 가져오는 과정에서 deviceregister (&client->dev) 가 호출되고 device와 devicedriver의 모델 관계를 통해 알 수 있듯이 device가 있는bus 버스는 일치하고, device는 i2c 버스 아래에 있으며, I2C match 함수의 일치에 따라 이름이 같은devicedriver가 일치합니다.우리는 i2c 를 가정한다client의name는 "ov5460"입니다. 이렇게 ov5460입니다.c 중:
static struct i2c_driver sensor_driver = {
  .driver = {
    .owner = THIS_MODULE,
  .name = SENSOR_NAME,
  },
  .probe = sensor_probe,                                                                                                                                       
  .remove = sensor_remove,
  .id_table = sensor_id,
};
의심의 여지가 없다,sensorprobe 함수가 호출됩니다.
static int sensor_probe(struct i2c_client *client,                                                                                                             
      const struct i2c_device_id *id)
{
  struct v4l2_subdev *sd;
  struct sensor_info *info;
//  int ret;

  info = kzalloc(sizeof(struct sensor_info), GFP_KERNEL);
  if (info == NULL)
    return -ENOMEM;
  sd = &info->sd;
  glb_sd = sd;
  v4l2_i2c_subdev_init(sd, client, &sensor_ops);

  info->fmt = &sensor_formats[0];
  info->af_first_flag = 1;
  info->init_first_flag = 1;
  info->auto_focus = 0;

  return 0;
}
여기 앞에 언급한 v4l2 가 보입니다.subdev 구조체, v4l2i2c_subdev_init 함수는 v4l2-subdev에 들어갑니다.c 일련의 초기화 작업을 진행하고 i2cset_clientdata(client, sd);나중에 체크 아웃할 수 있도록 서브시스템 포인터를 저장합니다.여기 센소르ops 구조체는 서브시스템에 대해 지원되는 유형입니다.
static const struct v4l2_subdev_ops sensor_ops = {                                                                                                             
  .core = &sensor_core_ops,
  .video = &sensor_video_ops,
};
Camera는 당연히 비디오죠. 코어가 핵심이니까 빼놓을 수 없는 조작이죠.사실 v4l2subdev에서 지원하는 유형은 매우 많습니다. 모든 유형은 다음과 같이 정의됩니다.
struct v4l2_subdev_ops {                                                                                                                                       
    const struct v4l2_subdev_core_ops   *core;
    const struct v4l2_subdev_tuner_ops  *tuner;
    const struct v4l2_subdev_audio_ops  *audio;
    const struct v4l2_subdev_video_ops  *video;
    const struct v4l2_subdev_vbi_ops    *vbi;
    const struct v4l2_subdev_ir_ops     *ir;
    const struct v4l2_subdev_sensor_ops *sensor;
    const struct v4l2_subdev_pad_ops    *pad;
};
tuner는 텔레비전 종류에 속하겠지, 그리고ir, 적외선;오디오비디오에서 수행할 작업을 보려면 다음과 같이 하십시오.
struct v4l2_subdev_video_ops {
    int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);
    int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags);
    int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
    int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
    int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
    int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std);
    int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
    int (*s_stream)(struct v4l2_subdev *sd, int enable);
    int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc);
    int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);
    int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);                                                                                             
    int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
    int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
    int (*g_frame_interval)(struct v4l2_subdev *sd,
                struct v4l2_subdev_frame_interval *interval);
    int (*s_frame_interval)(struct v4l2_subdev *sd,
                struct v4l2_subdev_frame_interval *interval);
    int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize);
    int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival);
    int (*enum_dv_presets) (struct v4l2_subdev *sd,
            struct v4l2_dv_enum_preset *preset);
    int (*s_dv_preset)(struct v4l2_subdev *sd,
            struct v4l2_dv_preset *preset);
    int (*g_dv_preset)(struct v4l2_subdev *sd,
            struct v4l2_dv_preset *preset);
    int (*query_dv_preset)(struct v4l2_subdev *sd,
            struct v4l2_dv_preset *preset);
    int (*s_dv_timings)(struct v4l2_subdev *sd,
            struct v4l2_dv_timings *timings);
    int (*g_dv_timings)(struct v4l2_subdev *sd,
            struct v4l2_dv_timings *timings);
    int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index,
                 enum v4l2_mbus_pixelcode *code);
    int (*enum_mbus_fsizes)(struct v4l2_subdev *sd,
                 struct v4l2_frmsizeenum *fsize);
    int (*g_mbus_fmt)(struct v4l2_subdev *sd,
              struct v4l2_mbus_framefmt *fmt);
    int (*try_mbus_fmt)(struct v4l2_subdev *sd,
                struct v4l2_mbus_framefmt *fmt);
    int (*s_mbus_fmt)(struct v4l2_subdev *sd,
              struct v4l2_mbus_framefmt *fmt);
    int (*g_mbus_config)(struct v4l2_subdev *sd,
                 struct v4l2_mbus_config *cfg);
    int (*s_mbus_config)(struct v4l2_subdev *sd,
                 const struct v4l2_mbus_config *cfg);
};
너무 많다. 이것은 구체적인 카메라가 주로 실현하는 조작이고 ov5640은 그 중의 일부 조작을 지원할 뿐이다.
static const struct v4l2_subdev_core_ops sensor_core_ops = {
  .g_chip_ident = sensor_g_chip_ident,
  .g_ctrl = sensor_g_ctrl,
  .s_ctrl = sensor_s_ctrl,
  .queryctrl = sensor_queryctrl,
  .reset = sensor_reset,
  .init = sensor_init,
  .s_power = sensor_power,
  .ioctl = sensor_ioctl,
};
static const struct v4l2_subdev_video_ops sensor_video_ops = {
  .enum_mbus_fmt = sensor_enum_fmt,                                                                                                                            
  .enum_framesizes = sensor_enum_size,
  .try_mbus_fmt = sensor_try_fmt,
  .s_mbus_fmt = sensor_s_fmt,
  .s_parm = sensor_s_parm,
  .g_parm = sensor_g_parm,
  .g_mbus_config = sensor_g_mbus_config,
};
이렇게 다시 v4l2i2c_new_subdev_board 함수에서 v4l2 호출device_register_subdev는 sd를 서브시스템에 등록했습니다.
vfe로 돌아가다.c의 프로브 함수에서자주 사용하는 조작은 v4l2subdev_사실 이것은 매크로입니다.
#define v4l2_subdev_call(sd, o, f, args...)             \                                                                                                      
    (!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ? \
        (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD))
장점은 서브시스템이 어떤 조작을 지원하는지 확인하는 것이다. 예를 들어 상기 ov5640의sensor 만 지원한다.core_ops와 sensorvideo_ops에서 정의한 작업입니다.다른 것은 error로 돌아갑니다.
마지막:
vfd = video_device_alloc();
*vfd = vfe_template;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, dev->id);
비디오 지정device 공간은 다음과 같은 값과 함께 할당됩니다.
static struct video_device vfe_template = {                                                                                                                    
  .name       = "vfe",
  .fops       = &vfe_fops,
  .ioctl_ops  = &vfe_ioctl_ops,
  .release    = video_device_release,
};
마지막으로 V4L2 시스템에 등록되었습니다.
상부 ioctl 호출부터 분석하면 vdev->fops->ioctl(filp, cmd,arg);여기 있는 vfe를 호출했어fops:
static const struct v4l2_file_operations vfe_fops = {
  .owner          = THIS_MODULE,
  .open           = vfe_open,
  .release        = vfe_close,
  .read           = vfe_read,
  .poll           = vfe_poll,
  .ioctl          = video_ioctl2,                                                                                                                              
  //.unlocked_ioctl = 
  .mmap           = vfe_mmap,
};  
video_ioctl2는 v4l2-ioctl을 호출합니다.c 일반적인 ioctl로 처리되며 사용자 공간 파라미터를 검사할 때 합법적인 동작을 합니다.이 파일에서 호출video_do_ioctl 함수, 모든 V4L2가 지원하는 ioctl 조작 명령을 포함하고 함수가 길면 코드가 붙지 않습니다.이 ioctl에서 최종적으로 vfe 호출ioctl_ops 작업:
static const struct v4l2_ioctl_ops vfe_ioctl_ops = {                                                                                                           
  .vidioc_querycap          = vidioc_querycap,
  .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
  .vidioc_enum_framesizes   = vidioc_enum_framesizes,
  .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
  .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
  .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
  .vidioc_reqbufs           = vidioc_reqbufs,
  .vidioc_querybuf          = vidioc_querybuf,
  .vidioc_qbuf              = vidioc_qbuf,
  .vidioc_dqbuf             = vidioc_dqbuf,
  .vidioc_enum_input        = vidioc_enum_input,
  .vidioc_g_input           = vidioc_g_input,
  .vidioc_s_input           = vidioc_s_input,
  .vidioc_streamon          = vidioc_streamon,
  .vidioc_streamoff         = vidioc_streamoff,
  .vidioc_queryctrl         = vidioc_queryctrl,
  .vidioc_g_ctrl            = vidioc_g_ctrl,
  .vidioc_s_ctrl            = vidioc_s_ctrl,
  .vidioc_g_parm            = vidioc_g_parm,
  .vidioc_s_parm            = vidioc_s_parm,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
  .vidiocgmbuf              = vidiocgmbuf,
#endif
};
이러한 통용되는 함수 조작용, 최종적으로 v4l2subdev_콜은 ov5640과 같은 구체적인 Camera 조작을 호출합니다

좋은 웹페이지 즐겨찾기