- 論壇徽章:
- 0
|
Spac5xx的實現(xiàn)是按照標(biāo)準(zhǔn)的USB VIDEO設(shè)備的驅(qū)動框架編寫(其具體的驅(qū)動框架可參照/usr/src/linux/drivers/usb/usbvideo.c文件),整個源程序由四個主體部分組成:設(shè)備模塊的初始化模塊和卸載模塊,上層軟件接口模塊,數(shù)據(jù)傳輸模塊。具體的模塊分析如下:
一. 初始化設(shè)備模塊:
該驅(qū)動采用了顯式的模塊初始化和消除函數(shù),即調(diào)用module_init來初始化一個模塊,并在卸載時調(diào)用moduel-exit函數(shù)(此二函數(shù)在2.3.13內(nèi)核開始支持)。其具體實現(xiàn)如下:
1.模塊初始化:
module_init (usb_spca5xx_init);
static int __init
usb_spca5xx_init (void)
{
#ifdef CONFIG_PROC_FS
proc_spca50x_create (); //建立PROC設(shè)備文件
#endif /* CONFIG_PROC_FS */
if (usb_register (&spca5xx_driver) //注冊USB設(shè)備驅(qū)動
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設(shè)備驅(qū)動
info ("driver spca5xx deregistered");
#ifdef CONFIG_PROC_FS
proc_spca50x_destroy (); //撤消PROC設(shè)備文件
#endif /* CONFIG_PROC_FS */
}
關(guān)鍵數(shù)據(jù)結(jié)構(gòu): //USB驅(qū)動結(jié)構(gòu),即插即用功能的實現(xiàn)
static struct usb_driver spca5xx_driver = {
"spca5xx",
spca5xx_probe, //注冊設(shè)備自我偵測功能
spca5xx_disconnect, //注冊設(shè)備自我斷開功能
{NULL,NULL}
};
用兩個函數(shù)調(diào)用spca5xx_probe 和spca5xx_disconnect來支持USB設(shè)備的即插即用功能:
spca5xx_probe具體實現(xiàn)如下:
static void *
spca5xx_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id)
{
struct usb_interface_descriptor *interface; //USB設(shè)備接口描述符
struct usb_spca50x *spca50x; //物理設(shè)備數(shù)據(jù)結(jié)構(gòu)
int err_probe;
int i;
if (dev->descriptor.bNumConfigurations != 1) //探測設(shè)備是不是可配置
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))
//具體物理設(shè)備查找,匹配廠商號,設(shè)備號(在子程序中)
{
err (" Devices not found !! ");
goto error;
}
PDEBUG (0, "Camera type %s ", Plist[spca50x->cameratype].name
for (i = 0; i
init_waitqueue_head (&spca50x->frame.wq); //初始化幀等待隊列
init_waitqueue_head (&spca50x->wq); //初始化驅(qū)動等待隊列
if (!spca50x_configure (spca50x))
//物理設(shè)備配置(主要完成傳感器偵測和圖形參數(shù)配置),主要思想是給控制寄存器寫值,讀回其返回值,以此判斷具體的傳感器型號
{
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 (); //設(shè)備控制塊內(nèi)存分配
if (!spca50x->vdev)
goto error;
memcpy (spca50x->vdev, &spca50x_template, sizeof (spca50x_template));
//系統(tǒng)調(diào)用的掛接,在此將驅(qū)動實現(xiàn)的系統(tǒng)調(diào)用,掛到內(nèi)核中
video_set_drvdata (spca50x->vdev, spca50x);
if (video_register_device (spca50x->vdev, VFL_TYPE_GRABBER, video_nr)
{ //video設(shè)備注冊
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; //增加模塊使用數(shù)
return spca50x; //返回數(shù)據(jù)結(jié)構(gòu)
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的具體實現(xiàn)如下:
static void
spca5xx_disconnect (struct usb_device *dev, void *ptr)
{
struct usb_spca50x *spca50x = (struct usb_spca50x *) ptr;
int n;
MOD_INC_USE_COUNT; //增加模塊使用數(shù)
if (!spca50x)
return;
down (&spca50x->lock); //減少信號量
spca50x->present = 0; //驅(qū)動卸載置0
for (n = 0; n //標(biāo)示所有幀ABORTING狀態(tài)
spca50x->frame[n].grabstate = FRAME_ABORTING;
spca50x->curframe = -1;
for (n = 0; n //喚醒所有等待進(jì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); //子函數(shù)終止URB包的傳輸
PDEBUG (3,"Disconnect Kill isoc done");
up (&spca50x->lock); //增加信號量
while(spca50x->user) //如果還有進(jìn)程在使用,進(jìn)程切換
schedule();
down (&spca50x->lock);
if (spca50x->vdev)
video_unregister_device (spca50x->vdev); //注銷video設(shè)備
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) //釋放內(nèi)存空間
{
spca5xx_dealloc (spca50x);
kfree (spca50x);
spca50x = NULL;
}
MOD_DEC_USE_COUNT; //減少模塊記數(shù)
PDEBUG (3, "Disconnect complete");
}
二. 上層軟件接口模塊:
該模塊通過file_operations數(shù)據(jù)結(jié)構(gòu),依據(jù)V4L協(xié)議規(guī)范,實現(xiàn)設(shè)備的關(guān)鍵系統(tǒng)調(diào)用,實現(xiàn)設(shè)備文件化的UNIX系統(tǒng)設(shè)計特點。作為攝相頭驅(qū)動,其功能在于數(shù)據(jù)采集,而沒有向攝相頭輸出的功能,因此在源碼中沒有實現(xiàn)write系統(tǒng)調(diào)用。其關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)如下:
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, //內(nèi)存映射功能
.ioctl = spca5xx_ioctl, //文件信息獲取
.llseek = no_llseek, //文件定位功能未實現(xiàn)
};
1. Open功能:
完成設(shè)備的打開和初始化,并初始化解碼器模塊。其具體實現(xiàn)如下:
static int
spca5xx_open(struct video_device *vdev, int flags)
{
struct usb_spca50x *spca50x = video_get_drvdata (vdev);
int err;
MOD_INC_USE_COUNT; //增加模塊記數(shù)
down (&spca50x->lock);
err = -ENODEV;
if (!spca50x->present) //檢查設(shè)備是不是存在,有不有驅(qū)動,是不是忙
goto out;
err = -EBUSY;
if (spca50x->user)
goto out;
err = -ENOMEM;
if (spca50x_alloc (spca50x))
goto out;
err = spca50x_init_source (spca50x); //初始化傳感器和解碼模塊,在此函數(shù)的實現(xiàn)中,對每一款DSP芯片的初始化都不一樣,對中星微301P的DSP芯片的初始化在子函數(shù)zc3xx_init,其實現(xiàn)方法為寄存器填值。
if (err != 0){
PDEBUG (0, "DEALLOC error on spca50x_init_source\n");
up (&spca50x->lock);
spca5xx_dealloc (spca50x);
goto out2;
}
spca5xx_initDecoder(spca50x); //解碼模塊初始化,其模塊的具體實現(xiàn)采用的是huffman算法
spca5xx_setFrameDecoder(spca50x);
spca50x->user++;
err = spca50x_init_isoc (spca50x); //初始化URB(usb request block) 包,啟動攝相頭,采用同步傳輸?shù)姆绞絺魉蛿?shù)據(jù)
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)
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功能:
完成設(shè)備的關(guān)閉,其具體過程是:
static void
spca5xx_close( struct video_device *vdev)
{
struct usb_spca50x *spca50x =vdev->priv;
int i;
PDEBUG (2, "spca50x_close");
down (&spca50x->lock); //參數(shù)設(shè)置
spca50x->user--;
spca50x->curframe = -1;
if (spca50x->present) //present:是或有驅(qū)動加載
{
spca50x_stop_isoc (spca50x); //停止攝相頭工作和數(shù)據(jù)包發(fā)送
spcaCameraShutDown (spca50x); //關(guān)閉攝相頭,由子函數(shù)spca50x_stop_isoc完成
for (i = 0; i //喚醒所有等待進(jìn)程
{
if (waitqueue_active (&spca50x->frame.wq))
wake_up_interruptible (&spca50x->frame.wq);
}
if (waitqueue_active (&spca50x->wq))
wake_up_interruptible (&spca50x->wq);
}
up (&spca50x->lock);
spca5xx_dealloc (spca50x); //回收內(nèi)存空間
PDEBUG(2,"Release ressources done");
MOD_DEC_USE_COUNT;
}
2. Read功能:
完成數(shù)據(jù)的讀取,其主要的工作就是將數(shù)據(jù)由內(nèi)核空間傳送到進(jìn)程用戶空間。
static long
spca5xx_read(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){ //判斷設(shè)備情況
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, //在指定的隊列上睡眠,直到參數(shù)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 //當(dāng)數(shù)據(jù)到來
if (spca50x->frame.grabstate == FRAME_DONE) //標(biāo)識數(shù)據(jù)已到
frmx = i;
if (frmx
{
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))) //實現(xiàn)用戶空間和內(nèi)核空間的數(shù)據(jù)拷貝
{
PDEBUG (2, "Copy failed! %d bytes not copied", i);
up(&spca50x->lock);
return -EFAULT;
}
/* Release the frame */
frame->grabstate = FRAME_READY; //標(biāo)識數(shù)據(jù)已空
up(&spca50x->lock);
return count; //返回拷貝的數(shù)據(jù)數(shù)
}
3. Mmap功能:
實現(xiàn)將設(shè)備內(nèi)存映射到用戶進(jìn)程的地址空間的功能,其關(guān)鍵函數(shù)是remap_page_range,其具體實現(xiàn)如下:
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) //循環(huán)實現(xiàn)內(nèi)存映射
{
page = kvirt_to_pa (pos);
if (remap_page_range (start, page, PAGE_SIZE, PAGE_SHARED)){ //實現(xiàn)內(nèi)存映射
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;
}
4. Ioctl功能:
實現(xiàn)文件信息的獲取功能,
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); //將信息傳送到用戶進(jìn)程,其關(guān)鍵函數(shù)實現(xiàn)spca5xx_do_ioctl
up(&spca50x->lock);
return rc;
}
spca5xx_do_ioctl函數(shù)的實現(xiàn)依賴于不同的硬件,本驅(qū)動為了支持多種芯片,實現(xiàn)程序過于煩瑣,其主要思想是通過copy_to_user(arg,b,sizeof(struct video_capability)函數(shù)將設(shè)備信息傳遞給用戶進(jìn)程。
三.數(shù)據(jù)傳輸模塊:
源程序采用tasklet來實現(xiàn)同步快速傳遞數(shù)據(jù),并通過spcadecode.c上的軟件解碼模塊實現(xiàn)圖形信息的解碼。此模塊的入口點掛節(jié)在spca_open函數(shù)中,其具體的函數(shù)為spca50x_init_isoc。當(dāng)設(shè)備被打開時,同步傳輸數(shù)據(jù)也已經(jīng)開始,并通過spca50x_move_data函數(shù)將數(shù)據(jù)傳遞給驅(qū)動程序,驅(qū)動程序通過輪詢的辦法實現(xiàn)對數(shù)據(jù)的訪問。
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); //輸出處理過的圖片數(shù)據(jù)
if (err != 0)
{
PDEBUG (0, "frame decoder failed (%d)", err);
taskletframe->grabstate = FRAME_ERROR;
}
else
{
taskletframe->grabstate = FRAME_DONE;
}
if (waitqueue_active (&taskletframe->wq)) //如果有進(jìn)程等待,喚醒等待進(jìn)程
wake_up_interruptible (&taskletframe->wq);
}
值得一提的是spcadecode.c上解碼模塊將原始壓縮圖形數(shù)據(jù)流yyuyv,yuvy, jpeg411,jpeg422解碼為RGB圖形,但此部分解壓縮算法的實現(xiàn)也依賴于壓縮的格式,歸根結(jié)底依賴于DSP(數(shù)字處理芯片)中的硬件壓縮算法。
四.USB CORE的支持:
LINUX下的USB設(shè)備對下層硬件的操作依靠系統(tǒng)實現(xiàn)的USB CORE層,USB CORE對上層驅(qū)動提供了眾多函數(shù)接口如:usb_control_msg,usb_sndctrlpipe等,其中最典型的使用為源碼中對USB端點寄存器的讀寫函數(shù)spca50x_reg_write和spca50x_reg_read等,具體實現(xiàn)如下:(舉spca50x_reg_write的實現(xiàn),其他類似)
static int spca50x_reg_write(struct usb_device *dev,__u16 reg,__u16 index,
__u16 value)
{
int rc;
rc = usb_control_msg(dev, //通過USB CORE提供的接口函數(shù)設(shè)置寄存器的值
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
err("reg write: error %d", rc);
return rc;
}
以上為驅(qū)動程序的具體框架說明,以及其中的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)和函數(shù)說明。
本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u/24981/showart_185841.html |
|