来自:http://blog.chinaunix.net/u/24981/showart_185841.html
注:我主要想看看上层读数据的时候,等待队列是怎么做的:)
Spac5xx
的实现是按照标准的
USB VIDEO
设备的驱动框架编写(其具体的驱动框架可参照
/usr/src/linux/drivers/usb/usbvideo.c
文件),整个源程序由四个主体部分组成:设备模块的初始化模块和卸载模块,上层软件接口模块,数据传输模块。具体的模块分析如下:
一.
初始化设备模块:
该驱动采用了显式的模块初始化和消除函数,即调用
module_init
来初始化一个模块,并在卸载时调用
moduel-exit
函数(此二函数在
2.3.13
内核开始支持)。其具体实现如下:
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);
tatic 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
设备的即插即用功能:
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;
}
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, //
文件定位功能未实现
};
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
功能:
完成设备的关闭,其具体过程是:
static void
spca5xx_close( struct video_device *vdev)
{
struct usb_spca50x *spca50x =vdev->priv;
int i;
PDEBUG (2, "spca50x_close");
down (&spca50x->lock); //
参数设置
spca50x->user--;
spca50x->curframe = -1;
if (spca50x->present) //present
:是或有驱动加载
{
spca50x_stop_isoc (spca50x); //
停止摄相头工作和数据包发送