标签:
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 ();
-
#endif /* CONFIG_PROC_FS */
-
if (usb_register (&spca5xx_driver) < 0)
-
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);
-
info ("driver spca5xx deregistered");
-
#ifdef CONFIG_PROC_FS
-
proc_spca50x_destroy ();
-
#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;
-
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;
-
}
-
-
-
-
-
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)
-
{
-
-
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;
-
for (n = 0; n < SPCA50X_NUMFRAMES; n++)
-
{
-
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);
-
PDEBUG (3,"Disconnect Kill isoc done");
-
up (&spca50x->lock);
-
while(spca50x->user) /如果还有进程在使用,进程切换
-
schedule();
-
down (&spca50x->lock);
-
if (spca50x->vdev)
-
{
-
video_unregister_device (spca50x->vdev);
-
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);
-
#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,
-
.release = spca5xx_close,
-
.read = spca5xx_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);
-
if (err != 0)
-
{
-
PDEBUG (0, "DEALLOC error on spca50x_init_source\n");
-
up (&spca50x->lock);
-
spca5xx_dealloc (spca50x);
-
goto out2;
-
}
-
-
spca5xx_initDecoder(spca50x);
-
spca50x->user++;
-
-
err = spca50x_init_isoc (spca50x);
-
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)
-
{
-
spca50x_stop_isoc (spca50x);
-
spcaCameraShutDown (spca50x);
-
for (i = 0; i < SPCA50X_NUMFRAMES; i++)
-
{
-
if(waitqueue_active(&spca50x->frame[i].wq))
-
wake_up_interruptible (&spca50x->frame[i].wq);
-
}
-
if(waitqueue_active (&spca50x->wq))
-
wake_up_interruptible (&spca50x->wq);
-
}
-
-
up(&spca50x->lock);
-
spca5xx_dealloc (spca50x);
-
-
PDEBUG(2,"Release ressources done");
-
MOD_DEC_USE_COUNT;
-
}
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,
-
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;
-
}
-
-
-
frame->grabstate = FRAME_READY;
-
up(&spca50x->lock);
-
return count;
-
}
4、Mmap功能
实现将设备内存映射到用户进程的地址空间的功能,其关键函数是remap_page_range,其具体实现如下:
-
static int spca5xx_mmap(struct video_device *dev,const char *adr, unsigned long size)
-
{
-
-
unsigned long start = (unsigned long) adr;
-
struct usb_spca50x *spca50x = dev->priv;
-
unsigned long page, pos;
-
if (spca50x->dev == NULL)
-
return -EIO;
-
-
PDEBUG (4, "mmap: %ld (%lX) bytes", size, size);
-
if(size > (((SPCA50X_NUMFRAMES * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE -1)))
-
return -EINVAL;
-
-
if (down_interruptible(&spca50x->lock))
-
return -EINTR;
-
-
pos = (unsigned long) spca50x->fbuf;
-
while (size > 0)
-
{
-
page = kvirt_to_pa (pos);
-
if (remap_page_range (start, page, PAGE_SIZE, PAGE_SHARED)){
-
up(&spca50x->lock);
-
return -EAGAIN;
-
}
-
-
start += PAGE_SIZE;
-
pos += PAGE_SIZE;
-
if (size > PAGE_SIZE)
-
size -= PAGE_SIZE;
-
else
-
size = 0;
-
}
-
-
up(&spca50x->lock);
-
return 0;
-
}
5、Ioctl功能:
实现文件信息的获取功能
-
static int spca5xx_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
-
{
-
struct video_device *vdev = file->private_data;
-
struct usb_spca50x *spca50x = vdev->priv;
-
int rc;
-
-
if(down_interruptible(&spca50x->lock))
-
return -EINTR;
-
-
rc = video_usercopy (inode, file, cmd, arg, spca5xx_do_ioctl);
-
up(&spca50x->lock);
-
return rc;
-
}
spca5xx_do_ioctl函数的实现依赖于不同的硬件,本驱动为了支持多种芯片,实现程序过于烦琐,其主要思想是通过copy_to_user(arg,b,sizeof(struct video_capability)函数将设备信息传递给用户进程。
三、数据传输模块
源程序采用tasklet来实现同步快速传递数据,并通过spcadecode.c上的软件解码模块实现图形信息的解码。此模块的入口点挂节在spca_open函数中,其具体的函数为spca50x_init_isoc。当设备被打开时,同步传输数据也已经开始,并通过spca50x_move_data函数将数据传递给驱动程序,驱动程序通过轮询的办法实现对数据的访问。
-
Void outpict_do_tasklet (unsigned long ptr)
-
{
-
int err;
-
struct spca50x_frame *taskletframe = (struct spca50x_frame *) ptr;
-
taskletframe->scanlength = taskletframe->highwater - taskletframe->data;
-
PDEBUG (2, "Tasklet ask spcadecoder hdrwidth %d hdrheight %d method %d",
-
taskletframe->hdrwidth, taskletframe->hdrheight,
-
taskletframe->method);
-
-
err = spca50x_outpicture (taskletframe);
-
if (err != 0)
-
{
-
PDEBUG (0, "frame decoder failed (%d)", err);
-
taskletframe->grabstate = FRAME_ERROR;
-
}
-
else
-
{
-
taskletframe->grabstate = FRAME_DONE;
-
}
-
-
if (waitqueue_active (&taskletframe->wq))
-
wake_up_interruptible (&taskletframe->wq);
-
}
值得一提的是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的实现,其他类似)
-
static int spca50x_reg_write(struct usb_device *dev,__u16 reg,__u16 index,__u16 value)
-
{
-
int rc;
-
rc = usb_control_msg(dev,
-
usb_sndctrlpipe(dev, 0),
-
reg,
-
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-
value, index, NULL, 0, TimeOut);
-
-
PDEBUG(5, "reg write: 0x%02X,0x%02X:0x%02X, 0x%x", reg, index, value, rc);
-
if (rc < 0)
-
err("reg write: error %d", rc);
-
return rc;
-
}
Linux USB 驱动开发实例(一) —— USB摄像头驱动实现源码分析
标签:
原文地址:http://blog.csdn.net/qq_21593899/article/details/51726707