Davinci VFPE的框架分析

#Davinci# (开源)可视应用平台介绍与展望 作者:徐翔 导读:通过图形化手段清晰地传达数据,促进信息的传递与沟通,是数据可视化的基础要素,也是设计美学和功能相结合的具体表现形式。Davinci便是这样一款可视应用平台。在敏捷大数据(Agile BigData)理论的背景下,围绕“数据视图”和“可视组件”两个核心概念设计,支持多种可视化功能。Davinci具体的设计理念和功能特点都有什么呢?它又将怎么成长呢?让我们一起来阅读本文吧~ 一... 阅读详情
 dm6446是基于Davinci架构设计的多媒体处理器。在这里我们分析的Linux源码是montavista的2.6.10的版本,该源码中使用的视频驱动架构为V4L2的框架。对这个框架而言,网上已经存在大量的分析,但涉及的内容主要都是框架层的封装以及相关应用层的解析,底层核心的内容很少会涉及。在这里,我将结合DM6446的视频处理前端VPFE中的CCDC模块,完成视频采集的驱动核心内容的解析,核心源码位于kernel/driver/media/video下面。

        依旧驱动模块的注册过程,视频采集的驱动模块主要分为:videodev_init(位于videodev_init.c),vpfe_init(位于davinci_vpfe.c),tvp5146_i2c_init(该模块在这里不做解析,是实现一款解码的芯片)。

videodev_init(位于videodev_init.c)模块解析

       该驱动模块下,主要是完成V4L2核心的驱动的注册。源码如下:

  1. static int __init videodev_init(void)  
  2. {  
  3.     int ret;  
  4.   
  5.     printk(KERN_INFO "Linux video capture interface: v1.00\n");  
  6.     if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) {  //注册一个视频驱动,主设备号为81  
  7.         printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR);  
  8.         return -EIO;  
  9.     }  
  10.   
  11.     ret = class_register(&video_class);  
  12.     if (ret < 0) {  
  13.         unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME);  
  14.         printk(KERN_WARNING "video_dev: class_register failed\n");  
  15.         return -EIO;  
  16.     }  
  17.   
  18.     return 0;  
  19. }  
static int __init videodev_init(void)
{
	int ret;

	printk(KERN_INFO "Linux video capture interface: v1.00\n");
	if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) {  //注册一个视频驱动,主设备号为81
		printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR);
		return -EIO;
	}

	ret = class_register(&video_class);
	if (ret < 0) {
		unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME);
		printk(KERN_WARNING "video_dev: class_register failed\n");
		return -EIO;
	}

	return 0;
}

