V4L2源代码之旅七:源码追踪

1. v4l2_device_register

/* kernel/drivers/media/video/v4l2-device.c */
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
    if (v4l2_dev == NULL)
        return -EINVAL;

    INIT_LIST_HEAD(&v4l2_dev->subdevs);  // 初始化一个将要挂在v4l2_device上的子设备列表
    spin_lock_init(&v4l2_dev->lock);     // 初始化一个spin lock
    mutex_init(&v4l2_dev->ioctl_lock);   // 初始化一个mutex,看来会在ioctrl上使用的mutex
    v4l2_prio_init(&v4l2_dev->prio);     // 优先级
    kref_init(&v4l2_dev->ref);           // 将v4l2_dev的父设备指定为struct device *dev
    v4l2_dev->dev = dev;                 // 父设备
    if (dev == NULL) {                   // 如果dev为空,v4l2->dev->name必须设置
        /* If dev == NULL, then name must be filled in by the caller */
        WARN_ON(!v4l2_dev->name[0]);
        return 0;
    }

    /* Set name to driver name + device name if it is empty. */
    if (!v4l2_dev->name[0])
        snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",  // 如果v4l2_dev->name为空,那么由dev_name(dev)来设置 driver name + device name
            dev->driver->name, dev_name(dev));
    if (!dev_get_drvdata(dev))
        dev_set_drvdata(dev, v4l2_dev);   // 将dev和v4l2_dev关联:dev->p->driver_data = v4l2_dev
    return 0;
}
EXPORT_SYMBOL_GPL(v4l2_device_register);
int dev_set_drvdata(struct device *dev, void *data)
{
    int error;

    if (!dev->p) {
        error = device_private_init(dev);
        if (error)
            return error;
    }
    dev->p->driver_data = data;
    return 0;
}
EXPORT_SYMBOL(dev_set_drvdata);

2. v4l2_i2c_new_subdev_board

/* Load an i2c sub-device. */
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);   // 此时会调用ov8858的probe函数,在probe函数中调用v4l2_i2c_subdev_init(sd, client, &ov8858_ops);

    /* 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);    // 在ov8858这个module加载的时候会调用v4l2_i2c_subdev_init,在这个函数值sd和client发生关系,所以此时可以通过client获取sd

    /* Register with the v4l2_device which increases the module‘s
       use count as well. */
    if (v4l2_device_register_subdev(v4l2_dev, sd))   // v4l2_device_register_subdev
        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;
}
EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board);

  上边我们说到在加载ov8858 module时,调用了至关重要的函数:v4l2_i2c_subdev_init:

/* v4l2-common.c */
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
        const struct v4l2_subdev_ops *ops)                                     // ops 由于我们是OV8858,是一个Camera Sensor,所以只实现了core和video的ops
{
    v4l2_subdev_init(sd, ops);
    sd->flags |= V4L2_SUBDEV_FL_IS_I2C;                   // 这个宏的注释:Set this flag if this subdev is a i2c device.
    /* the owner is the same as the i2c_client‘s driver owner */
    sd->owner = client->driver->driver.owner;
    /* i2c_client and v4l2_subdev point to one another */
    v4l2_set_subdevdata(sd, client);                      // sd->dev_priv = client
    i2c_set_clientdata(client, sd);                       // client->dev->p->driver_data = sd;
    /* initialize name */
    snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
        client->driver->driver.name, i2c_adapter_id(client->adapter),
        client->addr);
}
EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
 1 /* v4l2-subdev.c */
 2 void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
 3 {
 4     INIT_LIST_HEAD(&sd->list);
 5     BUG_ON(!ops);
 6     sd->ops = ops;
 7     sd->v4l2_dev = NULL;
 8     sd->flags = 0;
 9     sd->name[0] = ‘\0‘;
10     sd->grp_id = 0;
11     sd->dev_priv = NULL;
12     sd->host_priv = NULL;
13 #if defined(CONFIG_MEDIA_CONTROLLER)
14     sd->entity.name = sd->name;
15     sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
16 #endif
17 }
18 EXPORT_SYMBOL(v4l2_subdev_init);
1 /* v4l2-subdev.h */
2 static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p)
3 {
4     sd->dev_priv = p;
5 }
/*  */
static inline void i2c_set_clientdata(struct i2c_client *dev, void *data)
{
    dev_set_drvdata(&dev->dev, data);
}

