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)
{
//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,//文件定位功能未实现
};