打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Linux USB 驱动开发实例(一)——USB摄像头驱动实现源码分析

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的实现,其他类似)

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

总结;


技术点包含了C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等方面。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
摄像头驱动实现源码分析
USB摄像头(中星微ZC301)驱动程序的移植
OTG驱动分析(二)
USB设备主机侧驱动
spi子系统分析
摄像头(WebCam)在Linux操作系统中的驱动方法 (v0.1b) | LinuxSi...
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服