首頁/ 汽車/ 正文

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

Spac5xx的實現是按照標準的USB VIDEO裝置的驅動

框架

編寫(其具體的驅動框架可參照/usr/src/linux/drivers/usb/usbvideo。c檔案),整個源程式由四個主體部分組成:

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

裝置模組的初始化模組和解除安裝模組,上層軟體介面模組,資料傳輸模組。

具體的模組分析如下:

一、初始化裝置模組

該驅動採用了顯式的模組初始化和消除函式,即呼叫module_init來初始化一個模組,並在解除安裝時呼叫moduel-exit函式

其具體實現如下:

1、模組初始化:

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

2、模組解除安裝:

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

關鍵資料結構

USB驅動結構,即插即用功能的實現

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

用兩個函式呼叫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) < 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; }

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系統呼叫。

其關鍵的資料結構如下:

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

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功能

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

完成裝置的關閉,其具體過程是:

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

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, //在指定的佇列上睡眠,直到引數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 < 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; } /* Release the frame */ frame->grabstate = FRAME_READY; //標識資料已空 up(&spca50x->lock); return count;//返回複製的資料數 }

4、Mmap功能

實現將裝置記憶體對映到使用者程序的地址空間的功能,其關鍵函式是remap_page_range,其具體實現如下:

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

5、Ioctl功能:

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

實現檔案資訊的獲取功能

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

spca5xx_do_ioctl函式的實現依賴於不同的硬體,本驅動為了支援多種晶片,實現程式過於繁瑣,其主要思想是透過copy_to_user(arg,b,sizeof(struct video_capability)函式將裝置資訊傳遞給使用者程序。

三、資料傳輸模組

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

源程式採用tasklet來實現同步快速傳遞資料,並透過spcadecode。c上的軟體解碼模組實現圖形資訊的解碼。此模組的入口點掛節在spca_open函式中,其具體的函式為spca50x_init_isoc。當裝置被開啟時,同步傳輸資料也已經開始,並透過spca50x_move_data函式將資料傳遞給驅動程式,驅動程式透過輪詢的辦法實現對資料的訪問。

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

值得一提的是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的實現,其他類似)

總結送免費學習資料(包含影片、技術學習路線圖譜、文件等)

Linux USB 驅動開發例項(一)——USB攝像頭驅動實現原始碼分析

總結;

技術學習路線圖譜零聲教育 第11代 Linux C/C++後端伺服器架構開發 成長體系課程

後臺私信《資料》免費領取更多學習資料(包含影片、、文件等,)

技術點包含了C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK等方面。

相關文章

頂部