/* dd.c */
int dev_set_drvdata(struct device *dev, void *data)
{
    int error;

    if (!dev->p) {
        error = device_private_init(dev);
        if (error)
            return error;
    }
    dev->p->driver_data = data;
    return 0;
}
EXPORT_SYMBOL(dev_set_drvdata);

  在v4l2_i2c_new_subdev_board中,加载ov8858 module,并调用v4l2_i2c_subdev_init实现v4l2_subdev的初始化后,又调用了v4l2_device_register_subdev来注册subdev.

int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
                struct v4l2_subdev *sd)
{
#if defined(CONFIG_MEDIA_CONTROLLER)
    struct media_entity *entity = &sd->entity;
#endif
    int err;

    /* Check for valid input */
    if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
        return -EINVAL;

    /* Warn if we apparently re-register a subdev */
    WARN_ON(sd->v4l2_dev != NULL);

    if (!try_module_get(sd->owner))
        return -ENODEV;

    sd->v4l2_dev = v4l2_dev;       // 此时,subdev和v4l2_dev发生关系:sd->v4l2_dev = v4l2_dev;
    if (sd->internal_ops && sd->internal_ops->registered) {
        err = sd->internal_ops->registered(sd);
        if (err) {
            module_put(sd->owner);
            return err;
        }
    }

    /* This just returns 0 if either of the two args is NULL */
    err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
    if (err) {
        if (sd->internal_ops && sd->internal_ops->unregistered)
            sd->internal_ops->unregistered(sd);
        module_put(sd->owner);
        return err;
    }

#if defined(CONFIG_MEDIA_CONTROLLER)
    /* Register the entity. */
    if (v4l2_dev->mdev) {
        err = media_device_register_entity(v4l2_dev->mdev, entity);
        if (err < 0) {
            if (sd->internal_ops && sd->internal_ops->unregistered)
                sd->internal_ops->unregistered(sd);
            module_put(sd->owner);
            return err;
        }
    }
#endif

    spin_lock(&v4l2_dev->lock);
    list_add_tail(&sd->list, &v4l2_dev->subdevs);
    spin_unlock(&v4l2_dev->lock);

    return 0;
}
EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);

3. video_device_alloc

/* v4l2-dev.c */struct video_device *video_device_alloc(void)
{
    return kzalloc(sizeof(struct video_device), GFP_KERNEL);
}
EXPORT_SYMBOL(video_device_alloc);

4. video_register_device

/* v4l2-dev.h */
/* Register video devices. Note that if video_register_device fails,
   the release() callback of the video_device structure is *not* called, so
   the caller is responsible for freeing any data. Usually that means that
   you call video_device_release() on failure. *//*  注册video_device。如果注册失败,vieo_device的release()函数将不会被回调。因此,video_register_device的调用者应该来释放数据。  通常在注册失败时,调用video_device_release即可。*//*    我们是这么调用的:      ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);  第二个参数是type:#define VFL_TYPE_GRABBER    0               表明一个图像采集设备——包括摄像头/调谐器,诸如此类#define VFL_TYPE_VBI        1               代表设备是从视频消隐时间段取得信息的设备#define VFL_TYPE_RADIO      2         代表无线电设备#define VFL_TYPE_SUBDEV     3         不知道代表什么,可能是代表除上述以外其他的设备吧#define VFL_TYPE_MAX        4         最大值*/
