http://blog.csdn.net/leesagacious/article/details/49948163
/** 注册一个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); }
