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("Failedtoconfigurecamera");
gotoerror;
}
/*Initvideostuff*/
spca50x->vdev=video_device_alloc();//设备控制块内存分配
if(!spca50x->vdev)
gotoerror;
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)
{
//video设备注册
err("video_register_devicefailed");
gotoerror;
}
spca50x->present=1;
if(spca50x->force_rgb)
info("dataformatsettoRGB");
spca50x->task.sync=0;
spca50x->task.routine=auto_bh;
spca50x->task.data=spca50x;
spca50x->bh_requested=0;
MOD_DEC_USE_COUNT;//增加模块使用数
returnspca50x;//返回数剧结构
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;
returnNULL;
nodevice:
returnNULL;
}
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系统调用。
其关键的数据结构如下:
staticstructvideo_devicespca50x_template={
.owner=THIS_MODULE,
.name="SPCA5XXUSBCamera",
.type=VID_TYPE_CAPTURE,
.hardware=VID_HARDWARE_SPCA5XX,
.fops=&spca5xx_fops,
};
staticstructfile_operationsspca5xx_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摄像头 源码 Linux USB摄像头驱动实现源码分析》对你有帮助,请点赞、收藏,并留下你的观点哦!