/**
注册一个video_device
如果注册失败,video_device中的release()函数不会被调用。
调用者负责释放所有的数据,通常是调用video_device_release()函数来释放
uvc_driver.c
static int uvc_register_video()
{
struct video_device *vdev;
...
vdev = video_device_alloc();
vdev->fops = &uvc_fops;
vdev->release = uvc_release;
....
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",ret);
stream->vdev = NULL;
video_device_release(vdev);
return ret;
}
}
*/
static inline int __must_check video_register_device(struct video_device *vdev)
{
return __video_register_device(vdev,type,nr,1,vdev->fops->owner);
}
/**
注册一个video4linux设备
参数:
vdev : 要注册的video_device
type : 设备的类型
nr : 一般都选择使用 -1,代表生成设备节点号在原来号上递增。
*/
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; //minor = i + minor_offset
int minor_cnt = VIDEO_NUM_DEVICES;//用于设备节点序号
const char * name_base; //设备的名称会根据传入的type来选择
/**
次设备号设置为-1,表示这个video_device还没有被注册过
*/
vdev->minor = -1;
/**
这个video_device 一定要设置release函数,否则要出错返回
*/
if (WARN_ON(!vdev->release))
{
return -EINVAL;
}
spin_lock_init(&vdev->fh_lock); //获取自旋锁
INIT_LIST_HEAD(&vdev->fh_list); //初始化链表头
/**
根据传入的类型type,选择设备的名称
后面会调用下面的这个函数 来设置设备的名称
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
*/
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)
/*
在注册video_device之前的vivi_create_instance()函数中,
初始化v4l2_ctrl_handler
v4l2_ctrl_handler_init(hdl, 11);
创建v4l2_ctrl 并放入到v4l2_ctrl_handler链表
v4l2_ctrl_new_std()
...
v4l2_ctrl_new_custom()
dev->v4l2_dev.ctrl_handler = hdl;
dev->v4l2_dev : v4l2_device 每一个v4l2设备都用这个结构来描述
下面的代码是将v4l2_ctrl_handler与video_device进行了关联
*/
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;
}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/**
根据传入的类型type,为选择次设备号、设备节点序号作准备
可见次设备号是分段使用的。
minor = i + minor_offset
*/
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
mutex_lock(&videodev_lock);//获取互斥锁
//获取一个没有被使用的设备节点序号
//如果 上面传入的是 -1,VFL_TYPE_GRABBER,那么他会从0 - 64中选择
nr = devnode_find(vdev,nr == -1 ? 0 : nr,minor_cnt);
if (nr == minor_cnt) {
printk(KERN_ERR "could not get a free device node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
/**
static struct video_device *video_device[256];
从video_device[]数组中选择一个空缺项,这个空缺项的索引值放到i中
为下面把video_device放入到video_device[i]中做准备,这个设计和registered_fb[]设计的类似
*/
for (int i = 0; i < VIDEO_NUM_DEVICES; ++i)
{
if (video_device[i] == NULL)
{
break;
}
}
//检查i的值有没有超过256,否则出错返回
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
printk(KERN_ERR "could not get a free minor\n");
return -ENFILE;
}
//设备的次设备号
vdev->minor = i + minor_offset;
vdev->num = nr;
devnode_set(vdev);
//再一次测试这个video_device[i]是否是空缺项
WARN_ON(video_device[vdev->minor] != NULL);
vdev->index = get_index(vdev);
mutex_unlock(&videodev_lock);//释放互斥锁
//分配cdev结构体 cdev代表了字符设备的通用信息,通常被嵌入在一个更大的结构体中
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL)
{
ret = -ENOMEM;
goto cleanup;
}
/**
设置file_operations为v4l2_fops
用户层调用open、mmap、ioctl、read...的时候
这个v4l2_fops中的对应的方法会响应。
如 :
用户层调用mmap(),
那么v4l2_fops中的v4l2_mmap()会被调用
在v4l2_mmap()中,会调用具体设备提供的mmap函数
static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{
....
ret = vdev->fops->mmap(filp, vm);
....
}
*/
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;
}
//填充video_device中的成员值
//设置video_device所属的类,会在/sys/class/下创建目录
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);
/**
所有的设备注册的时候,都会经过这个方法。
所有的驱动注册的时候,都会经过driver_register()
所有的总线注册的时候,都会经过bus_register()
这三个函数的浅析请见另篇博文
*/
ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto cleanup;
}
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));
if (vdev->v4l2_dev)
v4l2_device_get(vdev->v4l2_dev);
#if defined(CONFIG_MEDIA_CONTROLLER)
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
/*将这个unsigned long flags 的第0 位 置1,表示这个video_device 是注册过的了,
在其他位置,会调用video_is_registeried( ) 来判断,其依据 还是测试这个flags的第0位。
video_is_registered( )
{
test_bit( V4L2_FL_REGISTERED, &vdev->flags )
}
*/
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
//获取锁 ---- 访问临界区 -----释放锁
mutex_lock(&videodev_lock);
/**
依据次设备号为下标,将设置好的video_device放入到video_device[]中
其他函数会 依据次设备号 从这个数组中获取对应的video_device,这个和registered_fb[]设计的类似
static struct video_device*video_device[256];
*/
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
}
video_register_device()浅析
最新推荐文章于 2024-06-05 16:03:02 发布