从代码的分析中我们看到,V4L2的架构中,先是注册一个主设备号为81的字符型设备,文件操作指针定义为video_fops。的确在上述的模块中完成的内容不多,这只是V4L2驱动的基本框架,可以再多平台之间移植。因此在这个框架下针对自己的处理器平台需要完成video_device的抽象设备和驱动的注册。也就是说V4L2下面可以注册进入很多的视频抽象设备。

           vpfe_init(位于davinci_vpfe.c)驱动模块中主要完成的内容就是完成video_device的注册,当然因为结合了专用的处理器平台在完成设备和驱动和注册的同时还需要完成相关硬件设备需求的初始化。

  1. static int vpfe_init(void)  
  2. {  
  3.     int i = 0;  
  4.     int fbuf_size;  
  5.     ccdc_frmfmt frame_format;  
  6.     void *mem;  
  7.     int ret = 0;  
  8.   
  9.     if (device_type == TVP5146) {  
  10.         fbuf_size = VPFE_TVP5146_MAX_FBUF_SIZE;   //768*576*2  
  11.         vpfe_device = vpfe_device_ycbcr;  
  12.         frame_format = vpfe_device.ccdc_params_ycbcr.frm_fmt;  
  13.     }  
  14.   
  15.     else if (device_type == MT9T001) {  
  16.         fbuf_size = VPFE_MT9T001_MAX_FBUF_SIZE;  
  17.         vpfe_device = vpfe_device_raw;  
  18.         frame_format = vpfe_device.ccdc_params_raw.frm_fmt;  
  19.     } else {  
  20.         return -1;  
  21.     }  
  22.     /* allocate memory at initialization time to guarentee availability *///对默认的3路缓冲区进行内存的申请  
  23.     for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {  //VPFE_DEFNUM_FBUFS=3  
  24.         mem =  
  25.             (void *)__get_free_pages(GFP_KERNEL | GFP_DMA,  
  26.                          get_order(fbuf_size));  //需要的一帧内存大小为768*576*2  
  27.         if (mem) {  
  28.             unsigned long adr = (unsigned long)mem;  //获取32位的内存地址  
  29.             u32 size = PAGE_SIZE << (get_order(fbuf_size));//get_order(fbuf_size)=8,PAGE_SIZE=4K,  
  30.             while (size > 0) {  
  31.                 /* make sure the frame buffers   
  32.                    are never swapped out of memory */  
  33.                 SetPageReserved(virt_to_page(adr));//对256页每页内存申请被保留,不被他人使用  
  34.                 adr += PAGE_SIZE;  
  35.                 size -= PAGE_SIZE;  
  36.             }//获取每一个物理缓存区的首地址,地址存入fbuffers数组  
  37.             vpfe_device.fbuffers[i] = (u8 *) mem;//3            
  38.         } else {  
  39.             while (--i >= 0) {  
  40.                 free_reserved_pages((unsigned long)  
  41.                             vpfe_device.  
  42.                             fbuffers[i], fbuf_size);   
  43.             }  
  44.             printk(KERN_INFO  
  45.                    "frame buffer memory allocation failed.\n");  
  46.             return -ENOMEM;  
  47.         }  
  48.     }  
  49.     if (driver_register(&vpfe_driver) != 0) {  
  50.         printk(KERN_INFO "driver registration failed\n");  
  51.         return -1;  
  52.     }  
  53.     if (platform_device_register(&_vpfe_device) != 0) {  
  54.         driver_unregister(&vpfe_driver);  
  55.         printk(KERN_INFO "device registration failed\n");  
  56.         return -1;  
  57.     }  
  58.   
  59.     ccdc_reset();  //CCDC复位不使能  
  60.   
  61.     if (device_type == TVP5146) {  
  62.         ret = tvp5146_ctrl(TVP5146_INIT, NULL);    //decoder I2C驱动的初始化  
  63.         if (ret >= 0) {  
  64.             ret = tvp5146_ctrl(TVP5146_RESET, NULL);  
  65.             /* configure the tvp5146 to default parameters */  
  66.             ret |=  
  67.                 tvp5146_ctrl(TVP5146_CONFIG,  
  68.                      &vpfe_device.tvp5146_params);  //vpfe_device=vpfe_device_ycbcr  
  69.         }  
  70.         if (ret < 0) {  
  71.             tvp5146_ctrl(TVP5146_CLEANUP, NULL);  
  72.         }  
  73.     } else if (device_type == MT9T001) {  
  74.         /* enable video port in case of raw capture */  
  75.         ccdc_enable_vport();  
  76.         vpfe_device.config_dev_fxn = mt9t001_ctrl;  
  77.         ret =  
  78.             vpfe_device.config_dev_fxn(MT9T001_INIT,  
  79.                            &vpfe_device.std,  
  80.                            &vpfe_device.device_params);  
  81.     }  
  82.   
  83.     if (ret < 0) {  
  84.         platform_device_unregister(&_vpfe_device);  
  85.         driver_unregister(&vpfe_driver);  
  86.         /* Free memory for all image buffers */  
  87.         for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {  
  88.             free_reserved_pages((unsigned long)  
  89.                         vpfe_device.fbuffers[i], fbuf_size);  
  90.         }  
  91.         return -1;  
  92.     }  
  93.   
  94.     /* setup interrupt handling */  
  95.     /* request VDINT1 if progressive format */   
  96.     if (frame_format == CCDC_FRMFMT_PROGRESSIVE) {    //frame_format=CCDC_FRMFMT_INTERLACED=1  
  97.         ret = request_irq(IRQ_VDINT1, vdint1_isr, SA_INTERRUPT,  
  98.                   "dm644xv4l2", (void *)&vpfe_device); //逐行格式,申请VDINT1中断  
  99.         if (ret < 0) {  
  100.             platform_device_unregister(&_vpfe_device);  
  101.             driver_unregister(&vpfe_driver);  
  102.             /* Free memory for all image buffers */  
  103.             for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {  
  104.                 free_reserved_pages((unsigned long)  
  105.                             vpfe_device.  
  106.                             fbuffers[i], fbuf_size);  
  107.             }  
  108.             return -1;  
  109.         }  
  110.     }  
  111.     ret = request_irq(IRQ_VDINT0, vpfe_isr, SA_INTERRUPT,  
  112.               "dm644xv4l2", (void *)&vpfe_device);//申请VDINT0中断  
  113.     if (ret < 0) {  
  114.         platform_device_unregister(&_vpfe_device);  
  115.         driver_unregister(&vpfe_driver);  
  116.         /* Free memory for all image buffers */  
  117.         for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {  
  118.             free_reserved_pages((unsigned long)  
  119.                         vpfe_device.fbuffers[i], fbuf_size);  
  120.         }  
  121.         free_irq(IRQ_VDINT1, &vpfe_device);  
  122.         return -1;  
  123.     }  
  124.   
  125.     printk(KERN_INFO "DaVinci v4l2 capture driver V1.0 loaded\n");  
  126.     return 0;  
  127. }  
  128.   
  129. /  
