基於Linux的USB 主/從設備之間通信的三種方式

轉載:http://archive.eet-china.com/www.eet-china.com/ART_8800323770_617693_TA_eda530e7.HTMspring

隨着簡單易用的USB接口日益流行,在嵌入式系統中添加對USB接口的支持已成爲大勢所趨。本文經過介紹Linux中支持USB的各類模塊和庫,分析了在Linux上利用USB實現高速串口和以太網鏈接等通訊方式的具體方法。編程

通用串行總線(USB,Universal Serial Bus)是一種很是實用的通訊接口,其應用日益普遍。有三種方法可使運行Linux操做系統的嵌入式系統支持USB接口,本文將對這三種方法逐一進行介紹。網絡

基於Linux的USB設備與USB主機通常有如下三種通訊方式:1.一些功能最完備結構也最複雜的設備採用用戶定製內核模塊來實如今標準USB總線上運行復雜的高級協議,而由USB主機上相應的用戶驅動程序和應用來完成鏈接。2.另外一些基於Linux的USB設備則利用USB總線來實現與主機上所運行的某個應用的簡單的點對點串行鏈接。主機上的應用雖然利用了主操做系統所提供的USB編程接口,但表面看來卻彷佛是在經過一個典型的串口進行通訊。3.最後,還有些設備以主計算機做爲網關,將USB設備鏈接到辦公局域網或互聯網上,從而使USB設備看起彷彿構成了一個以太網。這種方法專業性較強,但一般可行,是主機驅動程序使該方法成爲可能。函數

在這三種方法中,您能夠根據預留給開發的時間長短和指望USB接口在嵌入式應用中所扮演的角色來決定選用那一種方法比較恰當。爲了幫助您作出正確的選擇,下一節將向您介紹這三種方法分別應用於基於Linux的USB設備時的狀況,但首先讓咱們對USB接口作一個大體介紹。工具

 

USB概述 oop

USB是一種方便快捷的接口,可用於爲計算機工做站鏈接一些小配件。根據USB規範的定義,鼠標、鍵盤、音頻播放和錄音設備、照相機、大容量存儲設備以及許多其餘設備都可以經過USB接口,以高達480Mbps的速度鏈接到一臺主計算機。協議定製者對USB上運行的這種複雜的主從式協議作出了仔細的說明,這就幫助保證了全部這些設備之間具有互操做性和兼容性。例如,該協議規定,USB設備只有在被詢問時才能夠回答,而且USB主機會根據所鏈接的USB設備類型的不一樣,採用某些特定的格式,在某些特定的時間段從不一樣的設備獲取數據。性能

USB設備和主機之間一般經過專用的總線控制芯片創建鏈接。在USB主機上,名爲UHCI或OHCI等的控制芯片經過插卡形式加入主機或直接集成到工做站的主板上。在主機一端的總線控制驅動程序管理着主機控制芯片,它同時還跟蹤監視着主機目前鏈接的是哪些USB設備,從而決定應如何與它們通訊。spa

可用於鏈接照相機和鼠標之類USB設備的總線控制器有不少種。其中的一種就在一塊芯片上同時集成了USB接口以及另外一端的串口、I2C接口或並口。USB控制器(包括主機上的和USB設備上的控制器)也可能集成到英特爾StrongARM或 Hitachi H8之類的微控制器中去。這些芯片及其外圍部件有點相似以太網和CAN控制器,不一樣的是他們用於鏈接USB設備,並運行USB協議。操作系統

不少人都知道Linux操做系統中包含了USB主機控制器的驅動程序,於是USB鍵盤、數碼相機以及其餘一些USB設備均可以在一個運行Linux操做系統的桌面工做站上使用。但不多有人知道Linux中還包含了一組USB設備控制器的驅動程序,尤爲是集成到StrongARM SA1110處理器中的控制器。有了這些控制器驅動程序,基於Linux的嵌入式系統就能利用USB接口來與主計算機(運行Linux或其餘操做系統)通訊。.net

大多數USB通訊的實現過程都是雙端的。主機利用一個內核模塊或驅動程序來與USB設備通訊,而USB設備則經過其自身的驅動程序來與主機通訊。根據主機和USB設備所採用的通訊風格的不一樣,驅動程序能夠很簡單明白,也能夠很複雜,很具挑戰性。本文主要關注USB設備端的通訊過程,但也在適當的地方包含了關於主機端通訊過程實現的信息。

 

