1. 程式人生 > >UVC分析——基於UVC的羅技攝像頭C270 hack

UVC分析——基於UVC的羅技攝像頭C270 hack

現在,假如你的手上有一隻攝像頭,它是羅技高清網路攝像頭webcam-C270,還有一塊cortexA8開發板,這塊開發板來自FriendlyARM,已經預裝了linux系統,版本號是最新提供的linux-3.0.8,圖形介面是Qtopia-2.2.0,交叉編譯器是arm-linux-gcc-4.5.1。主機是Fedora9

攝像頭和開發板,這兩樣東西安安靜靜的躺在了你的手裡,準備就緒,狀態良好。而你的任務,就是要讓攝像頭正常的工作在開發板上,並且完成一些簡單的任務,比如說將影象顯示在Qtopia的介面上,並判斷當前的影象中有沒有阿拉伯數字。

(雖然說C270並不支援linux系統,可是支援

linux系統的攝像頭又有幾隻呢?即使C270不支援linux系統,不代表linux系統不支援C270^_^

插上攝像頭試試

在開發板的終端視窗,輸入“cat /proc/kmsg &”,來顯示核心列印資訊。

FriendlyARM最新提供的光碟,裡面是包含了C270的驅動程式和應用程式。將C270連線在開發板的USB口上面,核心會打印出下面的資訊:

<6>[960.933564] usb 1-1.3: new high speed USB device number 7 using s5p-ehci

<6>[961.262234] uvcvideo: Found UVC 1.00 device  (046d:0825)

<6>[961.362302] input: UVC Camera (046d:0825) as /devices/platform/s5p-ehci/usb1/1-1/1-1.3/1-1.3:1.0/input/input5

核心資訊列印有七個日誌級別,數字越低,級別越高。低於當前資訊的列印級別的資訊就不會被顯示出來。<6>[961.262234]的意思是,打印出的資訊級別是KERN_INFO(提示資訊),這個資訊在系統的961.262234這個tick時間被執行。

1-1.3:1.0的意思是,攝像頭使用的根集線器編號為1,集線器埠號為1,集線器(攝像頭使用)埠號為3,配置為1

,介面為0input: UVC Camera說明linux核心認出了這個裝置,而且知道這個裝置是UVC標準攝像頭。

如果攝像頭被正常的識別和驅動,開啟名為“USB 攝像頭”的應用程式(FriendlyARM提供),其介面上就會顯示出影象。

攝像頭如何被識別

如果攝像頭沒有找到正確的驅動,開發者如何確認這一點呢?如果沒有驅動或者安裝了錯誤的驅動,開發者如何安裝正確的驅動呢?

驅動一個LED燈,也就是cortexA8的一個IO口,驅動可以編譯進核心,也可以通過模組的方式載入。然後在/dev下形成裝置檔案。使用者程式通過讀寫裝置檔案,來控制這個IO口。這樣的流程,並不適合USB裝置。“USB裝置是一個非常複雜的東西,官方USB文件中有詳細的描述。幸運的是,Linux核心提供了一個稱為USB核心(USB Core)的子系統來處理大部分的複雜性。”——摘自《LINUX裝置驅動程式》。

USB裝置通過USB Core和驅動交換資料,使用者程式讀寫驅動的資料,因此USB Core作為中間層,需要把裝置和驅動,正確的對應起來。每個USB裝置插入主機的時候,主機都會請求裝置的device descriptor。裝置描述符device descriptor提供了USB協議版本、廠商ID、產品ID等資訊。

廠商IDidVendor)和產品IDidProduct),是USB Core把裝置和驅動聯絡起來的關鍵。USB裝置具備自身的idVendoridProduct;驅動程式也會定義自己的idVendoridProduct。如果USB Core從裝置請求到到的ID,正好符合驅動程式的定義,那麼USB Core就知道這個ID的裝置使用的是這個ID的驅動程式。

idProduct和idVendor16位的數值。在核心列印資訊中出現的046d0825這兩個數,就是idVendoridProduct。在/sys/bus/usb/drivers/usb/1-1.3/這個目錄下,檢視idProductidVendor這兩個檔案,也會發現它們的值是046d0825046d是羅技公司的idVendor