static int vpfe_init(void)
{
	int i = 0;
	int fbuf_size;
	ccdc_frmfmt frame_format;
	void *mem;
	int ret = 0;

	if (device_type == TVP5146) {
		fbuf_size = VPFE_TVP5146_MAX_FBUF_SIZE;   //768*576*2
		vpfe_device = vpfe_device_ycbcr;
		frame_format = vpfe_device.ccdc_params_ycbcr.frm_fmt;
	}

	else if (device_type == MT9T001) {
		fbuf_size = VPFE_MT9T001_MAX_FBUF_SIZE;
		vpfe_device = vpfe_device_raw;
		frame_format = vpfe_device.ccdc_params_raw.frm_fmt;
	} else {
		return -1;
	}
	/* allocate memory at initialization time to guarentee availability *///对默认的3路缓冲区进行内存的申请
	for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {  //VPFE_DEFNUM_FBUFS=3
		mem =
		    (void *)__get_free_pages(GFP_KERNEL | GFP_DMA,
					     get_order(fbuf_size));  //需要的一帧内存大小为768*576*2
		if (mem) {
			unsigned long adr = (unsigned long)mem;  //获取32位的内存地址
			u32 size = PAGE_SIZE << (get_order(fbuf_size));//get_order(fbuf_size)=8,PAGE_SIZE=4K,
			while (size > 0) {
				/* make sure the frame buffers 
				   are never swapped out of memory */
				SetPageReserved(virt_to_page(adr));//对256页每页内存申请被保留,不被他人使用
				adr += PAGE_SIZE;
				size -= PAGE_SIZE;
			}//获取每一个物理缓存区的首地址,地址存入fbuffers数组
			vpfe_device.fbuffers[i] = (u8 *) mem;//3          
		} else {
			while (--i >= 0) {
				free_reserved_pages((unsigned long)
						    vpfe_device.
						    fbuffers[i], fbuf_size); 
			}
			printk(KERN_INFO
			       "frame buffer memory allocation failed.\n");
			return -ENOMEM;
		}
	}
	if (driver_register(&vpfe_driver) != 0) {
		printk(KERN_INFO "driver registration failed\n");
		return -1;
	}
	if (platform_device_register(&_vpfe_device) != 0) {
		driver_unregister(&vpfe_driver);
		printk(KERN_INFO "device registration failed\n");
		return -1;
	}

	ccdc_reset();  //CCDC复位不使能

	if (device_type == TVP5146) {
		ret = tvp5146_ctrl(TVP5146_INIT, NULL);    //decoder I2C驱动的初始化
		if (ret >= 0) {
			ret = tvp5146_ctrl(TVP5146_RESET, NULL);
			/* configure the tvp5146 to default parameters */
			ret |=
			    tvp5146_ctrl(TVP5146_CONFIG,
					 &vpfe_device.tvp5146_params);  //vpfe_device=vpfe_device_ycbcr
		}
		if (ret < 0) {
			tvp5146_ctrl(TVP5146_CLEANUP, NULL);
		}
	} else if (device_type == MT9T001) {
		/* enable video port in case of raw capture */
		ccdc_enable_vport();
		vpfe_device.config_dev_fxn = mt9t001_ctrl;
		ret =
		    vpfe_device.config_dev_fxn(MT9T001_INIT,
					       &vpfe_device.std,
					       &vpfe_device.device_params);
	}

	if (ret < 0) {
		platform_device_unregister(&_vpfe_device);
		driver_unregister(&vpfe_driver);
		/* Free memory for all image buffers */
		for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {
			free_reserved_pages((unsigned long)
					    vpfe_device.fbuffers[i], fbuf_size);
		}
		return -1;
	}

	/* setup interrupt handling */
	/* request VDINT1 if progressive format */ 
	if (frame_format == CCDC_FRMFMT_PROGRESSIVE) {    //frame_format=CCDC_FRMFMT_INTERLACED=1
		ret = request_irq(IRQ_VDINT1, vdint1_isr, SA_INTERRUPT,
				  "dm644xv4l2", (void *)&vpfe_device); //逐行格式,申请VDINT1中断
		if (ret < 0) {
			platform_device_unregister(&_vpfe_device);
			driver_unregister(&vpfe_driver);
			/* Free memory for all image buffers */
			for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {
				free_reserved_pages((unsigned long)
						    vpfe_device.
						    fbuffers[i], fbuf_size);
			}
			return -1;
		}
	}
	ret = request_irq(IRQ_VDINT0, vpfe_isr, SA_INTERRUPT,
			  "dm644xv4l2", (void *)&vpfe_device);//申请VDINT0中断
	if (ret < 0) {
		platform_device_unregister(&_vpfe_device);
		driver_unregister(&vpfe_driver);
		/* Free memory for all image buffers */
		for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {
			free_reserved_pages((unsigned long)
					    vpfe_device.fbuffers[i], fbuf_size);
		}
		free_irq(IRQ_VDINT1, &vpfe_device);
		return -1;
	}

	printk(KERN_INFO "DaVinci v4l2 capture driver V1.0 loaded\n");
	return 0;
}

/