如下討論的技術應當引發讀者的注意。本文的目的是介紹如何在數碼相機和PDA等基於Linux的USB設備上使用Linux。此處所指的USB設備是嚴格意義上的USB設備,即帶正方形鏈接器的完整的設備,而不是哪些鏈接器形狀爲扁平矩形的設備。此外,USB鏈接的另外一端(一般是一臺PC工做站),應該是一臺USB主機。表1:利用USB創建一個串行鏈接。

 

 

關於USB信息包的格式和通訊參數的詳細信息,見本文的參考文獻。

 

 

經過編寫內核模塊添加USB接口

 

 

1. USB設備端通訊過程

向一個基於Linux的設備中添加USB接口的第一種方法是編寫一個用戶定製的Linux內核模塊,這也是可實現最完備功能的一種作法。採用這種方法時一般須要針對主機的操做系統(Windows, Linux等)開發相應的驅動程序。

 

 

一旦在設備中實現了用戶定製的內核模塊,就可使該設備完成至關複雜的功能,例如仿真一個文件系統,從而容許嵌入式應用將其USB主機看成一個遠程存儲設備。除此之外,採用這種方法以後,設備還能夠具有存儲轉發(store-and-forward)的功能,於是可以在與USB主機的鏈接創建以前對來自嵌入式應用的數據流進行緩衝。

 

 

在基於StrongARM的Linux設備中,內核代碼用於管理芯片所攜帶的USB設備控制器外設,經過調用函數sa1100_usb_open()來初始化。在初始化以後,內核模塊還會調用函數sa1100_usb_get_descriptor_ptr() 和sa1100_usb_set_string_descriptor()來設置在設備查詢期間傳送給USB主機的描述符,其中包含設備的數字廠商號和產品標識符,以及可讓主機用來識別設備的字符串,甚至還有一個序列號域,以便主機能夠惟一地識別一個鏈接在USB接口上的設備,或者在同種型號的多個設備中進行區分。

 

 

設備查詢過程是由USB設備控制器驅動的,而且一旦和USB主機連上以後會自動執行,因此內核模塊必須在USB通訊開始以前設置好每一個設備的描述符。當準備工做就緒以後,USB設備模塊就會調用函數sa1100_usb_start()來通知內核接收主機發來的USB鏈接請求。若是設備模塊在連上USB 主機以前調用了函數sa1100_set_configured_callback(),那麼接着內核模塊就會在查詢過程結束時調用回調函數。回調函數很適合用來在設備上發出警告或給出一些形象的暗示,說明鏈接已經創建。

 

 

若是再也不須要進行USB通訊,那麼設備的內核模塊就會先調用函數sa1100_usb_stop(),而後調用sa1100_usb_close(),來關閉SA1100上的USB控制器。

 

 

StrongARM的 USB控制器支持bulk-in和bulk-out兩種數據傳送方式。當接收來自USB主機的數據包時,內核模塊會調用sa1100_usb_recv(),將一個數據緩衝區的地址和一個回調函數送給它。而後內核中的USB設備控制代碼會從主機取回一個bulk-out數據包,將其內容存入制定的緩衝區,接着調用回調函數。

 

 

下一步,回調函數從接收緩衝區中提取出數據,將其存放到其餘地方,或者將緩衝區空間添加到一個隊列中,而後分配一個新的緩衝區來接收下一個數據包。而後,若是還有數據須要接收,那麼回調函數會從新調用sa1100_usb_recv(),準備接收另外一個數據包。

 

 

向USB 主機發送數據的過程與此相似。內核模塊收集了一幀數據以後,將數據的存放地址、數據長度和回調函數的地址送給sa1100_usb_send()函數。接着,在數據傳送結束以後,內核模塊會調用回調函數。

 

 

在www.embedded.com/code.htm(arch/arm/mach-sa1100/usb-char.c)能夠找到一個叫作usb-char的模塊,這是一個很好的設備端SA1110 Linux USB模塊的例子。該模塊將USB設備與USB 主機之間的鏈接變成一種高速串行連接。此外, usb-eth( arch/arm/mach-sa1100/usb-eth.c)模塊也是個不錯的例子,該模塊將USB變成了一種虛擬的以太型網絡。後面會深刻探討這兩種模塊。

 

 