static inline int __must_check video_register_device(struct video_device *vdev,
        int type, int nr)
{
    return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
}
/**
 *    __video_register_device - register video4linux devices
 *    @vdev: video device structure we want to register
 *    @type: type of device to register
 *    @nr:   which device node number (0 == /dev/video0, 1 == /dev/video1, ...
 *             -1 == first free)
 *    @warn_if_nr_in_use: warn if the desired device node number
 *           was already in use and another number was chosen instead.
 *    @owner: module that owns the video device node
 *
 *    The registration code assigns minor numbers and device node numbers
 *    based on the requested type and registers the new device node with
 *    the kernel.
 *
 *    This function assumes that struct video_device was zeroed when it
 *    was allocated and does not contain any stale date.
 *
 *    An error is returned if no free minor or device node number could be
 *    found, or if the registration of the device node failed.
 *
 *    Zero is returned on success.
 *
 *    Valid types are
 *
 *    %VFL_TYPE_GRABBER - A frame grabber
 *
 *    %VFL_TYPE_VBI - Vertical blank data (undecoded)
 *
 *    %VFL_TYPE_RADIO - A radio card
 *
 *    %VFL_TYPE_SUBDEV - A subdevice
 */
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;  // 将minor设置为-1,,标志video device从没有被注册过

    /* the release callback MUST be present */
    WARN_ON(!vdev->release);   // release回调函数必须设置
    if (!vdev->release)
        return -EINVAL;

    /* v4l2_fh support */
    spin_lock_init(&vdev->fh_lock);   // fh_lock:Lock for all v4l2_fhs
    INIT_LIST_HEAD(&vdev->fh_list);   // fb_list:List of struct v4l2_fh

    /* Part 1: check device type */
    switch (type) {
    case VFL_TYPE_GRABBER:
        name_base = "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;  // 如果vdev->v4l2_dev->dev不为空,那么将vdev的父设备和vdev->v4l2_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;  // 如果vdev没有设置优先级proi,那么使用vdev->v4l2_dev的优先级
    }

    /* 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.v4l.major = VIDEO_MAJOR;
        vdev->entity.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;
}
EXPORT_SYMBOL(__video_register_device);

  说了一大堆,核心部分就是video_device的成员的设置和字符设备的添加。

5. video_set_drvdata

  由于我们将video_device结构体嵌入到一个更大的结构体ovisp_camera_dev中,所有video_device和ovisp_camera_dev发生关系是必然的:

/* v4l2-dev.c */static inline void video_set_drvdata(struct video_device *vdev, void *data)
{
    dev_set_drvdata(&vdev->dev, data);  // videv->dev->p->driver_data = data
}
/* dd.c */int dev_set_drvdata(struct device *dev, void *data)
{
    int error;

    if (!dev->p) {
        error = device_private_init(dev);
        if (error)
            return error;
    }
    dev->p->driver_data = data;
    return 0;
}
EXPORT_SYMBOL(dev_set_drvdata);
时间: 2024-11-07 13:19:01

V4L2源代码之旅七:源码追踪的相关文章

mybatis源码追踪1——Mapper方法用法解析

Mapper中的方法执行时会构造为org.apache.ibatis.binding.MapperMethod$MethodSignature对象,从该类源码中可以了解如何使用Mapper方法. [支持的特殊参数类型] RowBounds.ResultHandler.普通参数 (作为sql执行时使用的变量) 其中普通参数可以是单一的model.查询条件的map或直接将一到多个查询条件作为参数(多个条件在框架中最终将封装为map使用) 另外普通参数支持添加@Param注解以修改参数名,如不修改则参

源码追踪,解决Could not locate executable null\bin\winutils.exe in the Hadoop binaries.问题