在这个初始化的内容中,我们看到做了很多的内容,这里的vpfe_device = vpfe_device_ycbcr;就是上面提到的video_device的实例,也就是DM6446的视频前端VPFE这个视频设备。同时可以发现,在这里进行了连续的内存页式缓存区的申请(主要用于后续的视频采集内存缓存区队列设置的相关内容),完成vpfe_device的初始化。随后通过基于平台设备进行了驱动和设备的注册。其实这个设备和驱动的注册,完全就是为了调用匹配以后的probe函数,进一步完成相关视频设备文件(主设备号为81,次设备号不一)节点的创建(相当于手动在终端完成mknod操作),只有这样才可以在应用层打开设备之后,才能经过系统调来调用主设备号81的文件操作指针video_fops。同时也完成了VPFE前端CCDC的中断申请。下面来看porbe的指针函数。

  1. static int __init vpfe_probe(struct device *device)   //传入参数_vpfe_device->dev  
  2. {  
  3.     struct video_device *vfd;  
  4.     vpfe_obj *vpfe = &vpfe_device;       //vpfe_device=vpfe_device_ycbcr  
  5.     vpfe_dev = device;            
  6.     dev_dbg(vpfe_dev, "\nStarting of vpfe_probe...");  
  7.     /* alloc video device */  
  8.     if ((vfd = video_device_alloc()) == NULL) {  
  9.         return -ENOMEM;  
  10.     }  
  11.     *vfd = vpfe_video_template;  //video_device的类型  
  12.     vfd->dev = device;  
  13.     vfd->release = video_device_release;  
  14.     snprintf(vfd->name, sizeof(vfd->name),  
  15.          "DM644X_VPFE_DRIVER_V%d.%d.%d",  
  16.          (VPFE_VERSION_CODE >> 16) & 0xff,  
  17.          (VPFE_VERSION_CODE >> 8) & 0xff, (VPFE_VERSION_CODE) & 0xff);  
  18.   
  19.     vpfe->video_dev = vfd; //vpfe_obj的实例vpfe_device获取video_device实例  
  20.     vpfe->usrs = 0;  
  21.     vpfe->io_usrs = 0;  
  22.     vpfe->started = FALSE;  
  23.     vpfe->latest_only = TRUE;  
  24.   
  25.     v4l2_prio_init(&vpfe->prio);  
  26.     init_MUTEX(&vpfe->lock);  
  27.   
  28.     /* register video device */  
  29.     dev_dbg(vpfe_dev, "trying to register vpfe device.\n");  
  30.     dev_dbg(vpfe_dev, "vpfe=%x,vpfe->video_dev=%x\n", (int)vpfe,  
  31.         (int)&vpfe->video_dev);  
  32.     if (video_register_device(vpfe->video_dev, VFL_TYPE_GRABBER, -1) < 0) {  //完成视频设备vfd的注册  
  33.         video_device_release(vpfe->video_dev);  
  34.         vpfe->video_dev = NULL;  
  35.         return -1;  
  36.     }  
  37.   
  38.     dev_dbg(vpfe_dev, "DM644X vpfe: driver version V%d.%d.%d loaded\n",  
  39.         (VPFE_VERSION_CODE >> 16) & 0xff,  
  40.         (VPFE_VERSION_CODE >> 8) & 0xff, (VPFE_VERSION_CODE) & 0xff);  
  41.   
  42.     dev_dbg(vpfe_dev, "vpfe: registered device video%d\n",  
  43.         vpfe->video_dev->minor & 0x1f);  
  44.   
  45.     /* all done */  
  46.     return 0;  
  47. }  
static int __init vpfe_probe(struct device *device)   //传入参数_vpfe_device->dev
{
	struct video_device *vfd;
	vpfe_obj *vpfe = &vpfe_device;       //vpfe_device=vpfe_device_ycbcr
	vpfe_dev = device;          
	dev_dbg(vpfe_dev, "\nStarting of vpfe_probe...");
	/* alloc video device */
	if ((vfd = video_device_alloc()) == NULL) {
		return -ENOMEM;
	}
	*vfd = vpfe_video_template;  //video_device的类型
	vfd->dev = device;
	vfd->release = video_device_release;
	snprintf(vfd->name, sizeof(vfd->name),
		 "DM644X_VPFE_DRIVER_V%d.%d.%d",
		 (VPFE_VERSION_CODE >> 16) & 0xff,
		 (VPFE_VERSION_CODE >> 8) & 0xff, (VPFE_VERSION_CODE) & 0xff);

	vpfe->video_dev = vfd; //vpfe_obj的实例vpfe_device获取video_device实例
	vpfe->usrs = 0;
	vpfe->io_usrs = 0;
	vpfe->started = FALSE;
	vpfe->latest_only = TRUE;

	v4l2_prio_init(&vpfe->prio);
	init_MUTEX(&vpfe->lock);

	/* register video device */
	dev_dbg(vpfe_dev, "trying to register vpfe device.\n");
	dev_dbg(vpfe_dev, "vpfe=%x,vpfe->video_dev=%x\n", (int)vpfe,
		(int)&vpfe->video_dev);
	if (video_register_device(vpfe->video_dev, VFL_TYPE_GRABBER, -1) < 0) {  //完成视频设备vfd的注册
		video_device_release(vpfe->video_dev);
		vpfe->video_dev = NULL;
		return -1;
	}

	dev_dbg(vpfe_dev, "DM644X vpfe: driver version V%d.%d.%d loaded\n",
		(VPFE_VERSION_CODE >> 16) & 0xff,
		(VPFE_VERSION_CODE >> 8) & 0xff, (VPFE_VERSION_CODE) & 0xff);

	dev_dbg(vpfe_dev, "vpfe: registered device video%d\n",
		vpfe->video_dev->minor & 0x1f);

	/* all done */
	return 0;
}

