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_data;

struct fimc_dev *fd = NULL;

int num_clients, ret, i;

/*

 * Runtime resume one of the FIMC entities to make sure

 * 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_data里面的isp_info

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_data noon010pc30_pldata = {

.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_data = &noon010pc30_pldata,

};

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);

// sourcesink双方各增加一个Link,增加相互联系,实际上是将vid capsensor 建立起了联系了

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是需要建立节点的,只有传感器和fimccapture设备需要建立设备节点。

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/,,,,感谢博主的分享,获益匪浅

0 0