什麼是UVC裝置

羅技C270是一個標準的UVC裝置,需要UVC driver

Its important for me for a webcam to be UVC compatible where UVC is the USB Video Class, and defines a standard/specification for devices capable of streaming video. For example, being UVC compatible was a logo requirement for Windows Vista which helped make this class of device popular, and fortunately there is good support under GNU/Linux。”

http://forums.opensuse.org/blogs/oldcpu/logitech-c270-webcam-opensuse-110博主Oldcpu是歐洲的一名航天器操作工程師,同時也是一名linux愛好者。Oldcpu為這個C270準備的系統是GNU/Linux (openSUSE),而驅動C270的關鍵是“UVC compatible”。

The USB Device Class Definition for Video Devices, or USB Video Class, defines video streaming functionality on the Universal Serial Bus. Much like nearly all mass storage devices (USB flash disks, external SATA disk enclosures, ...) can be managed by a single driver because they conform to the USB Mass Storage specification, UVC compliant peripherals only need a generic driver.

The UVC specification covers webcams, digital camcorders, analog video converters, analog and digital television tuners, and still-image cameras that support video streaming for both video input and output.

這段文字來自http://www.ideasonboard.org/uvc/。在網頁下方,列出了UVC支援的webcam型號,其中以046d作為idVendor的,就是羅技攝像頭。

046d:0825(logitech HD Webcam C270)出現在這張表中。

UVC是在linux-2.6.38版本時加入核心的,那麼更早的版本沒有整合UVC。好在FriendlyARM提供核心版本是linux-3.0.8,裡面集成了UVC驅動。在核心原始碼的“Documentation/video4linux/uvcvideo.txt”中,是有關於UVC的說明。

UVC驅動程式的位置 

根據Documentation/video4linux/uvcvideo.txt給出的資訊,UVC裝置不需要編寫單獨的驅動,它在使用者空間提供了類似驅動的介面。如果使用者需要實現ioctl功能,就需要在使用者空間呼叫這個介面。

linux-3.0.8原始碼的driver目錄下,執行find . -name *uvc* -type f,發現,uvc檔案集中在./usb/gadget/./media/video/uvc/這兩個目錄。USB gadget雖然有UVC部分,但它並不是UVC driver。真正的UVC driver,是./media/video/uvc/

./media/video/uvc/目錄下有12個檔案:

uvc*.c    8個) 

Kconfig

Makefile

modules.builtin

modules.order

Makefile用來指定生成目標檔案的規則。Kconfig用在定義生成的目標檔案在make menuconfig時選項名稱。.c檔案是UVC的實現。

UVC驅動程式的Makefile 

uvcvideo-objs  := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o uvc_status.o uvc_isight.o

ifeq ($(CONFIG_MEDIA_CONTROLLER),y)

        uvcvideo-objs  += uvc_entity.o

endif

obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o

在這個Makefile中,每個.c檔案生成一個.o檔案。除了uvc_entity.o外的共7.o檔案,連結成一個uvcvideo-objs

如果條件“($(CONFIG_MEDIA_CONTROLLER),y)”成立,則把uvc_entity.o也連結進去,生成一個uvcvideo-objs。生成的uvcvideo-objs,就是uvcvideo.o

UVC驅動程式的Kconfig

config USB_VIDEO_CLASS

        tristate "USB Video Class (UVC)"

        ---help---

          Support for the USB Video Class (UVC).  Currently only video

          input devices, such as webcams, are supported.

Device Drivers -> Multimedia support -> Video capture adapters -> V4L USB Devices -> USB Video Class (UVC),這個選項就是在make menuconfig的時候對應的Kconfig中的內容。

如果使用者選擇了此選項,那麼Makefile產生的uvcvideo.o就會被編譯進核心。

FriendlyARM提供的開發板設定,這一項是[*],也就是將uvcvideo.o預設靜態編譯在核心中。