在这里我们可以看到调用video_register_device完成


  1. int video_register_device(struct video_device *vfd, int type, int nr)  
  2. {  
  3.     int i=0;  
  4.     int base;  
  5.     int end;  
  6.     char *name_base;  
  7.       
  8.     switch(type)  
  9.     {  
  10.         case VFL_TYPE_GRABBER:  //明是一个图像采集设备?包括摄像头、调谐器  
  11.             base=0;  
  12.             end=64;  
  13.             name_base = "video";   
  14.             break;  
  15.         case VFL_TYPE_VTX:        //代表视传设备  
  16.             base=192;  
  17.             end=224;  
  18.             name_base = "vtx";  
  19.             break;  
  20.         case VFL_TYPE_VBI:        //代表的设备是从视频消隐的时间段取得信息的设备。  
  21.             base=224;  
  22.             end=240;  
  23.             name_base = "vbi";     
  24.             break;  
  25.         case VFL_TYPE_RADIO:   //代表无线电设备  
  26.             base=64;  
  27.             end=128;  
  28.             name_base = "radio";  
  29.             break;  
  30.         default:  
  31.             return -1;  
  32.     }  
  33.   
  34.     /* pick a minor number */  
  35.     down(&videodev_lock);  
  36.     if (nr >= 0  &&  nr < end-base) {  
  37.         /* use the one the driver asked for */  
  38.         i = base+nr;  
  39.         if (NULL != video_device[i]) {  
  40.             up(&videodev_lock);  
  41.             return -ENFILE;  
  42.         }  
  43.     } else {  
  44.         /* use first free */                //自动分配设备号,如果有当前全局的256个vide0_device为空  
  45.         for(i=base;i<end;i++)  
  46.             if (NULL == video_device[i])  
  47.                 break;  
  48.         if (i == end) {  
  49.             up(&videodev_lock);  
  50.             return -ENFILE;  
  51.         }  
  52.     }  
  53.     video_device[i]=vfd;  
  54.     vfd->minor=i;           //次设备号  
  55.     up(&videodev_lock);  
  56.   
  57.     sprintf(vfd->devfs_name, "v4l/%s%d", name_base, i - base); //生成设备文件节点v4l/video0  
  58.     devfs_mk_cdev(MKDEV(VIDEO_MAJOR, vfd->minor),  
  59.             S_IFCHR | S_IRUSR | S_IWUSR, vfd->devfs_name);    //对当前设备在dev下自动创建设备文件节点  
  60.     init_MUTEX(&vfd->lock);  
  61.   
  62.     /* sysfs class */  
  63.         memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev));  
  64.     if (vfd->dev)  
  65.         vfd->class_dev.dev = vfd->dev;  
  66.     vfd->class_dev.class       = &video_class;  
  67.     strlcpy(vfd->class_dev.class_id, vfd->devfs_name + 4, BUS_ID_SIZE);  
  68.     class_device_register(&vfd->class_dev);  
  69.     class_device_create_file(&vfd->class_dev,  
  70.                  &class_device_attr_name);  
  71.     class_device_create_file(&vfd->class_dev,  
  72.                  &class_device_attr_dev);  
  73.   
  74. #if 1 /* needed until all drivers are fixed */  
  75.     if (!vfd->release)  
  76.         printk(KERN_WARNING "videodev: \"%s\" has no release callback. "  
  77.                "Please fix your driver for proper sysfs support, see "  
  78.                "http://lwn.net/Articles/36850/\n", vfd->name);  
  79. #endif  
  80.     return 0;  
  81. }  