2. USB主機端通訊過程

 

 

有些很好的主機端USB驅動程序的例子是隨主流Linux操做系統的發佈而提供的,位於The Linux Kernel Archives (kernel.org)發佈的原始內核源代碼中。其中,Handspring Visor 模塊(drivers/usb/serial/visor.c)是一個編寫得更清晰,也更易理解的模塊,它同時也是USB 主機端模塊(drivers/usb/usb-skeleton.c)的模板。

 

 

利用USB實現高速串行通訊

 

 

1. USB設備端通訊過程

 

 

爲了達到最實用的效果,咱們能夠將USB總線簡單地看做一個高速串口,而後,在一些嵌入式設備和應用中,咱們就能夠用USB接口來模擬串口。StrongARM處理器的Linux內核就提供了一個名爲usb-char的USB設備驅動程序,它所完成的剛好就是用USB模擬串口的功能。

 

 

當須要與USB 主機通訊時,Linux操做系統中的USB設備應用只是簡單地打開一個與其usb-char設備節點的鏈接(鏈接類型爲字符型,major number 爲10, minor 爲240),而後就開始讀寫數據。在與USB 主機的鏈接創建以前,read()和write()操做均返回一個錯誤信息。一旦鏈接創建好,而且設備查詢完成以後,USB接口就開始象一個點對點的串口同樣與主機進行通訊。

 

 

這種進行USB數據傳送的方法很是簡單有效,於是usb-char設備模塊發佈以後一直很受歡迎。並且,該模塊還爲經過其餘方法進行USB通訊提供了一個參考。

 

 

在usb-char中,真正的操做開始於usbc_open()函數,列表1給出了函數的一部分代碼。筆者因爲臨時的興趣,對該代碼作了一點修改,取消了錯誤和超時句柄。在此向代碼的原做者Brad Parker、Nicolas Pitre 和Ward Willats致歉。

 

 

twiddle_descriptors()函數用於設置設備的USB描述符。在描述符設置好以後,咱們就能夠開始進行設備查詢,並從USB 主機接收一幀數據。kick_start_rx()函數段的代碼主要用於調用sa1100_usb_recv(),創建回調。

 

 

在USB主機發送一個數據包時,設備的內核模塊會經過回調方式調用rx_done_callback_packet_buffer()函數,將數據包的內容送入一個FIFO隊列,以便能經過read()函數將該數據包返回給usb-char設備節點。

 

 

2. USB主機端通訊過程

 

 

對於運行Linux操做系統的USB 主機,與usb-char相應的USB 主機模塊叫作usbserial。大多數Linux版本中都包含了該模塊,但它並不總能自動加載。一般應在主機與USB設備之間的鏈接創建以前利用modprobe 或insmod加載該模塊。

 

 

USB設備查詢完成以後,主機上的一項應用就會利用某個usbserial設備節點(字符型, major 爲188, minor 大於等於0)與其通訊。這些節點一般叫作/dev/ttyUSBn。Usbserial模塊會報告它將哪個節點分配給了哪一臺USB設備,並將這一信息按以下方式記載在內核消息記錄中:

 


===================================

 


usbserial.c:檢測到通常轉換器

 


usbserial.c:將通常轉換器加入ttyUSB0

 


==================================

 

 

這種鏈接一旦創建,USB 主機上的應用就能夠經過向特定的節點讀或寫的方式與某USB設備通訊。

 

 

此時,筆者並未考慮在運行Win32或其餘類型操做系統的主機上已有相似usbserial的模塊。但用於這些主機上的任何USB驅動程序,只要可以進行bulk-in 和 bulk-out數據傳輸,就極可能是一個近乎完整的驅動程序,只需進行必定的產品調整,並添加與產品綁定的廠商ID。

 

 

Linux主機上還有另外一種相似usbserial模塊的庫,叫作libusb (參見libusb.sourceforge.net)。該庫經過低級的內核系統調用而不是經過usbserial模塊來完成USB數據傳輸,於是在Linux kernel版本上更容易設置和使用。同時,該庫還能提供大量實用的調試功能,十分利於對USB連接上運行的複雜的通訊協議進行調試。

 

 