Linux下的編譯環境,沒有像Window下有那麼多可愛的按鈕讓你按下去,沒有工程的概念,也沒有後臺幫你把所有的事情都搞定了。Linux的編譯過程很麻煩,因為你得自己使用makefile檔案告訴它該怎麼編譯。這是linux的可恨之處,也是linux的可愛之處~

UVC驅動程式的uvc_driver.c

為了方便除錯,首先需要將靜態編譯模組UVC Driver,改成動態載入的模組。要不然,每次修改UVC Driver的時候,都需要重新編譯核心。

(如何將靜態編譯的模組,改成動態載入的模組呢?在編譯核心make menuconfig的時候,將Kconfig的對應項由“*”修改為“M”,重新編譯核心make zImage並使用新的zImage引導系統。原始檔driver/media/video/uvc/目錄下將生成uvcvideo.ko檔案,將uvcvideo.ko拷貝到嵌入式開發板的/lib/modules/3.0.8-FriendlyARM下,在終端輸入指令modprobe uvcvideo。如果終端指示找不到uvcvideo這個檔案,則需要先進入上一級目錄,執行depmod。)

修改uvc_driver.c,在其中加入除錯語句,就能跟蹤UVC攝像頭的驅動步驟。uvc_driver.c是一個module檔案,因為它具備了兩個很典型的module函式:

static int __init uvc_init(void)

{

        int result;

        result = usb_register(&uvc_driver.driver);

        if (result == 0)

                printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");

        return result;

}

static void __exit uvc_cleanup(void)

{

        usb_deregister(&uvc_driver.driver);

}

uvc_init是模組裝載函式,僅僅執行了usb_register(&uvc_driver.driver)這個操作。如果usb_register成功,會列印核心資訊,並返回0,如果不成功,則返回錯誤程式碼。一般來說,模組初始化的“__init”函式中,除了註冊裝置外,不進行任何動作,而需要進行的實質初始化動作,放在“open”函式中。這是因為模組註冊之後,使用者很少會去解除安裝它,如果“__init”函式中包含了對資源的佔領,那麼在它並不工作的時候,也無法釋放這些資源。

被“__init”註冊的struct uvc_driver定義在driver/media/video/uvcvideo.h中:

struct uvc_driver {

        struct usb_driver driver;

};

(好吧,雖然打著uvc_driver的旗號,尼瑪就是一個普通的USB裝置好不?~)知道了uvc_driver是什麼,就能夠理解在uvc_driver.c中定義的struct uvc_driver uvc_driver

struct uvc_driver uvc_driver = {

        .driver = {

                .name           = "uvcvideo",

                .probe          = uvc_probe,

                .disconnect     = uvc_disconnect,

                .suspend        = uvc_suspend,

                .resume         = uvc_resume,

                .reset_resume   = uvc_reset_resume,

                .id_table       = uvc_ids,

                .supports_autosuspend = 1,

        },

};

.name是要註冊進USB Core的驅動名字,它會顯示在/sys/bus/usb/drivers/下。但它並不是應用程式將要讀寫的裝置檔案。當UVC裝置插入的時候,會呼叫.uvc_probe。當UVC裝置拔出的時候,會呼叫.uvc_disconnect。核心需要.id_table用來判斷插入的裝置,是否自身適用。

定義.id_tableuvc_ids列表中,並沒有[046d:0825]這一項,但是探測回撥函式.uvc_probe確實又被呼叫了。這是為什麼呢?

const struct struct usb_device_id *id_table指向struct usb_device_id表的指標,該表中包含了一列該驅動程式可以支援的所有不同型別的USB裝置。如果沒有設定該變數,USB驅動程式中的探測回撥函式不會被呼叫。如果想要驅動程式對於系統中的每一個USB裝置都被條用,建立一個只設置driver_info欄位的條目。fromLINUX裝置驅動程式》

雖然uvc_ids列表中的36項都沒有[046d:0825],但是最後一項是:

 { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },

USB_INTERFACE_INFO定義在linux/usb.h:

#define USB_INTERFACE_INFO(cl, sc, pr) \

        .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \

        .bInterfaceClass = (cl), \

        .bInterfaceSubClass = (sc), \

        .bInterfaceProtocol = (pr)

.match_flags定義的USB_DEVICE_ID_MATCH_INT_INFO,說明只要USB裝置的介面描述符bInterfaceClassbInterfaceSubClassbInterfaceProtocol這三個變數,符合uvc_driver.c中定義的USB_CLASS_VIDEO10,那麼驅動也是可以被這個USB裝置使用的。這三個變數的意義,由USB協會定義。USB_CLASS_VIDEO定義在usb.h中。當把uvc_ids列表中的最後一項註釋掉之後,攝像頭也不再能被識別。

UVC攝像頭被正常識別之後,uvc_probe函式就會被呼叫。為了正常使用uvc_probe裡面的uvc_trace函式(定義在driver/media/video/uvcvideo.h中),需要將uvc_trace_param這個變數置為0x07FF。(意思是不管uvc你有什麼資訊,儘管全都顯示出來吧~)

當修改了uvc_trace_param後,原來被隱藏的UVC核心除錯資訊就會被顯示出來。(你會發現這時候核心會打印出一大串資訊,終端會刷刷刷的刷屏,弄得俺差點以為是segment錯誤)而這一大串資訊都來自probe函式(或者由它呼叫的函式)。

UVC驅動程式的uvc_driver.cprobe函式

當插上攝像頭時,usb core會自動呼叫probe函式,這個函式很重要。probe函式會完成很多事情。(既然是除錯,就讓我們讓它完成更多的事情,來幫助理解probe是怎麼工作的)

static int uvc_probe(struct usb_interface *intf, const struct usb_device_id *id)

上面就是probe函式頭了,它傳遞了兩個引數,這兩個引數由usb core直接賦值,使用者不用關心如何被賦值,只需要關心如何使用它們就好了。

probe函式需要完成的工作有:

NO.1:從一個struct usb_interface獲取一個控制的struct usb_device

struct usb_device *udev = interface_to_usbdev(intf);

probe函式被呼叫時,usb core僅僅傳輸了usb_interfaceusb_device_id這兩個引數。通過usb_interface就可以獲取到當前的usb_device了。usb_device就是我們的羅技攝像頭C270了,裝置描述符、配置描述符等等都可以通它來取得。

為嘛還要通過函式interface_to_usbdev獲取usb_device,而不通過引數直接傳遞呢?

一個usb_interface代表了一個基本功能,而每個USB驅動程式控制一個usb_interface。所有的USB驅動程式都用usb_interface來和usb core進行通訊,所以驅動程式就用usb_interface來做入口引數啦。

usb_device通常具備一個或多個usb_host_config;一個usb_host_config通常具備一個或多個usb_interface。(usb_device是爺爺,usb_host_config是爸爸,usb_interface是孩子)

NO.2:(這一步是俺加的)列印裝置描述符/配置描述符/介面描述符的內容。

知道了usb_device之後,就可以知道它包含的關於攝像頭C270更多的內容。usb_device有一項是struct usb_device_descriptor,也就是攝像頭C270的裝置描述符。

usb_device包含的配置有兩個,一個是config,一個是actconfig,從字面上就能理解它們的區別了,前者用來儲存所有的配置,後者用來儲存當前的配置。羅技攝像頭C270只有一個配置,所以configactconfig也是一樣的。

actconfig包含了struct usb_interface,實際上羅技攝像頭C2704個。雖然有4個可用,但是目前活動的只有1個,一般來說是interface 0。活動的interface 0會傳輸裝置描述符、配置描述符,而其他3個介面描述符也會和配置描述符一起被傳輸過來。

使用printk函式將它們的內容打印出來,列印的時候注意檢查指標是否為NULL

printk(KERN_NOTICE "maria_debug: bLength=0x%04x\n", udev->descriptor.bLength);

printk(KERN_NOTICE "maria_debug: bLength=0x%04x\n", 

udev->actconfig->desc.bLength);

intf_t = udev->actconfig->interface[i];

intf_desc_t = &(intf_t->altsetting->desc);