int video_register_device(struct video_device *vfd, int type, int nr)
{
	int i=0;
	int base;
	int end;
	char *name_base;
	
	switch(type)
	{
		case VFL_TYPE_GRABBER:  //明是一个图像采集设备?包括摄像头、调谐器
			base=0;
			end=64;
			name_base = "video"; 
			break;
		case VFL_TYPE_VTX:        //代表视传设备
			base=192;
			end=224;
			name_base = "vtx";
			break;
		case VFL_TYPE_VBI:        //代表的设备是从视频消隐的时间段取得信息的设备。
			base=224;
			end=240;
			name_base = "vbi";   
			break;
		case VFL_TYPE_RADIO:   //代表无线电设备
			base=64;
			end=128;
			name_base = "radio";
			break;
		default:
			return -1;
	}

	/* pick a minor number */
	down(&videodev_lock);
	if (nr >= 0  &&  nr < end-base) {
		/* use the one the driver asked for */
		i = base+nr;
		if (NULL != video_device[i]) {
			up(&videodev_lock);
			return -ENFILE;
		}
	} else {
		/* use first free */                //自动分配设备号,如果有当前全局的256个vide0_device为空
		for(i=base;i<end;i++)
			if (NULL == video_device[i])
				break;
		if (i == end) {
			up(&videodev_lock);
			return -ENFILE;
		}
	}
	video_device[i]=vfd;
	vfd->minor=i;           //次设备号
	up(&videodev_lock);

	sprintf(vfd->devfs_name, "v4l/%s%d", name_base, i - base); //生成设备文件节点v4l/video0
	devfs_mk_cdev(MKDEV(VIDEO_MAJOR, vfd->minor),
			S_IFCHR | S_IRUSR | S_IWUSR, vfd->devfs_name);    //对当前设备在dev下自动创建设备文件节点
	init_MUTEX(&vfd->lock);

	/* sysfs class */
        memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev));
	if (vfd->dev)
		vfd->class_dev.dev = vfd->dev;
	vfd->class_dev.class       = &video_class;
	strlcpy(vfd->class_dev.class_id, vfd->devfs_name + 4, BUS_ID_SIZE);
	class_device_register(&vfd->class_dev);
	class_device_create_file(&vfd->class_dev,
				 &class_device_attr_name);
	class_device_create_file(&vfd->class_dev,
				 &class_device_attr_dev);

#if 1 /* needed until all drivers are fixed */
	if (!vfd->release)
		printk(KERN_WARNING "videodev: \"%s\" has no release callback. "
		       "Please fix your driver for proper sysfs support, see "
		       "http://lwn.net/Articles/36850/\n", vfd->name);
#endif
	return 0;
}

我们可以看到在这个视频设备注册的函数中,首先会判断你的设备是采集,传输还是其他用途,用于确定devfs_mk_cdev中的设备文件节点的名字。最终按其自动创建设备文件节点的方法,最终创建出/v4l/video0,/v4l/video1之类的设备节点,此类设备就如同mknod的手动功能,只是设备的次设备号不同,主设备号都为81。这样就符合一个驱动可以带多个设备。只是在设备open时,会首先根据次设备号来选择对应的设备,然后获取该设备的fops,进行后续的操作ioctl时,转向专用的设备(即我们自己的设备不再是之前注册的V4L2最上层的抽象设备)。

       后一博文,我将完成V4L2的ioctl的解析,这块内容是英语词典核心调用所在。在这部分内容里会涉及到对tvp5416的设置,CCDC的控制以及视频缓存区内存的管理等。

dm6437 VPFE介绍 该文档详细介绍了DM6437 VP前端的接受流程以及寄存器设置,对开发有很大帮助 立即下载

相关推荐

VPBE和VPFE例程

TI的DM6437用的VPFE及VPBE的例程,配合用户手册使用。

DM6446的视频前端VPFE的驱动大框架解析

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。 欢迎和大家交流。qq:1037701636 email:200803090209@zjut.com,gzzaigcn2012@gmail.com       dm6446是基于Davinci架构设计的多媒体处理器。在这里我们分析的Linux源码是montavista的2.6.10的版本,该源码中使用的视频驱动架构为V4L2的框架。对这个框架

天才之嵌入式 6864

jsp视频网站前端框架

使用jsp实现动态页面框架,基于视频网站需求,值得参考!

DaVinci框架组成和不同应用的组件使用情况

DaVinci框架组成和不同应用的组件使用情况 Author: ZZ Time: 2015/5/18 10:22:27 DaVinci框架主要指DVSDK的开发框架 说明:该文档翻译整理自TMS320DM3730_Software_Developers_Guide 总体框架整个框架包含的主要组件和依赖关系如下图:(蓝色框为T

zszszszsd的专栏 1728

TI DM6437 VPFE,VPBE使用手册

TI DM6437 VPFE,VPBE使用手册

DM6437 VPFE和VPBE的中断机制

VPFE和VPBE两者的中断,都是来源于VSYNC这个垂直同步信号。而这里的中断,往往就形成一套完整的缓冲机制,包括采集和显示。

VFP也可以做前端,猫猫走过无数的弯路,才总结出这篇BS入门

调试服务器使用 打开框架项目文件 运行 环境设置.prg 运行调试服务器即可启动。 默认端口为801,IP为开发机器所在的IP 测试调试服务器 打开好调试服务器,就可以打开浏览器(推荐 使用360极速,谷歌浏览器) 输入http://192.168.0.99:801/1.fsp 根据开发机器修改IP 同台机器也可用127.0.0.1 出现如上的字样表示调试服务器工作良好。 网址URL和开发框架的一些约定 http://192.168.0.99:801/a1send.fsp?proc

加菲猫的VFP 1283

20 CVPR Learning 3D Semantic Scene Graphs From 3D Indoor Reconstructions 学习笔记

3RScan是大规模真实世界的室内场景,特点是对478个自然改变的室内环境在不同的时间帧进行了1482 RGB-D scans,每一个scene都包括了一些位置被改变的物体。它和ScanNet数据集的区别主要在于ScanNet中的每一个采样环境都不相同,但是3RScan中存在采样环境相同但物体的位置不同的场景。Scene Graph Annotations 则是在3RScan的基础上给物体加入了场景图需要的关系、物体特征和类别标注,同时对3RScan进行了扩展,以此形成了场景图数据。

weixin_43508459的博客 1834

VPFE

在EVMDM6437上使用VPFE至少有两种方法:<br /><br /> 1)用PSP (platform support package)的驱动, 需要DSP/BIOS,好处是 参数设置相对简单, 有现成的例子。 但是有些东东不支持,例如俺要用的 on-the-fly resizer,也就是 resizer 的输入是从 ccdc/preview 的输出,而不是从DDR 读入。 <br /><br /> 2)用BSL (board support library)的驱动, 不要BIOS,

