linux下的cmos摄像头驱动设计1-video驱动的注册
2014-11-30 21:23
302 查看
linux下的cmos驱动设计分析:
cmos摄像头驱动设计主要分为几部分:1.ARM处理器摄像头接口部分,2.通过i2c接口控制的摄像头芯片部分,被称为senor部分,3.就是上述两部分直接的联系部分,写在板级文件mach-boardname.c文件中。
其实这个摄像头驱动不是太难,摄像头驱动设计的目标就是,ARM处理器控制摄像头芯片,使其能够采集图像然后处理器把图像读出来。
围绕着这个目的,linux系统提供了v4l2架构,架构的目的是让我们程序员开发的时候更加方便,有个标准的规范,我们程序员在开发驱动的时候,就按着它给的标准的规范,把我们对硬件的操作写到框架中,这样在使用标准的应用程序操作这个设备的时候,,,就通过一层一层,内核中框架的调用对硬件进行了操作。
对于摄像头驱动来说,
我就写一写驱动的注册过程和应用程序对应的驱动程序中的操作吧
驱动的注册:
在我现在看的linux 3.0.12内核源码中,注册的关键代码主要在drivers/media/video/samsung/fimc/fimc_dev.c 文件的fimc_probe(struct platform_device *pdev)函数中
在这个函数中,我看关键部分代码主要是以下几个函数
1 . ctrl = fimc_register_controller(pdev);
2. ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
3.ret = fimc_init_global(pdev);
4.ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
接下来,一一分析这几个函数,我想,注册过程就大体知道了。
1.ctrl = fimc_register_controller(pdev);
ctrl是fimc_probe刚开始定义的一个结构体指针struct fimc_control *ctrl;
形参pdev是什么呢?是fimc的平台设备struct platform_device *pdev,这个在哪定义?
struct platform_device s3c_device_fimc0 = {
.name = "s3c-fimc",
.id = 0,
.num_resources
= ARRAY_SIZE(s3c_fimc0_resource),
.resource
= s3c_fimc0_resource,
};
还有一个很重要的函数就是在板级配置文件smdk4x12_machine_init函数中,s3c_fimc0_set_platdata(&fimc_plat);这句话的作用为
s3c_device_fimc0.dev.platform_data=fimc_plat;
fimc_plat为什么样的结构体呢?其定义为
static struct s3c_platform_fimc fimc_plat = {
.default_cam = CAMERA_PAR_A,
.camera = &ov5640,
.hw_ver = 0x51,
} 它的作用主要是描述了fimc结构的设置,比如读取哪种格式的数据,读取时采用哪种形式的时序,fimc读取时的时钟频率等,
其中ov5640这个结构展开为
static struct s3c_platform_camera ov5640 = {
.id = CAMERA_PAR_A,
.clk_name
= "sclk_cam0",
.i2c_busnum = 7,
.cam_power
= smdk4x12_cam1_reset,
.type = CAM_TYPE_ITU,
.fmt = ITU_601_YCBCR422_8BIT,
.order422
= CAM_ORDER422_8BIT_CBYCRY,
.info = &ov5640_i2c_info,
.pixelformat
= V4L2_PIX_FMT_UYVY, //modify by cym V4L2_PIX_FMT_UYVY,
.srclk_name = "xusbxti",
.clk_rate
= 24000000,
.line_length
= 1920,
.width
= 640,
.height = 480,
.window = {
.left = 0,
.top = 0,
.width
= 640,
.height = 480,
},
/* Polarity */
.inv_pclk
= 0,
.inv_vsync
= 1,
.inv_href
= 0,
.inv_hsync
= 0,
.reset_camera
= 1,
.initialized
= 0,
.layout_rotate = 0 //for shuping, //180,
};
上面是为我们分析函数进行了准备,把会用到的相关的结构体都进行了罗列,下面开始分析 ctrl = fimc_register_controller(pdev);
1,填充了struct fimc_control *ctrl;这个结构,这个结构是用来描述fimc控制器的,
2,映射了FIMC的的寄存器
3.申请设置了FImc控制器的时钟
*****************************************
接下来分析第二个函数 ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
这里的形参&pdev->dev,&ctrl,还是我们开始分析时提到的那些结构,
展开函数
1.初始化了一个链表
2.稍微对&v4l2_dev进行了设置
3.v4l2_dev->dev = dev;把形参平台设备结构中的dev关联到v4l2_dev->dev,以后有用
4.if (!dev_get_drvdata(dev)) dev_set_drvdata(dev, v4l2_dev); 使得 平台设备结构中的dev和这个v4l2_dev,实现相互关联
接下来看第3个关键函数,ret = fimc_init_global(pdev);
这个函数主要做的是:
根据平台设备设置的信息,初始化fimc_dev这个结构体,这是一个全局变量,在fimc_dev.c中有定义,struct fimc_global *fimc_dev;
接下来,看最后一个关键函数:4.ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
形参ctrl->vd为上面介绍的第一个函数中赋值的,为ctrl->vd = &fimc_video_device[id];ctrl->id代码是FIMC几,0,1,2或者3
展开看看
这个函数做的主要是
1.确定要注册的设备类型,与传入的那个形参有关,一般为video类型
2.查找相同类型设备的设备节点号,比如这次注册了VIDEO0,那下次执行这个函数时,要检查出来,0这个号已经被注册,注册的就是VIDEO1
3.注册设备,注册的是一个字符设备,所以某种意思上说,video设备也属于一个字符设备
cmos摄像头驱动设计主要分为几部分:1.ARM处理器摄像头接口部分,2.通过i2c接口控制的摄像头芯片部分,被称为senor部分,3.就是上述两部分直接的联系部分,写在板级文件mach-boardname.c文件中。
其实这个摄像头驱动不是太难,摄像头驱动设计的目标就是,ARM处理器控制摄像头芯片,使其能够采集图像然后处理器把图像读出来。
围绕着这个目的,linux系统提供了v4l2架构,架构的目的是让我们程序员开发的时候更加方便,有个标准的规范,我们程序员在开发驱动的时候,就按着它给的标准的规范,把我们对硬件的操作写到框架中,这样在使用标准的应用程序操作这个设备的时候,,,就通过一层一层,内核中框架的调用对硬件进行了操作。
对于摄像头驱动来说,
我就写一写驱动的注册过程和应用程序对应的驱动程序中的操作吧
驱动的注册:
在我现在看的linux 3.0.12内核源码中,注册的关键代码主要在drivers/media/video/samsung/fimc/fimc_dev.c 文件的fimc_probe(struct platform_device *pdev)函数中
在这个函数中,我看关键部分代码主要是以下几个函数
1 . ctrl = fimc_register_controller(pdev);
2. ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
3.ret = fimc_init_global(pdev);
4.ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
接下来,一一分析这几个函数,我想,注册过程就大体知道了。
1.ctrl = fimc_register_controller(pdev);
ctrl是fimc_probe刚开始定义的一个结构体指针struct fimc_control *ctrl;
形参pdev是什么呢?是fimc的平台设备struct platform_device *pdev,这个在哪定义?
struct platform_device s3c_device_fimc0 = {
.name = "s3c-fimc",
.id = 0,
.num_resources
= ARRAY_SIZE(s3c_fimc0_resource),
.resource
= s3c_fimc0_resource,
};
还有一个很重要的函数就是在板级配置文件smdk4x12_machine_init函数中,s3c_fimc0_set_platdata(&fimc_plat);这句话的作用为
s3c_device_fimc0.dev.platform_data=fimc_plat;
fimc_plat为什么样的结构体呢?其定义为
static struct s3c_platform_fimc fimc_plat = {
.default_cam = CAMERA_PAR_A,
.camera = &ov5640,
.hw_ver = 0x51,
} 它的作用主要是描述了fimc结构的设置,比如读取哪种格式的数据,读取时采用哪种形式的时序,fimc读取时的时钟频率等,
其中ov5640这个结构展开为
static struct s3c_platform_camera ov5640 = {
.id = CAMERA_PAR_A,
.clk_name
= "sclk_cam0",
.i2c_busnum = 7,
.cam_power
= smdk4x12_cam1_reset,
.type = CAM_TYPE_ITU,
.fmt = ITU_601_YCBCR422_8BIT,
.order422
= CAM_ORDER422_8BIT_CBYCRY,
.info = &ov5640_i2c_info,
.pixelformat
= V4L2_PIX_FMT_UYVY, //modify by cym V4L2_PIX_FMT_UYVY,
.srclk_name = "xusbxti",
.clk_rate
= 24000000,
.line_length
= 1920,
.width
= 640,
.height = 480,
.window = {
.left = 0,
.top = 0,
.width
= 640,
.height = 480,
},
/* Polarity */
.inv_pclk
= 0,
.inv_vsync
= 1,
.inv_href
= 0,
.inv_hsync
= 0,
.reset_camera
= 1,
.initialized
= 0,
.layout_rotate = 0 //for shuping, //180,
};
上面是为我们分析函数进行了准备,把会用到的相关的结构体都进行了罗列,下面开始分析 ctrl = fimc_register_controller(pdev);
static struct fimc_control *fimc_register_controller(struct platform_device *pdev) { struct s3c_platform_fimc *pdata; struct fimc_control *ctrl; struct resource *res; int id, err; struct cma_info mem_info; struct clk *sclk_fimc_lclk = NULL; struct clk *fimc_src_clk = NULL; id = pdev->id; pdata = to_fimc_plat(&pdev->dev); ctrl = get_fimc_ctrl(id); ctrl->id = id; ctrl->dev = &pdev->dev; ctrl->vd = &fimc_video_device[id]; ctrl->vd->minor = id; ctrl->log = FIMC_LOG_DEFAULT; ctrl->power_status = FIMC_POWER_OFF; /* CMA */ sprintf(ctrl->cma_name, "%s%d", FIMC_CMA_NAME, ctrl->id); err = cma_info(&mem_info, ctrl->dev, 0); fimc_info1("%s : [cma_info] start_addr : 0x%x, end_addr : 0x%x, " "total_size : 0x%x, free_size : 0x%x\n", __func__, mem_info.lower_bound, mem_info.upper_bound, mem_info.total_size, mem_info.free_size); if (err) { fimc_err("%s: get cma info failed\n", __func__); ctrl->mem.size = 0; ctrl->mem.base = 0; } else { ctrl->mem.size = mem_info.total_size; ctrl->mem.base = (dma_addr_t)cma_alloc (ctrl->dev, ctrl->cma_name, (size_t)ctrl->mem.size, 0); } printk(KERN_DEBUG "ctrl->mem.size = 0x%x\n", ctrl->mem.size); printk(KERN_DEBUG "ctrl->mem.base = 0x%x\n", ctrl->mem.base); ctrl->mem.curr = ctrl->mem.base; ctrl->status = FIMC_STREAMOFF; switch (pdata->hw_ver) { case 0x40: ctrl->limit = &fimc40_limits[id]; break; case 0x43: case 0x45: ctrl->limit = &fimc43_limits[id]; break; case 0x50: ctrl->limit = &fimc50_limits[id]; break; case 0x51: ctrl->limit = &fimc51_limits[id]; break; default: ctrl->limit = &fimc51_limits[id]; fimc_err("%s: failed to get HW version\n", __func__); break; } sprintf(ctrl->name, "%s%d", FIMC_NAME, id); strcpy(ctrl->vd->name, ctrl->name); atomic_set(&ctrl->in_use, 0); mutex_init(&ctrl->lock); mutex_init(&ctrl->v4l2_lock); spin_lock_init(&ctrl->outq_lock); init_waitqueue_head(&ctrl->wq); /* get resource for io memory */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { fimc_err("%s: failed to get io memory region\n", __func__); return NULL; } /* request mem region */ res = request_mem_region(res->start, res->end - res->start + 1, pdev->name); if (!res) { fimc_err("%s: failed to request io memory region\n", __func__); return NULL; } /* ioremap for register block */ ctrl->regs = ioremap(res->start, res->end - res->start + 1); if (!ctrl->regs) { fimc_err("%s: failed to remap io region\n", __func__); return NULL; } /* irq */ ctrl->irq = platform_get_irq(pdev, 0); if (request_irq(ctrl->irq, fimc_irq, IRQF_DISABLED, ctrl->name, ctrl)) fimc_err("%s: request_irq failed\n", __func__); if (soc_is_exynos4210()) fimc_src_clk = clk_get(&pdev->dev, "mout_mpll"); else fimc_src_clk = clk_get(&pdev->dev, "mout_mpll_user"); if (IS_ERR(fimc_src_clk)) { dev_err(&pdev->dev, "failed to get parent clock\n"); iounmap(ctrl->regs); return NULL; } sclk_fimc_lclk = clk_get(&pdev->dev, FIMC_CORE_CLK); if (IS_ERR(sclk_fimc_lclk)) { dev_err(&pdev->dev, "failed to get sclk_fimc_lclk\n"); iounmap(ctrl->regs); clk_put(fimc_src_clk); return NULL; } if (clk_set_parent(sclk_fimc_lclk, fimc_src_clk)) { dev_err(&pdev->dev, "unable to set parent %s of clock %s.\n", fimc_src_clk->name, sclk_fimc_lclk->name); iounmap(ctrl->regs); clk_put(sclk_fimc_lclk); clk_put(fimc_src_clk); return NULL; } clk_set_rate(sclk_fimc_lclk, FIMC_CLK_RATE); clk_put(sclk_fimc_lclk); clk_put(fimc_src_clk); #if (!defined(CONFIG_EXYNOS_DEV_PD) || !defined(CONFIG_PM_RUNTIME)) fimc_hwset_reset(ctrl); #endif return ctrl; }展开发现,上面代码主要做的了:
1,填充了struct fimc_control *ctrl;这个结构,这个结构是用来描述fimc控制器的,
2,映射了FIMC的的寄存器
3.申请设置了FImc控制器的时钟
*****************************************
接下来分析第二个函数 ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
这里的形参&pdev->dev,&ctrl,还是我们开始分析时提到的那些结构,
展开函数
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) { if (v4l2_dev == NULL) return -EINVAL; INIT_LIST_HEAD(&v4l2_dev->subdevs); spin_lock_init(&v4l2_dev->lock); mutex_init(&v4l2_dev->ioctl_lock); v4l2_prio_init(&v4l2_dev->prio); kref_init(&v4l2_dev->ref); v4l2_dev->dev = dev; if (dev == NULL) { /* 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", dev->driver->name, dev_name(dev)); if (!dev_get_drvdata(dev)) dev_set_drvdata(dev, v4l2_dev); return 0; }这个函数主要干了几件事:
1.初始化了一个链表
2.稍微对&v4l2_dev进行了设置
3.v4l2_dev->dev = dev;把形参平台设备结构中的dev关联到v4l2_dev->dev,以后有用
4.if (!dev_get_drvdata(dev)) dev_set_drvdata(dev, v4l2_dev); 使得 平台设备结构中的dev和这个v4l2_dev,实现相互关联
接下来看第3个关键函数,ret = fimc_init_global(pdev);
static int fimc_init_global(struct platform_device *pdev) { struct fimc_control *ctrl; struct s3c_platform_fimc *pdata; struct s3c_platform_camera *cam; struct clk *srclk; int id, i; pdata = to_fimc_plat(&pdev->dev); id = pdev->id; ctrl = get_fimc_ctrl(id); /* Registering external camera modules. re-arrange order to be sure */ for (i = 0; i < FIMC_MAXCAMS; i++) { cam = pdata->camera[i]; if (!cam) break; /* WriteBack doesn't need clock setting */ if ((cam->id == CAMERA_WB) || (cam->id == CAMERA_WB_B)) { fimc_dev->camera[i] = cam; fimc_dev->camera_isvalid[i] = 1; fimc_dev->camera[i]->initialized = 0; continue; } /* source clk for MCLK*/ srclk = clk_get(&pdev->dev, cam->srclk_name); if (IS_ERR(srclk)) { fimc_err("%s: failed to get srclk source\n", __func__); return -EINVAL; } /* mclk */ cam->clk = clk_get(&pdev->dev, cam->clk_name); if (IS_ERR(cam->clk)) { fimc_err("%s: failed to get mclk source\n", __func__); return -EINVAL; } if (clk_set_parent(cam->clk, srclk)) { dev_err(&pdev->dev, "unable to set parent %s of clock %s.\n", srclk->name, cam->clk->name); clk_put(srclk); clk_put(cam->clk); return -EINVAL; } /* Assign camera device to fimc */ fimc_dev->camera[i] = cam; fimc_dev->camera_isvalid[i] = 1; fimc_dev->camera[i]->initialized = 0; } fimc_dev->mclk_status = CAM_MCLK_OFF; fimc_dev->active_camera = -1; fimc_dev->initialized = 1; return 0; }
这个函数主要做的是:
根据平台设备设置的信息,初始化fimc_dev这个结构体,这是一个全局变量,在fimc_dev.c中有定义,struct fimc_global *fimc_dev;
/** * struct fimc_dev - abstraction for FIMC entity * @slock: the spinlock protecting this data structure * @lock: the mutex protecting this data structure * @pdev: pointer to the FIMC platform device * @pdata: pointer to the device platform data * @variant: the IP variant information * @id: FIMC device index (0..FIMC_MAX_DEVS) * @num_clocks: the number of clocks managed by this device instance * @clock: clocks required for FIMC operation * @regs: the mapped hardware registers * @regs_res: the resource claimed for IO registers * @irq: FIMC interrupt number * @irq_queue: interrupt handler waitqueue * @m2m: memory-to-memory V4L2 device information * @vid_cap: camera capture device information * @state: flags used to synchronize m2m and capture mode operation * @alloc_ctx: videobuf2 memory allocator context */ struct fimc_dev { spinlock_t slock; struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; struct samsung_fimc_variant *variant; u16 id; u16 num_clocks; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; struct resource *regs_res; int irq; wait_queue_head_t irq_queue; struct work_struct work_struct; struct workqueue_struct *irq_workqueue; struct fimc_m2m_device m2m; struct fimc_vid_cap vid_cap; unsigned long state; struct vb2_alloc_ctx *alloc_ctx; struct fimc_addr paddr[FIMC_MAX_OUT_BUFS]; #ifdef CONFIG_VIDEOBUF2_SDVMM enum vcm_dev_id vcm_id; #endif const struct fimc_vb2 *vb2; };
接下来,看最后一个关键函数:4.ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
形参ctrl->vd为上面介绍的第一个函数中赋值的,为ctrl->vd = &fimc_video_device[id];ctrl->id代码是FIMC几,0,1,2或者3
/* 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. */ 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 devices ,
展开看看
/** * __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; /* the release callback MUST be present */ WARN_ON(!vdev->release); if (!vdev->release) return -EINVAL; /* v4l2_fh support */ spin_lock_init(&vdev->fh_lock); INIT_LIST_HEAD(&vdev->fh_list); /* 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; 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; } /* 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; }
这个函数做的主要是
1.确定要注册的设备类型,与传入的那个形参有关,一般为video类型
2.查找相同类型设备的设备节点号,比如这次注册了VIDEO0,那下次执行这个函数时,要检查出来,0这个号已经被注册,注册的就是VIDEO1
3.注册设备,注册的是一个字符设备,所以某种意思上说,video设备也属于一个字符设备
相关文章推荐
- linux下的cmos摄像头驱动设计2-应用程序的调用与驱动程序的关系
- Linux那些事儿之我是Block层(2)注册一个块设备驱动
- Linux那些事儿之我是Block层(2)注册一个块设备驱动
- linux IDE驱动分析之IDE总线、驱动注册(五)
- linux 内核驱动--Platform Device和Platform_driver注册过程
- 课程设计──LINUX字符驱动
- Linux 2.6 驱动设计快速入门!
- Linux关于总线、设备、驱动的注册顺序
- linux IDE驱动分析之IDE总线、驱动注册(三)
- linux IDE驱动分析之IDE总线、驱动注册(二)
- video 4 linux 2驱动的一种实现
- 嵌入式Linux系统下MPEG-4专用编码器驱动设计
- Linux-2.6驱动开发 1 注册
- Linux 2.6 驱动设计快速入门
- 基于ARM+Linux 2.6内核的控制系统驱动设计
- linux IDE驱动分析之IDE总线、驱动注册(四)
- Linux2.6 驱动设计―从 2.4.x 到 2.6.x
- I2C总线在Linux系统中的驱动设计
- linux IDE驱动分析之Ide_driver的注册(三)
- Linux设备驱动的分层设计思想[转]