基于Linux的USB設備
引言
通用串行總線(xiàn)(USB)是一種快速而靈活地連接配件與計算機工作站的接口,其應用非常廣泛。Linux中除了包含對USB主機控制器的驅動(dòng),還含有USB設備控制器,尤其是集成在StrongARM SA1110處理器上的控制器的驅動(dòng)。這些控制器驅動(dòng)通過(guò)使用USB可使基于Linux的嵌入式系統與主機 (運行的可以是Linux,或不是)進(jìn)行通信。這里提供三種方法給運行Linux操作系統的嵌入式系統增加USB支持,可采用其中一種與USB主機展開(kāi)通信。
第一種,最復雜的設備采用專(zhuān)門(mén)編寫(xiě)的內核模塊解析標準USB總線(xiàn)上通行的錯綜復雜的高層協(xié)議;相應的USB主機定制驅動(dòng)和應用程序來(lái)完成連接。第二種,有些基于Linux的設備把總線(xiàn)當作一種簡(jiǎn)單的運行在主機上的點(diǎn)對點(diǎn)串行連接使用;主機應用程序采用主機操作系統提供的USB編程界面,而其外在表現則仿佛是在通過(guò)一種典型的串行端口進(jìn)行通信。第三種,另有一些設備把USB看作一種以太網(wǎng)絡(luò ),它們用主機作網(wǎng)關(guān),把USB設備與辦公LAN或Internet相連接。通常的做法是使用專(zhuān)門(mén)的主機驅動(dòng)實(shí)現它。
最佳方案的選擇取決于研發(fā)所需時(shí)間,以及針對具體嵌入式應用,要把USB接口作成什么樣。以下對這三種方法如何在基于Linux的USB設備上的應用逐一進(jìn)行描述。本文是關(guān)于如何在基于Linux的照相機和PDA之類(lèi)的USB設備上使用Linux的論述,在此,USB是指由方形連接器而非扁平矩形連接器構成的USB設備。
內核模塊
把USB加到基于Linux的設備上的第一種方法是編寫(xiě)一個(gè)定制的Linux內核模塊。這種方法通常要求相應開(kāi)發(fā)主機操作系統(Windows、Linux以及其它OS)的驅動(dòng)。
借助定制內核模塊在設備中的安裝,可以進(jìn)行文件系統仿真等,使嵌入式應用將其USB主機當作遠程存儲設備對待。這一方法的另一潛在用途是構成一種存儲轉發(fā)字符設備,從嵌入式應用程序中緩沖數據流,直到USB主機連接完成建立為止。
對于基于StrongARM的Linux設備,其USB應用內核模塊調用sa1100_usb_open(),對管理芯片的板上USB設備控制器外設的內核代碼進(jìn)行初始化。然后該模塊調用sa1100_usb_get_descriptor_ptr()和sa1100_usb_set_string_descriptor(),通過(guò)枚舉過(guò)程對USB主機的給定USB描述符進(jìn)行設置。這些描述符包括設備供貨商及產(chǎn)品的數字標識符、正文字符串等主機可用來(lái)對設備進(jìn)行識別的信息。甚至有一個(gè)序列號域,以便主機唯一地識別設備或對USB上相同設備的多個(gè)實(shí)例加以區分。
內核模塊必須在開(kāi)始USB通信前完成USB描述符的建立,這是因為枚舉過(guò)程由USB設備控制器驅動(dòng),一旦USB主機連上后會(huì )自動(dòng)執行。一切準備就緒后,USB設備模塊便調用sa1100_usb_start(),告訴內核接受來(lái)自主機的USB連接請求。如果模塊在USB主機連上前調用sa1100_set_configured_ callback(),那么內核將會(huì )在枚舉過(guò)程結束時(shí)調用所提供的回調函數?;卣{函數能很好地對設備完成連接狀態(tài)進(jìn)行可視化指示。
如果USB通信不再需要,那么設備的內核模塊便調用sa1100_usb_stop(),然后是 sa1100_usb_close(),關(guān)閉SA1100的USB控制器。
StrongARM USB控制器支持數據傳輸作業(yè)的bulk-in 和bulk-out。在從USB主機接收數據包時(shí),內核模塊調用sa1100_usb_recv(),把數據緩沖區和回調函數地址傳遞給它。然后內核的底層USB設備控制代碼對來(lái)自主機的bulk-out包進(jìn)行檢索,把內容放于緩沖區中,并調用回調函數。
回調函數必須從接收緩沖區提取數據并保存于其它位置或者把緩沖區空間加到一個(gè)隊列中,為下一個(gè)數據包的接收分配新的緩沖區。而后回調函數二次調用sa1100_usb_recv(),在需要時(shí)進(jìn)行下一個(gè)數據包的接收。過(guò)程與對USB主機的數據傳輸相類(lèi)似。在聚集起一幀的數據量后,內核模塊將數據的地址、長(cháng)度和回調地址傳遞給sa1100_usb_send()。傳輸完成時(shí),內核調用回調函數。
主機
主機端USB驅動(dòng)的幾個(gè)例子在主流的Linux版本以及Linux內核檔案組織分配的原始內核源中都有提供。用于Handspring Visor(drivers/usb/serial/visor.c)的模塊是編寫(xiě)較為簡(jiǎn)潔易懂的模塊之一,作為USB主機端模塊的模板(drivers/usb/usb-skeleton.c)使用。
高速串行
對于大多數實(shí)際應用來(lái)說(shuō), 可以把USB總線(xiàn)當作一種高速串行端口考慮。如此在某些類(lèi)型的嵌入式設備和應用中對它進(jìn)行原型模擬是有意義的。StrongARM處理器的Linux內核提供現成的USB設備驅動(dòng)專(zhuān)工于此,稱(chēng)作usb-char。
在希望與USB主機通信時(shí),Linux USB設備應用程序只是打開(kāi)對其usb-char設備節點(diǎn)(字符型,最大10,最小240)的連接,然后開(kāi)始讀寫(xiě)數據即可。read()和 write()操作將一直返回錯誤值直到USB主機連上為止。一旦連接建立和枚舉完成,便開(kāi)始通信,就像USB是一種點(diǎn)對點(diǎn)串行端口一樣。
由于這種USB數據傳遞方法十分直接且實(shí)用,因此usb-char設備得到高效使用。它還為其它USB通信方法的實(shí)現提供了重要的參照基準。
usb-char的實(shí)際動(dòng)作從usbc_open()功能開(kāi)始,部分內容示于列表1中。
列表1:打開(kāi)USB上的串行連接
static int usbc_open(struct inode *pInode, struct file *pFile)
{
int retval = 0;
/* start usb core */
sa1100_usb_open(_sb-char?;
/* allocate memory for in-transit USB packets */
tx_buf = (char*) kmalloc(TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA);
packet_buffer = (char*) kmalloc(RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA);
/* allocate memory for the receive buffer; the contents of this
buffer are provided during read() */
rx_ring.buf = (char*) kmalloc(RBUF_SIZE, GFP_KERNEL);
/* set up USB descriptors */
twiddle_descriptors();
/* enable USB i/o */
sa1100_usb_start();
/* set up to receive a packet */
kick_start_rx();
return 0;
twiddle_descriptors()功能建立起設備的USB描述符。在描述符全部建起后,準備從USB主機枚舉并接收一個(gè)數據幀。kick_start_rx()所需的代碼大多數情況下只是一種對sa1100_usb_recv() 的調用以建立回調而已。當USB主機發(fā)送數據包時(shí),設備的內核通過(guò)回調調用rx_done_callback_packet_buffer()函數,把數據包的內容移入usb-char 設備點(diǎn)上由read()返回的FIFO隊列。
主機
對于運行Linux的USB主機,usb-char相應的USB主機模塊稱(chēng)為usbserial模塊。大多數Linux版本都包括Usbserial模塊,盡管通常不是自動(dòng)裝入。在USB與設備的連接建立之前,usbserial 由modprobe 或 insmod載入。
一旦USB設備開(kāi)始枚舉,主機上的應用程序便用usbserial設備點(diǎn)(字符型,最大188,最小0以上)之一與設備進(jìn)行通信。這些節點(diǎn)通常命名為/dev/ttyUSBn。Usbserial模塊在內核報文日志記錄中報告它把哪個(gè)節點(diǎn)指定給USB設備使用:
usbserial.c: 通用轉換器刪除
usbserial.c: 通用轉換器當前連到ttyUSB0上。連接建立后,USB主機上的應用程序便通過(guò)讀寫(xiě)指定的節點(diǎn)與USB設備進(jìn)行通信。
Linux主機上usbserial模塊的一種替代選擇是一種稱(chēng)作libusb(libusb.sourceforge.net)的庫。這種庫使用低層內核系統調用進(jìn)行USB數據傳輸,而不是通過(guò)usbserial模塊,在某種程度上跨Linux內核版本建立和使用時(shí)更方便。Libusb庫還提供大量有用的調試功能,這一點(diǎn)在對運行在USB鏈路上的復雜通信協(xié)議進(jìn)行除錯時(shí)有幫助。用libusb與采用usb-char的USB設備進(jìn)行通信時(shí),Linux主機應用程序使用usb_open()函數建立與該設備的連接。然后應用程序使用usb_bulk_read()和usb_bulk_write()與設備交換數據。
USB上的以太網(wǎng)
另一種選擇是把USB作為一種以太網(wǎng)絡(luò )來(lái)對待。Linux具有在主機和設備端均可實(shí)現這種功能的模塊。由于iPAQ硬件既沒(méi)有可接入的串行端口也沒(méi)有一種專(zhuān)用的網(wǎng)絡(luò )接口,因此,iPAQ 的Linux內核專(zhuān)門(mén)采用這種通信策略,在StrongARM的Linux內核中,usb-eth模塊(arch/arm/mach-sa1100/usb-eth.c)對用USB作物理媒介的虛構以太網(wǎng)設備進(jìn)行仿真。一旦創(chuàng )建后,這一網(wǎng)絡(luò )界面便被指定一個(gè)IP地址,否則作為通常的以太網(wǎng)硬件對待。一旦USB主機連上后,usb-eth模塊便能使USB設備“看到” Internet(如果存在Internet的話(huà)),ping測其它IP地址,甚至“談?wù)摗盌HCP, HTTP, NFS, telnet, 和e-mail。簡(jiǎn)言之,任何在實(shí)際的以太網(wǎng)界面上運行的應用將不折不扣地在usb-eth接口上得到實(shí)現,因為它們不能分辨出其正在使用的不是實(shí)在的以太網(wǎng)硬件。
主機
在Linux主機上,相應的Ethernet-over-USB內核模塊稱(chēng)為usbnet。當usbnet模塊得到安裝且設備的USB連接建立完成時(shí),usbnet模塊便針對主機端內核及用戶(hù)應用創(chuàng )建一個(gè)與實(shí)際硬件酷似的虛構以太網(wǎng)界面,主機端應用程序通過(guò)運行設備IP地址ping測,可以檢查USB設備的存在。如果ping測成功,設備便加上了。
結語(yǔ)
Linux不再只是USB主機使用,當今它也是USB設備的合適選擇,Linux 下的USB通信是非常靈活和易用的。(鋤禾譯)
linux操作系統文章專(zhuān)題:linux操作系統詳解(linux不再難懂)
評論