首页 > 编程语言 >USB摄像头驱动实现源码分析

USB摄像头驱动实现源码分析

时间:2023-01-29 18:23:15浏览次数:42  
标签:USB err lock frame spca50x 源码 video 模块 摄像头

Spac5xx的实现是按照标准的USB VIDEO设备的驱动框架编写(其具体的驱动框架可参照
/usr/src/linux/drivers/usb/usbvideo.c文件),整个源程序由四个主体部分组成:

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

设备模块的初始化模块和卸载模块,上层软件接口模块,数据传输模块。

具体的模块分析如下:

一、初始化设备模块

该驱动采用了显式的模块初始化和消除函数,即调用module_init来初始化一个模块,并在卸载时调用moduel-exit函数

其具体实现如下:

1、模块初始化:总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

 

 

2、模块卸载:

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

 

 

关键数据结构 USB驱动结构,即插即用功能的实现

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

 

 

用两个函数调用spca5xx_probe 和spca5xx_disconnect来支持USB设备的即插即用功能:
a -- spca5xx_probe具体实现如下:

static void * spca5xx_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) 
{    
	struct usb_interface_descriptor *interface;          //USB设备接口描述符 
	struct usb_spca50x *spca50x;                    //物理设备数据结构   
	int err_probe;   
	int i;    
	if (dev->descriptor.bNumConfigurations != 1)        //探测设备是不是可配置     
		goto nodevice;   
	if (ifnum > 0)     
		goto nodevice;    
	interface = &dev->actconfig->interface[ifnum].altsetting[0];  
	MOD_INC_USE_COUNT;    
	interface = &intf->altsetting[0].desc;   
	if (interface->bInterfaceNumber > 0)     
		goto nodevice;    
	if ((spca50x = kmalloc (sizeof (struct usb_spca50x), GFP_KERNEL)) == NULL) //分配物理地址空间     
	{        
		err ("couldn't kmalloc spca50x struct");       
		goto error;     
	}    
 
	memset (spca50x, 0, sizeof (struct usb_spca50x));   
	spca50x->dev = dev;    
	spca50x->iface = interface->bInterfaceNumber;    
	if ((err_probe = spcaDetectCamera (spca50x)) < 0)       //具体物理设备查找,匹配厂商号,设备号(在子程序中)       
	{        
		err (" Devices not found !! ");       
		goto error;     
	}    
	PDEBUG (0, "Camera type %s ", Plist[spca50x->cameratype].name)
	for (i = 0; i < SPCA50X_NUMFRAMES; i++)      
		init_waitqueue_head (&spca50x->frame[i].wq);     //初始化帧等待队列     
		init_waitqueue_head (&spca50x->wq);            //初始化驱动等待队列   
 
	if (!spca50x_configure (spca50x))                  //物理设备配置(主要完成传感器侦测和图形参数配置),主要思想是给控制寄存器写值,读回其返回值,以此判断具体的传感器型号     
	{        
		spca50x->user = 0;        
		init_MUTEX (&spca50x->lock);                  //信号量初始化       
		init_MUTEX (&spca50x->buf_lock);        
		spca50x->v4l_lock = SPIN_LOCK_UNLOCKED;       
		spca50x->buf_state = BUF_NOT_ALLOCATED;     
	}                                    
	else     
	{
		err ("Failed to configure camera");       
			goto error;     
	} 
  
 
	/* Init video stuff */ 
  
	spca50x->vdev = video_device_alloc ();           //设备控制块内存分配   
	if (!spca50x->vdev)     
		goto error;
	memcpy (spca50x->vdev, &spca50x_template, sizeof (spca50x_template));  
	//系统调用的挂接,在此将驱动实现的系统调用,挂到内核中   
	video_set_drvdata (spca50x->vdev, spca50x); 
  
		if (video_register_device (spca50x->vdev, VFL_TYPE_GRABBER, video_nr) < 0)     
	{                                            
	//video设备注册       
		err ("video_register_device failed");       
		goto error;     
	} 
  
	spca50x->present = 1;   
	if (spca50x->force_rgb) 
    
		info ("data format set to RGB");   
	spca50x->task.sync = 0; 
  
	spca50x->task.routine = auto_bh;   
	spca50x->task.data = spca50x;   
	spca50x->bh_requested = 0; 
			
	MOD_DEC_USE_COUNT; //增加模块使用数   
	return spca50x; //返回数剧结构 
error://错误处理   
	if (spca50x->vdev)     
	{ 
      
		if (spca50x->vdev->minor == -1) 
       
			video_device_release (spca50x->vdev);       
		else 
       
			video_unregister_device (spca50x->vdev);       
		spca50x->vdev = NULL;     
	} 
  
	if (spca50x)     
	{ 
      
		kfree (spca50x);       
		spca50x = NULL;     
	} 
  
	MOD_DEC_USE_COUNT;   
	return NULL; 
nodevice: 
  
	return NULL; 
}