svmtracking的专栏 5791

【BUAA】【计组】计算机组成 pre (含详细解析)

BUAA 计算机组成 本文章旨在为@走肖暂时无法接通的系列文章创造一个目录,收藏本文章,方便查阅! 1. 入门测试 2. 基础知识 3. Logisim   欢迎来到 Logisim 的世界   Logisim 门电路   Logisim 组合电路   Logisim 时序电路   Logisim 仿真与调试   应用与挑战 4. Verilog 与仿真工具   前言   ISE 入门   VCS 入门   Verilog 语法   Verilog 题目实例与分析   Verilog 工程的设计开发调试

Schrodinger007的博客 9807

Vue3 - 详解实现网站接入钉钉扫码登录功能,钉钉授权生成登录二维码用户用手机扫描二维码登陆,支持弹框式、浏览器单独新开页显示,钉钉扫描二维码登录详细流程及示例代码,最新vue集成钉钉扫码授权登录!

vue3,nuxt3,钉钉,扫码登录,vue3钉钉扫码登陆,钉钉授权第三方WEB网站扫码登录功能,钉钉扫码登录第三方网站,单点登录,二维码不显示,报错,扫了没反应,出错,vue3钉钉扫码登录,内嵌,获取个人信息,pc网页钉钉登录,钉钉扫码登录怎么新开一页,跳页显示二维码,内嵌登录二维码,iframe新页面弹出,弹出一个小窗口显示登录二维码,登录报错,扫码没反应,解决手机点登录报错没反应,官方文档,手机扫描钉钉二维码登录网站,页面弹出一个小窗口而不是打开一个页面,vue3中使用iframe实现另开小窗口扫码登

🏆 前端领域优质创作者 3694

DM6437的学习过程

一、DM6437的概述       关于视频处理方面,DM6437有很大的优势,DM6437包括一个视频处理子系统(VPSS),带2个可配置的视频/图像外设:一个视频处理前端VPFE用于视频捕捉,一个视频处理后端VPBE用于视频输出。        VPFE包括1个CCDC(CCD 控制器),1个预览引擎Previewer, Histogram(直方图) Module, Auto-Exposu...

YI00000的博客 2387

【YOLOv8全方位学习手册】第四章 数据增强:从扩充数据到塑造模型的认知边界

"""使用 Albumentations 做自定义增强,兼容 YOLO 格式"""])# 转成 numpy# boxes 必须是 [[x_min,y_min,x_max,y_max], ...]# 在数据加载时调用你可以把集成到 PyTorch 的 Dataset 类里,或使用 Ultralytics 自带的DataLoader。

VectorShift的博客 211

Davinci视频采集驱动文档

Davinci视频采集驱动文档 概述 Davinci的视频采集接口的驱动涉及到内容包括I2C,AD芯片,V4L2,视频采集等内容。下面主要分成视频采集接口描述,I2C和A/D芯片,V4L2采集驱动以及V4L2应用程序编程。 名词解释: A-low: YUV:在现代彩色电视系统中,通常采用三管彩色摄像机或彩色CCD摄像机进行摄像,然后把摄得的彩色图像信号经分色、分别放大校...

alexstone2014的博客 403

vscode的launch:program ‘‘ does not exist

如图 一般是由于launch.json文件没有配置好的缘故。在右下角的ADD configuration如选第二个登录: 看到program这一栏,这一栏很重要,一般是选择你CMakelist,编译出来的可执行文件 看我CMakelist, 我生成的可执行文件是test_,所以路径可以这样输入: 我在之前cmake的时候已经有了test_这个文件了 然后就可以运...

狂小虎/Anthony Dave的博客 KuangXiaoHu's Blogs 5万+

Eplan设计师必会—伺服安全回路的设计方法

本文介绍了在EPLAN中设计伺服STO安全回路的方法。首先需查阅伺服驱动器说明书确定STO接线端子,重点关注双通道配置。设计步骤包括:使用黑盒子功能绘制驱动器外形,正确标注STO接线点,并连接安全继电器或PLC的安全输出。关键注意事项有:必须采用双通道独立布线,确保同步动作,遵循故障安全原则,使用常闭触点串联回路。文中以西门子V90和汇川伺服为例说明具体接线要求,并附有项目实例参考。