在windows系统本地运行spark的wordcount程序,会出现一个异常,但不影响现有程序运行. >>提君博客原创  http://www.cnblogs.com/tijun/  << 总归是一个异常,老是爆红,看着心烦,下面是异常信息 提君博客原创 让我们源码追踪一下,看看到底是什么原因导致,点击第一行爆红的异常信息提示,就是(shell.java:355) 看到我的截图提示,大概了解什么原因了.发现HADOOP_HOME_DIR为null,右边outline里面找到一个私

CAS源码追踪系列二:AuthenticationFilter对于请求的处理

上一篇我们说了在web项目中了和spring整合之后,如何进行对应Filter的初始化,如果你还没看过,请点击 <CAS源码追踪系列一:Filter的初始化>. 本篇我们来看看在初始化完成以后,cas-client是如何处理请求的. 源码地址:https://github.com/apereo/java-cas-client 如何你还不太清楚sso的原理,你可以看看这篇文章<单点登录原理与简单实现>. 当访问系统受保护的资源时,cas的过滤器AuthenticationFilter

CAS源码追踪系列三:cas-server端对请求的处理

目录 InitialFlowSetupAction ServiceAuthorizationCheck AuthenticationViaFormAction SendTicketGrantingTicketAction GenerateServiceTicketAction 第一次访问接入cas的另一个应用系统 总结 系列: CAS源码追踪系列一:Filter的初始化 CAS源码追踪系列二:AuthenticationFilter对于请求的处理 上一篇,我们了解了AuthenticationF

从使用Handler致内存泄漏角度源码追踪Handler工作机制

使用Handler时内存泄漏分析 在Android中,处理完异步任务后常常会在主线程进行一些操作,所以我们可能会使用到Handler,下面是Handler的常见使用方法: public class MainActivity extends AppCompatActivity { private Handler mHanlder = new Handler() { @Override public void handleMessage(Message msg) { //TODO } }; } 但是

源码追踪经验谈(侯捷)

1. 前提:在深入框架底层以前,先学会使用它做一个大概认识2. 书籍:找正确的书籍,其实网上就有很多3. 工具:grep, windiff, IDE debugger(CallStack窗口等等), Spy++, TDump, Source Navigator4. 方法:动手5. 状态:搞清文件命名方式6. 线头:找到入口7. 笔记:必须战战兢兢.心无旁骛,有心得的时候做一些笔记,最好还能截图,配以少量文字,20年之内可复习一小时就明白了.8. 瓶颈:知识水平与阅读对象差距太远,就必须补基础9.

CAS源码追踪系列一:Filter的初始化

目录 代码跟踪 Spring-web:DelegatingFilterProxy CAS:AuthenticationFilter 总结 最近研究了一下SSO(Single Sign On:单点登录)原理. 于是想借助CAS(基于SSO原理的实现框架)加深一下理解同时参考一下具体代码实现,因此有了此系列文章. 先从CAS-CLIENT说起. 假设你已经掌握了如何在你的web项目中引入CAS.我们以AuthenticationFilter为例,说一说它是如何从初始化的. 代码跟踪 Spring-w

1、Hibernate之生成SessionFactory源码追踪

Hibernate的所有session都是由sessionFactory来生成的,那么,sessionFactory是怎么得来的呢?它与我们配置的xxx.cfg.xml文件以及xxx.hbm.xml文件之间又有着怎么样的联系呢? 先看一小段生成sessionFactory的代码: code_1: public class HibernateTest { @Test public void test() { System.out.println("test..."); //1. 创建一个

Android源代码 之 构建 编译源码的环境(Ubuntu篇)

官方已经在Ubuntu LTS (14.04)上对源代码进行了测试.其他系统也可以,笔者建议使用Ubuntu LTS (14.04). 一.系统要求: 1.Linux   或    Mac    (也可在虚拟机上的Linux下进行) 2.若要编译Gingerbread (2.3.x)及以上版本的源代码,需要64位的系统环境,Gingerbread (2.3.x)以前的版本可以在32位系统上编译. 源代码版本名称和版本号.API级别列表见:http://blog.csdn.net/u0136474