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

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

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


具体的模块分析如下:

 一、初始化设备模块

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

        其具体实现如下: 

1、模块初始化:

module_init (usb_spca5xx_init); 

static int __init  usb_spca5xx_init (void) 
{  
#ifdef CONFIG_PROC_FS                     
	proc_spca50x_create ();//建立PROC设备文件 
#endif /* CONFIG_PROC_FS */    
	if (usb_register (&spca5xx_driver) < 0) //注册USB设备驱动    
		return -1;    
	info ("spca5xx driver %s registered", version);   
	return 0; 
}

2、模块卸载:

module_exit (usb_spca5xx_exit); 

static void __exit  usb_spca5xx_exit (void) 
{    
	usb_deregister (&spca5xx_driver); //注销USB设备驱动   
	info ("driver spca5xx deregistered"); 
#ifdef CONFIG_PROC_FS    
	proc_spca50x_destroy ();//撤消PROC设备文件 
#endif /* CONFIG_PROC_FS */ 
} 


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

static struct usb_driver spca5xx_driver = {        
	"spca5xx",         
	spca5xx_probe, //注册设备自我侦测功能        
	spca5xx_disconnect,//注册设备自我断开功能        
	{NULL,NULL} 
};

用两个函数调用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系统调用。

其关键的数据结构如下:

static struct video_device spca50x_template = {   
	.owner = THIS_MODULE,  
	.name = "SPCA5XX USB Camera",   
	.type = VID_TYPE_CAPTURE,  
	.hardware = VID_HARDWARE_SPCA5XX,   
	.fops = &spca5xx_fops,         
}; 


static struct file_operations spca5xx_fops = {   
	.owner = THIS_MODULE,  
	.open = spca5xx_open, //open功能   
	.release = spca5xx_close,//close功能   
	.read = spca5xx_read, //read功能   
	.mmap = spca5xx_mmap, //内存映射功能   
	.ioctl = spca5xx_ioctl, //文件信息获取  
	.llseek = no_llseek,//文件定位功能未实现 
}; 









你可能感兴趣的:(Linux USB摄像头驱动实现源码分析)