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

转自:http://www.cnblogs.com/ronnydm/p/5785265.html

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);
复制代码

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值