V4L2 学习笔记1-驱动的注册过程2
来源:互联网 发布:python读取jason文件 编辑:程序博客网 时间:2024/05/16 07:50
接下来看一看传感器实体注册的代码,这里面不关实体什么事
static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
{
struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_da
struct fimc_dev *fd = NULL;
int num_clients, ret, i;
/*
* Runtime resume on
* the sclk_cam clocks are not globally disabled.
*/
for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++)
if (fmd->fimc[i])
fd = fmd->fimc[i];
if (!fd)
return -ENXIO;
ret = pm_runtime_get_sync(&fd->pdev->dev);
if (ret < 0)
return ret;
WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor));
fmd->num_sensors = num_clients;
for (i = 0; i < num_clients; i++) {
fmd->sensor[i].pdata = &pdata->isp_info[i];
// 指向了platform_da
ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
if (ret)
break;
fmd->sensor[i].subdev =
fimc_md_register_sensor(fmd, &fmd->sensor[i]);
ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
if (ret)
break;
}
pm_runtime_put(&fd->pdev->dev);
return ret;
}
在mach-goni.c里面定义了如下数据
static struct noon010pc30_platform_da
.clk_rate= 16000000UL,
.gpio_nreset= S5PV210_GPB(2), /* CAM_CIF_NRST */
.gpio_nstby= S5PV210_GPB(0), /* CAM_CIF_NSTBY */
};
// 一定要记住这个名字,noon010,他告诉你了传感器要用的哪个了,接下来跟进i2c这条线路要靠这个了,这个线索也是re serch N多天才发现的。,汗。
static struct i2c_board_info noon010pc30_board_info = {
I2C_BOARD_INFO("NOON010PC30", 0x60 >> 1),
.platform_da
};
static struct s5p_fimc_isp_info goni_camera_sensors[] = {
{
.mux_id= 0,
.flags= V4L2_MBUS_PCLK_SAMPLE_FALLING |
V4L2_MBUS_VSYNC_ACTIVE_LOW,
.bus_type= FIMC_ITU_601,
.board_info= &noon010pc30_board_info,
.i2c_bus_num= 0,
.clk_frequency= 16000000UL,
},
};
struct s5p_platform_fimc goni_fimc_md_platdata __initdata = {
.isp_info= goni_camera_sensors,
.num_clients= ARRAY_SIZE(goni_camera_sensors),
};
static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
struct fimc_sensor_info *s_info)
{
struct i2c_adapter *adapter;
struct v4l2_subdev *sd = NULL;
if (!s_info || !fmd)
return NULL;
adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num);
if (!adapter)
return NULL;
sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
s_info->pdata->board_info, NULL);
if (IS_ERR_OR_NULL(sd)) {
i2c_put_adapter(adapter);
v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n");
return NULL;
}
v4l2_set_subdev_hostdata(sd, s_info);
sd->grp_id = SENSOR_GROUP_ID;
v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
s_info->pdata->board_info->type);
return sd;
}
struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
struct i2c_adapter *adapter, struct i2c_board_info *info,
const unsigned short *probe_addrs)
{
struct v4l2_subdev *sd = NULL;
struct i2c_client *client;
BUG_ON(!v4l2_dev);
request_module(I2C_MODULE_PREFIX "%s", info->type);
/* 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);
// 这里会轮询bus上的所有驱动,找到匹配的,然后调用probe操作。client会在传感器初始化的时候附加子设备,下面就用到了相应的子设备。
/* Note: by loading the module first we are certain that c->driver
will be set if the driver was found. If the module was not loaded
first, then the i2c core tries to delay-load the module for us,
and then c->driver is still NULL until the module is finally
loaded. This delay-load mechanism doesn't work if other drivers
want to use the i2c device, so explicitly loading the module
is the best alternative. */
if (client == NULL || client->driver == NULL)
goto error;
/* Lock the module so we can safely get the v4l2_subdev pointer */
if (!try_module_get(client->driver->driver.owner))
goto error;
sd = i2c_get_clientdata(client); // 在i2c驱动里面整的,参加noon010pc30.c里面的noon010_probe函数。这里逻辑比较绕,得好好理解理解。
/* Register with the v4l2_device which increases the module's
use count as well. */
if (v4l2_device_register_subdev(v4l2_dev, sd))
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
module_put(client->driver->driver.owner);
error:
/* If we have a client but no subdev, then something went wrong and
we must unregister the client. */
if (client && sd == NULL)
i2c_unregister_device(client);
return sd;
}
===============
下面看看建立连接的代码
static int fimc_md_create_links(struct fimc_md *fmd)
{
struct v4l2_subdev *sensor, *csis;
struct s5p_fimc_isp_info *pdata;
struct fimc_sensor_info *s_info;
struct media_entity *source, *sink;
int i, pad, fimc_id = 0;
int ret = 0;
u32 flags;
for (i = 0; i < fmd->num_sensors; i++) {
if (fmd->sensor[i].subdev == NULL)
continue;
sensor = fmd->sensor[i].subdev;
s_info = v4l2_get_subdev_hostdata(sensor);
if (!s_info || !s_info->pdata)
continue;
source = NULL;
pdata = s_info->pdata;
switch (pdata->bus_type) {
case FIMC_MIPI_CSI2:
if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
"Wrong CSI channel id: %d\n", pdata->mux_id))
return -EINVAL;
csis = fmd->csis[pdata->mux_id].sd;
if (WARN(csis == NULL,
"MIPI-CSI interface specified "
"but s5p-csis module is not loaded!\n"))
return -EINVAL;
ret = media_entity_create_link(&sensor->entity, 0,
&csis->entity, CSIS_PAD_SINK,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED);
if (ret)
return ret;
v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
sensor->entity.name, csis->entity.name);
source = &csis->entity;
pad = CSIS_PAD_SOURCE;
break;
case FIMC_ITU_601...FIMC_ITU_656:
source = &sensor->entity;
pad = 0;
break;
default:
v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
pdata->bus_type);
return -EINVAL;
}
if (source == NULL)
continue;
ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad,
fimc_id++);
// 把每一个sensor和 vid cap建立起联系来
}
/* Create immutable links between each FIMC's subdev and video node */
flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
for (i = 0; i < FIMC_MAX_DEVS; i++) {
if (!fmd->fimc[i])
continue;
source = &fmd->fimc[i]->vid_cap.subdev->entity; // 这个又有SOURCEpad又有sink pad,这个实体里面的Pads指针是指向vid_cap.sd_pads的。
// 又将这两个建立起联系了
sink = &fmd->fimc[i]->vid_cap.vfd->entity;
ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
sink, 0, flags);
if (ret)
break;
}
return ret;
}
static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
struct media_entity *source,
struct v4l2_subdev *sensor,
int pad, int fimc_id)
{
struct fimc_sensor_info *s_info;
struct media_entity *sink;
unsigned int flags;
int ret, i;
for (i = 0; i < FIMC_MAX_DEVS; i++) {
if (!fmd->fimc[i])
break;
/*
* Some FIMC variants are not fitted with camera capture
* interface. Skip creating a link from sensor for those.
*/
if (sensor->grp_id == SENSOR_GROUP_ID &&
!fmd->fimc[i]->variant->has_cam_if)
continue;
flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0;
sink = &fmd->fimc[i]->vid_cap.subdev->entity;
ret = media_entity_create_link(source, pad, sink,
FIMC_SD_PAD_SINK, flags);
// 在source和sink双方各增加一个Link,增加相互联系,实际上是将vid cap和sensor 建立起了联系了
if (ret)
return ret;
/* Notify FIMC capture subdev entity */
ret = media_entity_call(sink, link_setup, &sink->pads[0],
&source->pads[pad], flags);
if (ret)
break;
v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
source->name, flags ? '=' : '-', sink->name);
if (flags == 0)
continue;
s_info = v4l2_get_subdev_hostdata(sensor);
if (!WARN_ON(s_info == NULL)) {
unsigned long irq_flags;
spin_lock_irqsave(&fmd->slock, irq_flags);
s_info->host = fmd->fimc[i];
spin_unlock_irqrestore(&fmd->slock, irq_flags);
}
}
return 0;
}
int media_entity_create_link(struct media_entity *source, u16 source_pad,
struct media_entity *sink, u16 sink_pad, u32 flags)
{
struct media_link *link;
struct media_link *backlink;
BUG_ON(source == NULL || sink == NULL);
BUG_ON(source_pad >= source->num_pads);
BUG_ON(sink_pad >= sink->num_pads);
link = media_entity_add_link(source);
if (link == NULL)
return -ENOMEM;
link->source = &source->pads[source_pad];
link->sink = &sink->pads[sink_pad];
link->flags = flags;
/* Create the backlink. Backlinks are used to help graph traversal and
* are not reported to userspace.
*/
backlink = media_entity_add_link(sink);
if (backlink == NULL) {
source->num_links--;
return -ENOMEM;
}
backlink->source = &source->pads[source_pad];
backlink->sink = &sink->pads[sink_pad];
backlink->flags = flags;
link->reverse = backlink;
backlink->reverse = link;
sink->num_backlinks++;
return 0;
}
这个最终结果是将多个传感器(source)连接到了vid_cap.subdev->entity(sink),又将vid_cap.subdev->entity(source)连接到了vid_cap.vfd->entity(sink)。
==============
接下来的操作就是注册子设备的节点
int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
{
struct video_device *vdev;
struct v4l2_subdev *sd;
int err;
/* Register a device node for every subdev marked with the
* V4L2_SUBDEV_FL_HAS_DEVNODE flag.
*/
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
// 对之前挂接到v4l2上的子设备,如果需要建立节点的话,就建立个设备节点。sensor是需要建立节点的,只有传感器和fimc的capture设备需要建立设备节点。
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
continue;
vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
if (!vdev) {
err = -ENOMEM;
goto clean_up;
}
video_set_drvdata(vdev, sd);
strlcpy(vdev->name, sd->name, sizeof(vdev->name));
vdev->v4l2_dev = v4l2_dev;
vdev->fops = &v4l2_subdev_fops;
vdev->release = v4l2_device_release_subdev_node;
vdev->ctrl_handler = sd->ctrl_handler;
err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
sd->owner); // 注册video设备,设备类型是子设备,设备名是以“v4l-subdev”开始的。
if (err < 0) {
kfree(vdev);
goto clean_up;
}
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.info.v4l.major = VIDEO_MAJOR;
sd->entity.info.v4l.minor = vdev->minor;
#endif
sd->devnode = vdev;
}
return 0;
clean_up:
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
if (!sd->devnode)
break;
video_unregister_device(sd->devnode);
}
return err;
}
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
/* A minor value of -1 marks this video device as never
having been registered */
vdev->minor = -1;
/* the release callback MUST be present */
if (WARN_ON(!vdev->release))
return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
INIT_LIST_HEAD(&vdev->fh_list);
/* Part 1: check device type */
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video"; // 图像采集的设备名称以video开始
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev"; // 子设备的名称
break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type);
return -EINVAL;
}
vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->v4l2_dev) {
if (vdev->v4l2_dev->dev)
vdev->parent = vdev->v4l2_dev->dev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
/* If the prio state pointer is NULL, then use the v4l2_device
prio state. */
if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;
}
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
* Newer devices (not yet in place) should use the range
* of 128-191 and just pick the first free minor there
* (new style). */
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
default:
minor_offset = 128;
minor_cnt = 64;
break;
}
#endif
/* Pick a device node number */
mutex_lock(&videodev_lock);
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
if (nr == minor_cnt)
nr = devnode_find(vdev, 0, minor_cnt);
if (nr == minor_cnt) {
printk(KERN_ERR "could not get a free device node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node number to minor number */
i = nr;
#else
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
printk(KERN_ERR "could not get a free minor\n");
return -ENFILE;
}
#endif
vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);
/* Should not happen since we thought this minor was free */
WARN_ON(video_device[vdev->minor] != NULL);
vdev->index = get_index(vdev);
mutex_unlock(&videodev_lock);
/* 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\n", __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);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto cleanup;
}
/* Register the release callback that will be called when the last
reference to the device goes away. */
vdev->dev.release = v4l2_device_release;
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));
/* Increase v4l2_device refcount */
if (vdev->v4l2_dev)
v4l2_device_get(vdev->v4l2_dev);
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
vdev->vfl_type != VFL_TYPE_SUBDEV) {
vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
vdev->entity.name = vdev->name;
vdev->entity.info.v4l.major = VIDEO_MAJOR;
vdev->entity.info.v4l.minor = vdev->minor;
ret = media_device_register_entity(vdev->v4l2_dev->mdev,
&vdev->entity);
if (ret < 0)
printk(KERN_WARNING
"%s: media_device_register_entity failed\n",
__func__);
}
#endif
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
return 0;
cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
vdev->minor = -1;
return ret;
}
================
接下来是注册视频节点
static int fimc_md_register_video_nodes(struct fimc_md *fmd)
{
struct video_device *vdev;
int i, ret = 0;
for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) {
if (!fmd->fimc[i])
continue;
vdev = fmd->fimc[i]->m2m.vfd;
if (vdev) {
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
// 注册m2m设备节点
if (ret)
break;
v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n",
vdev->name, video_device_node_name(vdev));
}
vdev = fmd->fimc[i]->vid_cap.vfd;
if (vdev == NULL)
continue;
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
// 注册capture设备节点
v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n",
vdev->name, video_device_node_name(vdev));
}
return ret;
}
以上是驱动注册的大概过程,就分析到此为止,有问题的朋友可以继续研究一下,欢迎一起探讨。接下来的部分是分析V4L2使用时的代码执行过程。
转载自htt p:// jieohsong.blog.163.com/blog/static/1105308842013546338681/,,,,感谢博主的分享,获益匪浅
- V4L2 学习笔记1-驱动的注册过程2
- V4L2 学习笔记1-驱动的注册过程2
- V4L2 学习笔记1-驱动的注册过程1
- V4L2 学习笔记1-驱动的注册过程1
- V4L2的学习笔记
- 基于V4L2的视频驱动开发笔记(1)
- 基于v4l2的摄像头驱动笔记
- 摄像头驱动笔记1---V4L2框架分析
- 基于V4L2 的视频驱动开发1-2
- Linux驱动学习之---平台驱动的注册过程(转载)
- 基于V4L2的视频驱动开发(2
- V4L2的视频驱动
- v4l2 video设备注册和调用过程
- 开发笔记:从ioctl到linux v4l2驱动的调用
- zturn开发板网口驱动的注册过程
- zturn开发板网口驱动的注册过程
- V4L2驱动编写入门学习
- Camera--V4L2驱动学习记录
- opnet各进程数据共享机制
- 非UI线程更改UI线程3
- Android实现异步处理 -- HTTP请求为例
- Mysql 中自增字段(AUTO_INCREMENT)的一些常识
- KAFKA分布式消息系统
- V4L2 学习笔记1-驱动的注册过程2
- JSTL学习
- Phonotonic音乐设备:将你的运动变成音乐
- 小明的骰子(递推)
- 用smarty实现简单分页的示例完整代码
- git 常用命令手记
- 自己的独立博客已创建
- 暑期读书季——7月推荐给程序员们的书
- 解决Android与服务器交互大容量数据问题