video
video_register_device()浅析/**注册⼀个video_device如果注册失败,video_device中的release()函数不会被调⽤。调⽤者负责释放所有的数据,通常是调⽤video_device_release()函数来释放uvc_driver.cstatic 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_devicetype : 设备的类型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_offsetint 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_handlerv4l2_ctrl_handler_init(hdl, 11);创建v4l2_ctrl 并放⼊到v4l2_ctrl_handler链表v4l2_ctrl_new_std()...v4l2_ctrl_new_custom()dev->l_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)win7 7600激活工具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;}#endifmutex_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];360安全卫士 下载从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;}魔兽争霸3冰封王座修改器/**常用语不分青红皂白中皂是指设置file_operations为v4l2_fops⽤户层调⽤open、mmap、ioctl、的时候这个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);联想z480/**所有的设备注册的时候,都会经过这个⽅法。所有的驱动注册的时候,都会经过driver_register()所有的总线注册的时候,都会经过bus_register()这三个函数的浅析请见另篇博⽂*/ret = device_register(&vdev->dev);if (ret < 0) {printk(KERN_ERR "%s: device_register failed\n", __func__);goto cleanup;}vdev-&lease = 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-&pe = 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);}设备,函数,注册,节点,获取,设置,时候