b -- Spca5xx_disconnect 的具体实现如下:总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

static void  spca5xx_disconnect (struct usb_device *dev, void *ptr) 
{    
	struct usb_spca50x *spca50x = (struct usb_spca50x *) ptr;   
	int n;    
	MOD_INC_USE_COUNT; //增加模块使用数   
	if (!spca50x)     
		return;    
	down (&spca50x->lock); //减少信号量   
	spca50x->present = 0;  //驱动卸载置0    
	for (n = 0; n < SPCA50X_NUMFRAMES; n++)       //标示所有帧ABORTING状态     
	{
		spca50x->frame[n].grabstate = FRAME_ABORTING;     
		spca50x->curframe = -1;   
	
	}
	 for (n = 0; n < SPCA50X_NUMFRAMES; n++)       //唤醒所有等待进程     
	{
		if (waitqueue_active (&spca50x->frame[n].wq))       
			wake_up_interruptible (&spca50x->frame[n].wq);   
		
		if (waitqueue_active (&spca50x->wq))        
			wake_up_interruptible (&spca50x->wq);    
	}
	spca5xx_kill_isoc(spca50x);  //子函数终止URB包的传输   
	PDEBUG (3,"Disconnect Kill isoc done");    
	up (&spca50x->lock);  //增加信号量    
	while(spca50x->user) /如果还有进程在使用,进程切换       
		schedule();      
	down (&spca50x->lock);     
	if (spca50x->vdev)  
	{       
		video_unregister_device (spca50x->vdev);   //注销video设备     
		usb_driver_release_interface (&spca5xx_driver,&spca50x->dev->actconfig->interface[spca50x->iface]); //端口释放       
		spca50x->dev = NULL;       
	}
	up (&spca50x->lock); 
#ifdef CONFIG_PROC_FS        
	destroy_proc_spca50x_cam (spca50x); //注销PROC文件 
#endif /* CONFIG_PROC_FS */ 
       
	if (spca50x && !spca50x->user)                      //释放内存空间        
	{           
		spca5xx_dealloc (spca50x);          
		kfree (spca50x);          
		spca50x = NULL;        
	}    
	MOD_DEC_USE_COUNT;                              //减少模块记数   
	PDEBUG (3, "Disconnect complete"); 
}

二、上层软件接口模块:

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

该模块通过file_operations数据结构,依据V4L协议规范,实现设备的关键系统调用,实现设备文件化的UNIX系统设计特点。作为摄像头驱动,其功能在于数据采集,而没有向摄像头输出的功能,因此在源码中没有实现write系统调用。

其关键的数据结构如下:

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

 

 

1. Open功能

完成设备的打开和初始化,并初始化解码器模块。其具体实现如下:

static int  spca5xx_open(struct video_device *vdev, int flags) 
{    
	struct usb_spca50x *spca50x = video_get_drvdata (vdev);   
	int err;    
	MOD_INC_USE_COUNT;                         //增加模块记数   
	down (&spca50x->lock);                              
	err = -ENODEV;    
 
	if (!spca50x->present)                 //检查设备是不是存在,有不有驱动,是不是忙     
		goto out;  
	err = -EBUSY;   
	if (spca50x->user)     
		goto out;    
	err = -ENOMEM;    
	if (spca50x_alloc (spca50x))  
		goto out;                    
	
	err = spca50x_init_source (spca50x);           //初始化传感器和解码模块,在此函数的实现中,对每一款DSP芯片的初始化都不一样,对中星微301P的DSP芯片的初始化在子函数zc3xx_init,其实现方法为寄存器填值。   
	if (err != 0)
	{        
		PDEBUG (0, "DEALLOC error on spca50x_init_source\n");       
		up (&spca50x->lock);        
		spca5xx_dealloc (spca50x);       
		goto out2;     
	} 
 
	spca5xx_initDecoder(spca50x);                  //解码模块初始化,其模块的具体实现采用的是huffman算法   	spca5xx_setFrameDecoder(spca50x);   
	spca50x->user++;    
	
	err = spca50x_init_isoc (spca50x);              //初始化URB(usb request block) 包,启动摄相头,采用同步传输的方式传送数据   
	if (err)     
	{        
		PDEBUG (0, " DEALLOC error on init_Isoc\n");       
		spca50x->user--;        
		spca5xx_kill_isoc (spca50x);       
		up (&spca50x->lock);        
		spca5xx_dealloc (spca50x);       
		goto out2;
	}    
 
	spca50x->brightness = spca50x_get_brghtness (spca50x) << 8;   
	spca50x->whiteness = 0; 
 
out:    
	up (&spca50x->lock); 
out2:   
	if (err)     
		MOD_DEC_USE_COUNT;   
	if (err)    	
	{        
		PDEBUG (2, "Open failed");     
	}   
	else     
	{        
		PDEBUG (2, "Open done");    
 	}    
	return err; 
}

2.Close功能

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

完成设备的关闭,其具体过程是:

 

 

3、 Read功能

完成数据的读取,其主要的工作就是将数据由内核空间传送到进程用户空间

static long spca5xx_rea(struct video_device *dev, char * buf, unsigned long count,int noblock) 
{   
	struct usb_spca50x *spca50x = video_get_drvdata (dev);   
	int i;   
	int frmx = -1;   
	int rc;   
	volatile struct spca50x_frame *frame; 
	
        if(down_interruptible(&spca50x->lock))  //获取信号量       
		return -EINTR; 
	
        if(!dev || !buf){//判断设备情况     
		up(&spca50x->lock);     
		return -EFAULT; 
	}  
  
	if(!spca50x->dev){     
		up(&spca50x->lock);     
		return -EIO; 
	} 
  	
	if (!spca50x->streaming){     
		up(&spca50x->lock);     
		return -EIO; 
	} 
	
        if((rc = wait_event_interruptible(spca50x->wq,     //在指定的队列上睡眠,直到参数2的条件为真 
              spca50x->frame[0].grabstate == FRAME_DONE || 
              spca50x->frame[1].grabstate == FRAME_DONE ||               
	      spca50x->frame[2].grabstate == FRAME_DONE ||               
              spca50x->frame[3].grabstate == FRAME_DONE )))	
        {               
		up(&spca50x->lock);               
		return rc;        
	}  
  
 
	for (i = 0; i < SPCA50X_NUMFRAMES; i++)         //当数据到来     
		if (spca50x->frame[i].grabstate == FRAME_DONE)   //标识数已到       
			frmx = i;   
	if (frmx < 0)     
	{ 
      
		PDEBUG(2, "Couldnt find a frame ready to be read.");       
		up(&spca50x->lock);       
		return -EFAULT;     
	} 
  
	frame = &spca50x->frame[frmx]; 
	PDEBUG (2, "count asked: %d available: %d", (int) count,(int) frame->scanlength);   
	if (count > frame->scanlength)     
		count = frame->scanlength; 
  
	if ((i = copy_to_user (buf, frame->data, count)))   //实现用户空间和内核空间的数据贝     
	{ 
      
		PDEBUG (2, "Copy failed! %d bytes not copied", i);           
		up(&spca50x->lock);       
		return -EFAULT;     
	} 
  
	/* Release the frame */  
	frame->grabstate = FRAME_READY; //标识数据已空 
	up(&spca50x->lock);                           
	return count;//返回拷贝的数据数 
}

4、Mmap功能

实现将设备内存映射到用户进程的地址空间的功能,其关键函数是remap_page_range,其具体实现如下:

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

 

 