printk(KERN_NOTICE "maria_debug: bLength=0x%04x\n", intf_desc_t->bLength);

就能得到下面的三張表:

O f f s e t

F i e l d  

S i z e 

V a l u e

D e s c r i p t i o n

0

bLength

1

0x12

描述符長度為18

1

bDescriptorType

1

0x01

這是裝置描述符

2

bcdUSB

2

0x0200

2.00的USB協議版本

4

bDeviceClass 

1

0xEF

Miscellaneous Device Class 

5

bDeviceSubClass

1

相關推薦

UVC分析——基於UVC攝像頭C270 hack

現在,假如你的手上有一隻攝像頭,它是羅技高清網路攝像頭webcam-C270,還有一塊cortexA8開發板,這塊開發板來自FriendlyARM,已經預裝了linux系統,版本號是最新提供的linux-3.0.8,圖形介面是Qtopia-2.2.0,交叉編譯器是ar

銷量預測和用戶行為的分析--基於ERP的交易數據

測試數據 為什麽 5% 重要 思考 發的 span 左右 參考 寫在前面: 這段時間一直都在看一些機器學習方面的內容,其中又花了不少時間在推薦系統這塊,然後自己做了一套簡單的推薦系統,但是跑下來的結果總覺得有些差強人意,我在離線實驗中得到Precision,Recall一般

u-boot-201611 啟動過程分析——基於smdk2410

u-bootu-boot-201611 啟動過程分析——基於smdk2410

基於ONVIF協議的攝像頭開發總結

方式 遠程 object amp 利用 構建 bin ide code <什麽是ONVIF協議> 2008年5月,由安訊士(AXIS)聯合博世(BOSCH)及索尼(SONY)公司三方宣布攜手共同成立一個國際開放型網絡視頻產品標準網絡接口開發論壇,取名為O

Deepin 15 如何使用 無線鍵盤/鼠標(采用優聯技術)

無需 如何使用 html 其他 blog ech 鼠標 文章 strong 1、羅技的“無線優聯技術”還是非常強大的,它跟具體的操作系統無關; 2、你只需要 讓“優聯接收器(一個USB設備)”跟 “無線鍵盤/鼠標

宿主機系統 Deepin 15.4,解決 Virtualbox 5.1 中 XP虛擬機無法使用 USB設備(如:U盤、優聯接收器等)的問題

rdquo brush 完成後 usb 5.4 extension user network ron 軟件環境 宿主機系統:Deepin 15.4.1, 虛擬機軟件:VirtualBox 5.1 虛擬機系統:XP 操作步驟如下: (1) 安裝 Virtu

Mx Master 2s 無線滑鼠 購買、使用、體驗

    既然買了15k的電腦,怎麼也得配個好點的滑鼠。有線滑鼠已經有了羅技的G300s,打聯盟已經夠用了。     工作轉正後入手新滑鼠了,接下來談談這個Mx Master 2s 。     手感:上手使用之後的第一個

Linux 核心中獲取時間分析基於do_gettimeofday()

Linux 核心中獲取時間分析基於do_gettimeofday() 核心程式碼能一直獲取一個當前時間的表示,通過檢視jifies的值。通常這個值只代表從最後一次啟動以來的時間,這個事實對驅動來說無關,因為它的生命週期受限於系統的uptime。 驅動可以使用jifie

滑鼠 MM Mx Master 2 掉幀的一種解決方法

入手MM已經月餘了,不得不說這是MAC下的相當有力的助手,但是這幾天發現掉幀嚴重,要不就卡,要不就飛,網上說需要調整藍芽和wifi 的服務順序,我也調整了,無效。忽然發現藍芽裝置裡面有兩個 MM 連結,因為MM可以同時接三個裝置,而我不知道什麼時候在MAC裡面填了兩個MM連結,雖然有一個是未連結,但

全面講解分析基於區鏈塊的實現貨幣中心化的商城報單系統