Qinmingyue13的博客 436

DAVINCI DM6446 开发攻略——V4L2视频驱动和应用分析

        2010年即将过去,有很多感慨需要总结一下,自从2010年1月份开始着手写DM6446开发攻略以来,得到很多网友和客户对本人博客的支持,同时结识一些有诚意的客户,他们对本人和我们团队的认可,在这里表示感谢。特别是南京航空航天大学的一个非常有个性、有良知和责任的李博导,对本团队的项目设计速度的赞赏,让本人非常感动。潜水2个多月没有更新博客,多少有点对不住51CTO的关照,在另一款新产...

weixin_34248118的博客 244

【8】mctp & pldm

异步会check seq,保证不丢包,虽然发一条req,不用等,但是不能发下一条req,等上条response回来后才能发下一条。TO(tag owner)=1是request,TO=0是response,但是异步通信response由0改为1。如下还有kcs,i3c,serial等。

持续更新ing... 1079

从零开始理解DM368的H264视频编码过程(上)

目录〇、前言一、DVSDK的结构和编码程序依赖1.DVSDK目录结构2.程序依赖二、DM368的视频处理子系统三、DM368的编码流程本文资料下载 〇、前言 本系列博客性质为个人学习笔记。记录了我如何从什么都不知道逐渐理解了DVSDK例程的过程。 相关环境搭建可参阅: 从零开始搭建DM368开发环境,安装DVSDK4_02_00_06并编译生成uboot、rootfs和kernel镜像 *本文并不...

Turix附属空间[已停用] 3235

后悔没早点知道!用AI只需四步,一次性讲清高质量论文引言写法(附公式+AI指令)

帮我写SCI 论文撰写引言的“研究背景与重要性”,要求从本研究所属领域的小背景切入,使用现在时态描述该领域的总体状况与发展趋势,突出其在学术研究、实际应用或社会层面的重要价值,并通过一个吸引人的钩子开头,如关键事实、现实问题或学术热点,引导读者关注本主题的重要性。梳理你所在领域和你研究相关的关键成果,但不能罗列所有文献,重点抓核心脉络,这部分最重要的实际上是让你了解别人的研究脉络,然后在每个关键的节点上,引用几篇关键参考文献作支撑,来体现你对领域的熟悉度就可以了。3)研究缺口模糊,没法突出问题的真实价值;

qq_64378896的博客 114

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

嵌入式 视频处理前端

VPFE 1.OVERVIEW 1.1 CCDC 1)产生SD,HD时序信号 2)可编程镜头阴影校正( Lens Shading Correction). 3)支持BT656,YCBCR422(8BIT,16BIT,WITH HS,VS),及14BIT RAW DATA FROM CCD/CMOS 4) 可编程14BIT 到8BIT输出 5)可通过外部写能动信号EN控制向DDR写数据

skdkjxy的专栏 2580

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Davinci源码部署-后端前端部署指南

Davinci源码部署-后端前端部署指南 Davinci源码地址: https://github.com/edp963/davinci 如果这个网址进不去,可能是被墙了,可以在window的hosts文件中添加相关的域名解析,如:34.120.127.130 artifacts.elastic.co、52.74.223.119 github.com、185.199.110.153 edp963.github.io这三个hosts域名解析,使用的工具是站长工具,可以帮你选择近一些的DNS域名。 Davinci

黑马Jack的博客 1740

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

DM6446的视频前端VPFE驱动之ioctl控制(视频缓存区,CCDC,decoder)解析之二

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。 欢迎和大家交流。qq:1037701636 email:200803090209@zjut.com,gzzaigcn2012@gmail.com  本文承接上文的主要内容,对视频缓存区队列的相关执行过程进行一个解析。先给出之前的应用层的调用流程 本文主要涉及到的命令为VIDIOC_QBUF,VIDIOC_DQBUF,VIDIOC_ST

天才之嵌入式 6567

Davinci可视化平台 ——前端部分代码开发

Davinci 是一个 DVaaS(Data Visualization as a Service)平台解决方案,面向业务人员/数据工程师/数据分析师/数据科学家,致力于提供一站式数据可视化解决方案。 Davinci源码地址: https://github.com/edp963/davinci Davinci源码大概分为三部分: 采用React的前端工程 采用Spring Boot的后端工程 采...

huzhenv5的博客 1万+

dm6446 硬件概述

一、处理器系统结构 1、特点       处理器核        –300MHz  ARM926EJ-S™ (MPU) 内核        –600MHz  TMS320C64x+™ DSP 内核   存储资源       –片内L1/SRAM: 112 KB DSP, 40 KB       ARM       –片内L2/SRAM: 64 KB DSP   外设

1167
上一篇: dma_alloc_coherent() 的实现
下一篇: put_user和get_user的使用
独角鲸
博客等级 码龄16年 24粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值