5、Ioctl功能:

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

实现文件信息的获取功能

 

 

spca5xx_do_ioctl函数的实现依赖于不同的硬件,本驱动为了支持多种芯片,实现程序过于繁琐,其主要思想是通过copy_to_user(arg,b,sizeof(struct video_capability)函数将设备信息传递给用户进程。

三、数据传输模块

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

源程序采用tasklet来实现同步快速传递数据,并通过spcadecode.c上的软件解码模块实现图形信息的解码。此模块的入口点挂节在spca_open函数中,其具体的函数为spca50x_init_isoc。当设备被打开时,同步传输数据也已经开始,并通过spca50x_move_data函数将数据传递给驱动程序,驱动程序通过轮询的办法实现对数据的访问。

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

 

 

值得一提的是spcadecode.c上解码模块将原始压缩图形数据流yyuyv,yuvy, jpeg411,jpeg422解码为RGB图形,但此部分解压缩算法的实现也依赖于压缩的格式,归根结底依赖于DSP(数字处理芯片)中的硬件压缩算法。总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

四.USB CORE的支持

LINUX下的USB设备对下层硬件的操作依靠系统实现的USB CORE层,USB CORE对上层驱动提供了众多函数接口如:usb_control_msg,usb_sndctrlpipe等,其中最典型的使用为源码中对USB端点寄存器的读写函数spca50x_reg_write和spca50x_reg_read等,具体实现如下:(举spca50x_reg_write的实现,其他类似)

总结送免费学习资料(包含视频、技术学习路线图谱、文档等)

 

标签:USB,err,lock,frame,spca50x,源码,video,模块,摄像头
From: https://www.cnblogs.com/kn-zheng/p/17073473.html

相关文章

  • USB驱动开发学习(键盘+鼠标)
    昨天学习了一下usb鼠标的简单识别,今天来完整的写一套键盘和鼠标的驱动,起码能够支持树莓派使用的。先来写一下键盘的驱动。键盘驱动框架框架部分很简单,和昨天的鼠标基本一......
  • Seata源码结构及事务模式介绍
    1.Seata是什么Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式......
  • usb驱动开发19——驱动生命线
    现在开始就沿着usb_generic_driver的生命线继续往下走。设备的生命线你可以为是从你的usb设备连接到hub的某个端口时开始,而驱动的生命线就必须得回溯到usb子系统的初始化函......
  • eureka源码分析环境准备
    一、工具准备:eureka源码,下载地址:https://github.com/Netflix/eureka/tree/v1.7.2;gradle安装配置环境变量,自行百度;IDEA2018.1版本(其他版本不一定兼容)二、......
  • IdentityServer4源码解析_2_元数据接口
    1|0目录IdentityServer4源码解析_1_项目结构IdentityServer4源码解析_2_元数据接口IdentityServer4源码解析_3_认证接口IdentityServer4源码解析_4_令牌发放接口Id......
  • 数据访问层服务自动注册类封装和使用源码-AutoFac
    项目使用三层结构RepositoryIocFactoryusingSystem;usingSystem.Reflection;usingAutofac;namespaceCommonHelper.AutoInject.Repository{publicclassRe......
  • spring 源码浅析
    AliasRegistry:定义对alias的简单增删改查SimpleAliasRegistry:主要使用map作为alias的缓存,并对接口AliasRegistry进行实现SingletonBeanRegistry:定义对单例的注册及获取Bean......
  • Java安全 - RMI源码分析
    RMI远程服务创建流程分析1、远程对象创建过程首先步入对象的构造方法下一步这里步入了父类UnicastRemoteObject的构造函数,传入一个参数port,作用是将远程对象随即发......
  • 华为C8650USB连接win10无法访问内部储存空间
    C8650,MIUI-破落MIUI-2.3.7无tf卡时显示内部储存150MB不可用,显示SD卡18MB可卸载猜测是分区问题计算机管理-设备-AndroidAdapter显示此设备配置不正确(code1)磁盘空间不足......
  • Spring源码解析
    publicvoidrefresh()throwsBeansException,IllegalStateException{synchronized(this.startupShutdownMonitor){StartupStepcontextRefres......