爲了經過libusb與一個採用了usb-char模塊的USB設備進行通訊,Linux主機應用首先經過庫中的usb_open()函數與設備創建鏈接,而後利用函數usb_bulk_read()和usb_bulk_write()與設備交換數據。Libusb中含有幾個程序範例。

 

 

利用USB實現以太網鏈接

 

 

1. USB 設備端通訊過程

 

 

若是利用USB鏈接來實現高速串口並不是您所但願,那麼您還能夠將全部USB鏈接用做一個以太網。不論在主機端仍是在設備端,Linux均有模塊能實現這一功能。iPAQ(掌上電腦)的Linux內核就獨一無二地採用了這種通訊策略,由於iPAQ硬件中既沒有可訪問的串口也沒有專門的網絡接口。

 

 

StrongARM Linux內核中,有一個叫作usb-eth的模塊(arch/arm/mach-sa1100/usb-eth.c),它利用USB做爲物理媒介,模擬出一個虛構的以太網設備。一旦這種網絡接口建立起來以後,就能夠爲它分配IP地址,而且外部環境均將其做爲一個普通的以太網硬件對待。一旦USB 主機鏈接創建起來,usb-eth模塊就容許USB設備「瀏覽」因特網,拼其餘的IP地址,甚至經過DHCP、HTTP、NFS或者遠程網「交談」,以及收發電子郵件。簡而言之,任何可以在真正的以太網接口上運行的應用均可以原封不動地在usb-eth 上運行,由於這些應用沒法識別它們所使用的其實並不是真正的以太網硬件。

 

 

2. USB 主機端通訊過程

 

 

相應的,在運行Linux操做系統的主機一端,可用來在USB上實現以太網鏈接的內核模塊叫作usbnet。安裝了該模塊以後,一旦主機與USB設備的鏈接創建起來,它就會建立一個虛擬的以太網接口,在主機一端的內核模塊以及用戶應用看來,這個虛擬的接口與真正的以太網接口別無二致。主機端的應用能夠經過拼一個USB設備的IP地址來檢查該設備是否已經連上,若是拼操做成功,那麼就表示設備已經鏈接成功。

 

 

最近出現了一種針對Win32主機的usbnet風格的驅動,叫作Bahia網絡驅動,關於該驅動的詳細信息請訪問www.bahia21.com/download.htm。

 

 

USB通訊的調試

 

 

遺憾的是,在USB 主機與Linux USB設備之間進行通訊時,可以幫助咱們跟蹤通訊過程當中出現的問題的工具實在很少。除了libusb所提供的調試功能之外(該功能十分強大,但對於內核的系統調用接口則無能爲力),在一次失敗的設備查詢或數據傳輸的嘗試過程當中發生了什麼問題?只有內核源代碼和記錄可以提供一些線索。筆者嘗試在開發過程當中向USB 主機和設備代碼中大量添加printk()函數調用,但這種方法會引入額外開銷,從而改變USB代碼自身的性能,這在有些狀況下反而是事與願違。

 

 

對那些但願對 USB設備接口進行逆向工程處理,或者但願查找其產品缺陷的Linux開發者而言,一個叫作USB Snoopy (home.jps.net/~koma)的程序是個不錯的選擇。只是USB Snoopy僅能在Win32主機上運行。關於USB Snoopy的詳細信息或關於常規的USB調試,請參看本文末給出的參考文獻中Jan Axelson撰寫的 「USB Debug Tips」。

 

 

Linux已成爲通用型操做系統

 

 

現在Linux已再也不是USB 主機專用的操做系統了,USB設備也能夠方便地選擇它。並且Linux下的USB通訊太靈活易用了,於是筆者採用其餘易用型串口(RS-232)的日子極可能就此結束,對我而言,這是件好事。

 

 

做者:Bill Gatliff

 


一位嵌入式領域的顧問,同時也是一位免費軟件熱愛者,熱衷於撰寫關於免費軟件的文章並在其項目中使用免費軟件

 


Email: bgat@billgatliff.com。

 

 

參考文獻:

 


1. Ganssle, Jack. "An Introduction to USB Development," Embedded Systems Programming, March 2000, p.79.

 

2. Axelson, Jan. "HIDs Up," Embedded Systems Programming, October 2000, p.61.

相關文章
相關標籤/搜索