1. 用户空间进行open操作
在《Linux camera驱动(3) - 框架》中master 端进行初始化和注册过程中添加了file_operations结构体
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.open = v4l2_open,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
.release = v4l2_release,
};
2. open操作的整体流程
v4l2_open
-->mxc_v4l_open
-->ipu_csi_init_interface(...,cam_fmt.fmt.pix.pixelformat,csi_param)
-->cam_fmt.fmt.pix.pixelformat
-->vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt)
-->ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
-->f->fmt.pix = sensor->sen.pix
-->struct sensor *sensor = s->priv
3. open操作的详细流程
//mxc_v4l2_capture.c
static struct v4l2_file_operations mxc_v4l_fops = {
.owner = THIS_MODULE,
.open = mxc_v4l_open,
.ioctl = mxc_v4l_ioctl,
.mmap = mxc_mmap,
};
static struct video_device mxc_v4l_template = {
.name = "Mxc Camera",
.fops = &mxc_v4l_fops,
};
//v4l2-dev.c
static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev;
int ret = 0;
/*这里的vdev即init_camera_struct()函数中填充的cam->video_dev=mxc_v4l_template,可以回看《Linux camera驱动(3) - 框架》进行确认*/
vdev = video_devdata(filp);
video_get(vdev);
if (vdev->fops->open) {
if (video_is_registered(vdev))
ret = vdev->fops->open(filp); /*vdev->fops等于mxc_v4l_fops结构体,即调用mxc_v4l_open函数*/
else
ret = -ENODEV;
}
return ret;
}
static int mxc_v4l_open(struct file *file)
{
struct v4l2_ifparm ifparm;
struct v4l2_format cam_fmt;
ipu_csi_signal_cfg_t csi_param;
struct video_device *dev = video_devdata(file); /* 获取video_device结构体 */
cam_data *cam = video_get_drvdata(dev);
int err = 0;
struct sensor_data *sensor;
/*如果cam->sensor不存在或者它的类型不是slave就返回-EAGAIN*/
/*这个cam->sensor是在mxc_v4l2_master_attach函数中通过cam->sensor= slave设置的,所以cam->sensor就代表当前使用的slave设备*/
if (cam->sensor == NULL ||
cam->sensor->type != v4l2_int_type_slave) {
pr_err("ERROR: v4l2 capture: slave not found!\n");
return -EAGAIN;
}
/*这个cam->sensor->priv是在slave设备的probe函数中设置的*/
sensor = cam->sensor->priv;
/*这个power_queue等待队列是在init_camera_struct函数中初始化的,等待上电以后继续往下运行,在mxc_v4l2_resume函数中,会将low_power标志位置为false,然后唤醒这个队列*/
if (cam->open_count++ == 0) {
wait_event_interruptible(cam->power_queue, cam->low_power == false);
/*cam->current_input在init_camera_struct中被初始化为0,所以会执行prp_enc_select()函数*/
if (strcmp(mxc_capture_inputs[cam->current_input].name, "CSI MEM") == 0) {
err = csi_enc_select(cam);
} else if (strcmp(mxc_capture_inputs[cam->current_input].name, "CSI IC MEM") == 0) {
err = prp_enc_select(cam);
}
/*初始化三个队列头,这3个队列是在使用buffer过程中需要使用的*/
cam->enc_counter = 0;
INIT_LIST_HEAD(&cam->ready_q);
INIT_LIST_HEAD(&cam->working_q);
INIT_LIST_HEAD(&cam->done_q);
vidioc_int_g_ifparm(cam->sensor, &ifparm);/*从底层的slave设备里获取参数,跟cam->sensor没啥关系*/
csi_param.mclk = ifparm.u.bt656.clock_curr; /*根据上面从底层slave设备获取的参数来对csi_param进行初始化*/
vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
/*在init_camera_struct中直接将窗口位置和大小设置为(0,0)和640*480,现在根据从底层slave设备中获取的参数重新设置窗口位置和大小*/
ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, cam->crop_current.height, cam->csi);
ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, cam->crop_current.top, cam->csi);
/*根据这几个参数设置底层ipu寄存器的值,CSI_CCIR_CODE_1~3等寄存器*/
ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width,cam->crop_bounds.height,cam_fmt.fmt.pix.pixelformat,csi_param);
vidioc_int_s_power(cam->sensor, 1); /*最终会调用到slave里的ioctl_s_power函数,打开设备电源;(软件意义上的电源)*/
vidioc_int_init(cam->sensor); /*调用ioctl_init函数,空函数*/
vidioc_int_dev_init(cam->sensor); /*调用ioctl_dev_init函数,初始化摄像头本身的一些寄存器的值*/
}
file->private_data = dev;
}
int32_t ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, uint32_t pixel_fmt, ipu_csi_signal_cfg_t cfg_param)
{
uint32_t data = 0;
uint32_t csi = cfg_param.csi;
/*首先根据传进来的不同pixel_fmt参数的值,设置cfg_param.data_fmt的值*/
switch (pixel_fmt) {
case IPU_PIX_FMT_YUYV: /*ov5640里的pixel_fmt是这个*/
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
break;
case IPU_PIX_FMT_UYVY: /*ak8859和max9288里的pixel_fmt都是这个*/
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
break;
case IPU_PIX_FMT_RGB24:
······
default:
return -EINVAL;
}
/* Setup sensor frame size */ /*根据传进来的width和height参数,设置CSI_SENS_FRM_SIZE寄存器的值*/
ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE);
/* Set CCIR registers */ /*根据cfg_param.clk_mode参数的不同值来设置CSI_CCIR_CODE_1~3寄存器的值*/
if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) { /*在open函数中被赋值为0,所以这里不执行*/
ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
}
else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) || (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) {
_ipu_csi_ccir_err_detection_disable(ipu, csi);
}
return 0;
}
vidioc_int_g_fmt_cap()即ioctl_g_fmt_cap()
static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
{
struct sensor *sensor = s->priv;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
f->fmt.pix = sensor->sen.pix;
break;
case V4L2_BUF_TYPE_PRIVATE: {
v4l2_std_id std;
ak8859_get_std(&std);
f->fmt.pix.pixelformat = (u32)std;
}
break;
default:
f->fmt.pix = sensor->sen.pix;
break;
}
return 0;
}