感覺寫完了其實也沒怎麼理解這個函數,但是還是這裡儲存一下,希望大家予以拍磚指導:
下面分析一下video_register_device這個函數:位於include/media/v4l2-dev.h標頭檔中:
參考文獻:http://blog.sina.com.cn/s/blog_602f87700101a52s.html
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函數:
其中:第一個參數struct video_device *vdev是想註冊的video device的結構體;
第二個參數是視頻裝置類型,在此標頭檔(v4l2-dev.h中),定義了幾種視頻裝置類型:
#defineVFL_TYPE_GRABBER 0
//映像採集裝置
#defineVFL_TYPE_VBI 1
//從視頻消隱的時間段獲得資訊的裝置
#defineVFL_TYPE_RADIO 2
//無線電裝置
#defineVFL_TYPE_SUBDEV 3
//視頻裝置(不確定)
#defineVFL_TYPE_MAX 4 //最大值
第三個參數是註冊的裝置節點號;
video_register_device函數主要是註冊一個V4L2device,指定類型,然後指定device node number。函數只是簡單的調用了__video_register_device函數。
按照參考文檔的說明:這個__video_register_device函數主要做的工作有:1、初始化一些值;2、建立字元模式驅動;3、註冊字元裝置等。
下面我來分析這個函數,這個函數的定義在v4l2-dev.c中,聲明在v4l2-dev.h中:
int__video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
前三個參數與video_register_device中相同,第四個參數不知道,第五個參數表面指向當前的模組;
int i =0;
intret;
intminor_offset = 0;
intminor_cnt = VIDEO_NUM_DEVICES;
constchar *name_base;
聲明了一些變數;其中minor_cnt的賦值VIDEO_NUM_DEVICES仍定義在v4l2-dev.c中:
#defineVIDEO_NUM_DEVICES 256
聲明最大的裝置數量為256個?
/* A minor value of -1marks this video device as never
having beenregistered */
vdev->minor = -1;
-1表明這個video device從未被註冊過(其實是指之前註冊失敗)。
/* the release callbackMUST be present */
if (WARN_ON(!vdev->release))
return -EINVAL;
聲明release回呼函數。
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
INIT_LIST_HEAD(&vdev->fh_list);
調用自旋鎖和初始化鏈表。
下面開始進入各個部分的工作:
第一部分:
/* Part 1: check devicetype */
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;
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;
}
這裡是填充struct video_device的一些欄位。
第二部分:
註冊device node number等。
/* Part 2: find a freeminor, device node number and device index. */
#ifdefCONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
* Newer devices (notyet in place) should use the range
* of 128-191 and justpick 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
定義minor_offset和minor_cnt。
/* 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 nodenumber\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
註冊device node number。
函數devnode_find:發現節點號。
#ifdefCONFIG_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 thefirst 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 freeminor\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);
/* if no lock was passed, then make sure the LOCK_ALL_FOPS bit is
clear and warn if itwasn't. */
if (vdev->lock == NULL)
WARN_ON(test_and_clear_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags));
if(vdev->ioctl_ops)
determine_valid_ioctls(vdev);
我都搞暈了,不知道第二部分在搞什麼。
第三部分:初始化字元裝置
/* Part 3: Initialize thecharacter 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;
}
調用cdev_add註冊字元裝置。這個看的比較簡單。
第四部分:
註冊device
/* Part 4: register thedevice 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_registerfailed\n", __func__);
goto cleanup;
}
在sysfs中註冊device。
/* Register the release callback that will be called when thelast
reference to thedevice goes away. */
vdev->dev.release = v4l2_device_release;
註冊回呼函數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_devicerefcount */
if(vdev->v4l2_dev)
v4l2_device_get(vdev->v4l2_dev);
第五部分:
在定義了CONFIG_MEDIA_CONTROLLER後才有第五部分,註冊entity(實體?)
#ifdefined(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 thisminor. 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);
解鎖,啟用這一字元裝置。
最後:
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;
相關的一些清理操作。
總之呢,video_register_device的作用就是註冊一個video device,並完成相關裝置初始化的一些工作。