1、前言: 系統是基於區鏈塊分散式智慧合約技術,實現貨幣的去中心化、點對點無損無痕流通,讓流通產生價值,也可以通證(雙倍積分釋放)提現,對接交易所可用錢包轉出。年賺百萬,只要輕鬆轉動通證。 2、名稱說明: 商城】1、贈送積分套餐購物板塊 2、商城商家版(餘額和通證可以購物) 【註冊】

Tomcat 原始碼分析 WebappClassLoader 分析 (基於8.0.5)

0. 疑惑 在剛接觸 Tomcat 中的ClassLoader時心中不免冒出的疑惑: "Tomcat 裡面是怎麼樣設計ClassLoader的, 這樣設計有什麼好處?"; 我們先把這個問題留著, 到最後在看 ! 1. Java 中 ClassLoader 類別 1. BootstrapC

cgroup原始碼分析——基於centos3.10.0-693.25.4

  核心升級完測試兄弟跑ltprun套件,發現跑完後cgroup失效了。看系統一切執行正常核心也沒啥錯誤日誌,又不熟cgroup的實現,在一頓翻程式碼後發現cgroup註冊了CPU熱插拔的notifier chain。去翻ltp的測試內容發現,CPU熱插拔赫然在列。為啥不一開始先翻ltp

M558 鼠標維修記錄

內部 故障 ron mage 使用 image 後退 com 鼠標 羅技 M558 鼠標維修記錄 故障現象 按鍵不靈敏 拆機內部圖 前進鍵 後退鍵 左鍵 右鍵 中鍵 自定義功能鍵 使用的是 OMRON 按鍵,好東西,質量可以。 但畢竟是機械的東西,還是有老化,用萬用

mmap原始碼分析--基於3.10.0-693.11.1

mmap是個既簡單又好用的東西,對於讀寫檔案,它減少了一次記憶體拷貝,對於記憶體申請,它可以方便的申請到大塊記憶體,用於自己管理。今天就來說說mmap的實現。 mmap的原型是這樣的: void *mmap(void *addr, size_t length,

史上最全的基於ffmpeg+sdl網路攝像頭編解碼播放資料(包含交叉編譯過程,附帶完整原始碼)

原創博文,嚴禁私自轉載,轉載請註明出處!!! 近期,由於工作需要,要在開發板上跑一個攝像頭,攝像頭款式比較老,不支援rtsp格式,所以選擇編譯ffmpeg+sdl實現軟解碼播放攝像頭,特此記錄整個編譯過程(非常之艱辛,發文留念) 在ubuntu上交叉編譯環境的搭建:因為開發板上搭建的程式的執

ArrayList的原始碼分析(基於jdk1.8)

1.初始化 transient Object[] elementData; //實際儲存元素的陣列 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; public ArrayList() { //初

LinkedList的原始碼分析(基於jdk1.8)

1.初始化 public LinkedList() { } 並未開闢任何類似於陣列一樣的儲存空間,那麼連結串列是如何儲存元素的呢?   2.Node型別 儲存到連結串列中的元素會被封裝為一個Node型別的結點。並且連結串列只需記錄第一個結點的位置和最後一個結點的位置。然後每一個結

springmvc工作原理以及原始碼分析(基於spring3.1.0)

springmvc是一個基於spring的web框架.本篇文章對它的工作原理以及原始碼進行深入分析. 一、springmvc請求處理流程   引用spring in action上的一張圖來說明了springmvc的核心元件和請求處理流程:       

OpenStack訊息佇列AMQP技術具體實現 [分析基於icehouse]

       本文系技術總結,供將來需要時翻開看看, 如有錯誤, 歡迎交流  第一部分:關鍵技術        先發下感慨, 社群的code真是變化太快, code結構發生了很大的重構增強, 關於訊息佇列, openstack.common.rpc慢慢的都會轉移到oslo

Appro-RTSP詳細結構分析——基於live555的視訊直播

參考  http://wenku.baidu.com/link?url=P-Nw0PkeXh41jPAsNkIWhyXLe3tNcXUlethAQ3eanDDU3cTSE5LeYYoqSy8CxxcdBTbwmOZ5tikbFPmmgOCHWfN8QcQY0uWLupzJW