摘" 要: 針對(duì)Linux下通過USB Gadget框架將USB接口模擬成其他USB設(shè)備時(shí)存在設(shè)備配置信息修改不方便的問題,可以使用Configfs動(dòng)態(tài)配置USB設(shè)備。文中介紹了USB Gadget的框架與原理,重點(diǎn)分析了Configfs的主要數(shù)據(jù)結(jié)構(gòu)和內(nèi)部實(shí)現(xiàn)機(jī)制,給出了使用Configfs在用戶模式配置USB設(shè)備的具體方法,并在I.MX6ULL平臺(tái)上將Gadget功能驅(qū)動(dòng)層已有的串口功能驅(qū)動(dòng)程序使用Configfs進(jìn)行配置后,實(shí)現(xiàn)了將USB接口虛擬成USB串口設(shè)備。經(jīng)過測(cè)試,虛擬串口設(shè)備傳輸正常,為開發(fā)人員在USB Gadget下使用Configfs配置USB設(shè)備提供了參考。
關(guān)鍵詞: 虛擬串口; USB Gadget; Configfs; Linux; 串口設(shè)備; I.MX6ULL
中圖分類號(hào): TN919?34; TP368.1; TP316.81" " " " " 文獻(xiàn)標(biāo)識(shí)碼: A" " " " " " " "文章編號(hào): 1004?373X(2025)02?0046?05
Research on Gadget Configfs and its application in USB virtual serial port
ZHANG Yi1, BAO Aida1, GUO Xin2, LIU Xinyi1
(1. College of Instrumentation and Electronics, North University of China, Taiyuan 030051, China;
2. School of Information and Communication Engineering, North University of China, Taiyuan 030051, China)
Abstract: In allusion to the problem of inconvenient modification of device configuration information when simulating USB interfaces to other USB devices by means of the USB Gadget framework under Linux, Configfs can be used to configure USB devices dynamically. The framework and principle of USB Gadget is introduced, and the main data structure and internal mechanism of Configfs is analyzed stressly. A specific method for configuring USB devices in user mode using Configfs is provided. After configuring the existing serial port function driver program of the Gadget function driver layer on the I.MX6ULL platform using Configfs, the USB interface was virtualized into a USB serial port device. The testing results show that the virtual serial device transmission is normal, providing a reference for developers to configure USB devices using Configfs under USB Gadget.
Keywords: virtual serial port; USB Gadget; Configfs; Linux; serial port device; I.MX6ULL
0" 引" 言
隨著嵌入式系統(tǒng)的發(fā)展,在Linux下通過通用的Gadget驅(qū)動(dòng)框架將USB接口模擬成其他類型設(shè)備,從而滿足開發(fā)人員在不需要實(shí)際硬件設(shè)備的情況下進(jìn)行軟件開發(fā)和測(cè)試需求的方法也日益增多[1?6]。而以往通過Gadget驅(qū)動(dòng)框架實(shí)現(xiàn)USB設(shè)備端驅(qū)動(dòng)時(shí),需要在gadget\legacy功能驅(qū)動(dòng)層編寫驅(qū)動(dòng)代碼配置設(shè)備信息,存在配置更改不方便的問題。利用內(nèi)核提供的Configfs允許用戶直接通過文件系統(tǒng)的操作,在運(yùn)行時(shí)根據(jù)需要?jiǎng)討B(tài)配置USB Gadget的參數(shù)和屬性,而無需重新編譯內(nèi)核或啟動(dòng)系統(tǒng),配置設(shè)備信息更加靈活便捷,這對(duì)于需要適應(yīng)不同應(yīng)用場(chǎng)景和需求的嵌入式系統(tǒng)特別有用。因此本文研究了如何通過Configfs配置USB Gadget虛擬的串口設(shè)備。介紹了USB Gadget框架與原理;重點(diǎn)分析了Configfs的數(shù)據(jù)結(jié)構(gòu)和內(nèi)部實(shí)現(xiàn)機(jī)制,并給出了通過Configfs配置Gadget以實(shí)現(xiàn)USB接口虛擬成USB串口設(shè)備的具體操作方法;最后編寫串口測(cè)試程序,在應(yīng)用程序中設(shè)置好串口參數(shù)進(jìn)行測(cè)試。結(jié)果表明,該方法傳輸正常,為開發(fā)人員在USB Gadget下使用Configfs配置USB設(shè)備提供了參考。
1" USB Gadget框架結(jié)構(gòu)與原理
USB Gadget驅(qū)動(dòng)框架主要分為三層:UDC驅(qū)動(dòng)層、Gadget中間層、Gadget功能驅(qū)動(dòng)層[7?8],USB Gadget框架圖如圖1所示。UDC驅(qū)動(dòng)作為USB設(shè)備控制器的硬件驅(qū)動(dòng),負(fù)責(zé)USB設(shè)備控制器與主機(jī)側(cè)USB控制器之間的數(shù)據(jù)通信,同時(shí)向Gadget中間層提供了與UDC硬件相關(guān)的操作函數(shù)接口[9],包括底層寄存器配置、中斷處理等。Gadget中間層是對(duì)UDC驅(qū)動(dòng)層函數(shù)的封裝,向上提供一套與具體UDC硬件無關(guān)的統(tǒng)一接口,能夠?qū)⒌讓佑布ㄐ排c上層功能實(shí)現(xiàn)分離,以便不同的功能能夠以統(tǒng)一的方式與不同的UDC驅(qū)動(dòng)進(jìn)行交互,避免了每個(gè)Gadget功能驅(qū)動(dòng)都得單獨(dú)實(shí)現(xiàn)USB設(shè)備驅(qū)動(dòng)的情況,提高了開發(fā)效率[10]。Gadget功能層通過調(diào)用Gadget中間層提供的統(tǒng)一接口來控制USB設(shè)備功能的實(shí)現(xiàn),如實(shí)現(xiàn)大容量存儲(chǔ)設(shè)備、人機(jī)接口設(shè)備、USB虛擬成串口等。
UDC驅(qū)動(dòng)層中使用usb_gadget結(jié)構(gòu)體描述UDC,結(jié)構(gòu)體內(nèi)通過usb_gadget_ops操作函數(shù)集完成UDC電源管理、設(shè)備自供電特征相關(guān)設(shè)置或清理、喚醒與設(shè)備相連接的主機(jī)等功能;使用usb_ep結(jié)構(gòu)體描述端點(diǎn),結(jié)構(gòu)體內(nèi)通過usb_ep_ops端點(diǎn)操作函數(shù)集完成配置端點(diǎn)、將請(qǐng)求插入端點(diǎn)隊(duì)列等功能。Gadget中間層中usb_gadget_driver結(jié)構(gòu)體內(nèi)定義了bind綁定、connect連接等函數(shù)指針,主要通過調(diào)用usb_gadget_probe_driver(structusb_gadget_driver *driver)函數(shù)找到對(duì)應(yīng)的UDC,然后在udc_bind_to_driver函數(shù)中通過usb_udc結(jié)構(gòu)體中指向usb_gadget結(jié)構(gòu)體和usb_gadget_driver結(jié)構(gòu)體的指針,將UDC驅(qū)動(dòng)層與Gadget中間層聯(lián)系起來[11]。Gadget中間層中usb_composite_driver結(jié)構(gòu)體內(nèi)定義了bind綁定、connect連接等函數(shù)指針,主要通過調(diào)用usb_composite_probe(structusb_composite_driver*driver)函數(shù)為usb_composite_driver結(jié)構(gòu)體中成員usb_gadget_driver設(shè)置統(tǒng)一的模板composite_driver_template,然后調(diào)用composite_bind函數(shù)完成與Gadget功能驅(qū)動(dòng)層的交互;使用usb_composite_dev結(jié)構(gòu)體表示一個(gè)USB設(shè)備,結(jié)構(gòu)體中含有USB設(shè)備的設(shè)備描述符和配置描述符,一般通過usb_composite_driver結(jié)構(gòu)體對(duì)其進(jìn)行初始化和配置,結(jié)構(gòu)體內(nèi)通過指向usb_gadget結(jié)構(gòu)體和usb_composite_driver的指針,將UDC驅(qū)動(dòng)層與Gadget功能驅(qū)動(dòng)層聯(lián)系起來。
Gadget功能層,在gadget\legacy功能層中調(diào)用usb_get_function_instance函數(shù)獲得function實(shí)例,調(diào)用usb_get_function函數(shù)獲得function,再通過usb_add_config_only函數(shù)給設(shè)備添加配置,最后調(diào)用usb_add_function函數(shù)將function放入配置鏈表,根據(jù)配置鏈表所含function決定設(shè)備具備哪些功能。在gadget\function功能層中實(shí)現(xiàn)所模擬的function功能,function的功能實(shí)現(xiàn)過程主要是完成數(shù)據(jù)傳輸,數(shù)據(jù)傳輸?shù)暮诵膶?duì)象是端點(diǎn)。主要實(shí)現(xiàn)流程如下:通過端點(diǎn)描述符usb_endpoint_descriptor表明端點(diǎn)類型與方向,然后調(diào)用功能驅(qū)動(dòng)里bind函數(shù),根據(jù)endpoint描述符調(diào)用usb_ep_autoconfig函數(shù)向底層申請(qǐng)分配endpoint,調(diào)用enable_endpoint函數(shù)使能endpoint;然后調(diào)用usb_ep_alloc_reques函數(shù),給endpoint分配USB請(qǐng)求,調(diào)用lb_alloc_ep_req函數(shù)給USB請(qǐng)求分配buffer,再為USB請(qǐng)求設(shè)置好回調(diào)函數(shù);最后調(diào)用usb_ep_queue函數(shù)將USB請(qǐng)求放入隊(duì)列,UDC與Host完成USB傳輸后就會(huì)觸發(fā)中斷,調(diào)用USB請(qǐng)求中的回調(diào)函數(shù)。
2" Configfs結(jié)構(gòu)分析與程序設(shè)計(jì)
Configfs是Linux內(nèi)核中一個(gè)層次分明的虛擬文件系統(tǒng),主要由幾個(gè)相關(guān)聯(lián)的結(jié)構(gòu)體構(gòu)成,其核心概念是對(duì)象和屬性,對(duì)象表示內(nèi)核中的實(shí)體,而屬性則是這些實(shí)體的配置參數(shù),用戶可以通過在文件系統(tǒng)中創(chuàng)建對(duì)象和屬性的方式,動(dòng)態(tài)地創(chuàng)建和配置內(nèi)核參數(shù)、設(shè)備和子系統(tǒng)。從頂向下分析其主要結(jié)構(gòu)體間的層級(jí)關(guān)系,有助于理解如何使用Configfs來配置虛擬設(shè)備的參數(shù),Configfs主要數(shù)據(jù)結(jié)構(gòu)間關(guān)系圖如圖2所示。config_group結(jié)構(gòu)體描述一個(gè)可創(chuàng)建子目錄的目錄,結(jié)構(gòu)體內(nèi)主要定義了config_item結(jié)構(gòu)體;config_item結(jié)構(gòu)體描述一個(gè)不含有子目錄的目錄,結(jié)構(gòu)體內(nèi)主要定義了可用于管理配置項(xiàng)的結(jié)構(gòu)體config_item_type以及目錄名;config_item_type結(jié)構(gòu)體內(nèi)主要定義了鏈接文件操作結(jié)構(gòu)體configfs_item_operations、目錄下文件結(jié)構(gòu)體configfs_attribute以及目錄下操作結(jié)構(gòu)體configfs_group_operations;configfs_item_operations結(jié)構(gòu)體主要定義了allow_link/drop_link操作鏈接文件;configfs_attribute結(jié)構(gòu)體主要定義了show/strote操作文件讀寫。configfs_group_operations結(jié)構(gòu)體主要定義了make_item創(chuàng)建config_item結(jié)構(gòu)體,make_group創(chuàng)建config_group結(jié)構(gòu)體。
使用Configfs文件系統(tǒng)配置和控制acm串口功能,需要在gadget\function\f_acm.c中提供與Configfs項(xiàng)相關(guān)的操作,可通過config_group_init_type_name函數(shù)添加config_item_type結(jié)構(gòu)體,使配置組關(guān)聯(lián)到acm功能上,從而管理與acm功能相關(guān)的配置項(xiàng)。構(gòu)造to_f_serial_opts函數(shù),將acm功能的數(shù)據(jù)結(jié)構(gòu)與Configfs項(xiàng)的私有數(shù)據(jù)關(guān)聯(lián),將給定的config_item轉(zhuǎn)換為相應(yīng)的f_serial_opts結(jié)構(gòu)體指針;構(gòu)造acm_attr_release函數(shù)釋放與acm功能配置項(xiàng)相關(guān)的資源,包括釋放USB功能實(shí)例相關(guān)的資源;構(gòu)造f_acm_port_num_show函數(shù)讀取acm功能配置項(xiàng)的端口號(hào)屬性,并將其格式化到給定的緩沖區(qū)中,以便用戶空間程序能夠獲取和顯示這一屬性的值。
在drivers\usb\gadget\configfs.c中調(diào)用configfs_register_subsystem,就會(huì)創(chuàng)建subsystem子系統(tǒng),它對(duì)應(yīng)Configfs文件系統(tǒng)中的頂層目錄usb_gadget,其本質(zhì)是特殊的config_item。本文使用Configfs配置Gadget框架中的虛擬串口驅(qū)動(dòng),主要通過設(shè)計(jì)腳本命令程序在usb_gadget目錄下完成配置,可替代原來gadget\legacy功能驅(qū)動(dòng)層中serial.c串口程序完成設(shè)置串口設(shè)備描述符信息、配置描述符信息,以及指定使用gadget\function功能驅(qū)動(dòng)層中哪個(gè)功能并添加到配置鏈表中等配置工作。腳本程序中關(guān)鍵命令執(zhí)行目的及在Configfs內(nèi)部中相關(guān)調(diào)用工作如下。
在usb_gadget目錄下執(zhí)行mkdirtest_serial命令,會(huì)創(chuàng)建名為test_serial的目錄,最終會(huì)調(diào)用到頂層目錄usb_gadget的目錄操作結(jié)構(gòu)體configfs_group_operation中make_group所指向的gadgets_make函數(shù),函數(shù)中通過configfs_add_default_group函數(shù)創(chuàng)建配置、功能等目錄,通過config_group_init_type_name函數(shù)生成一系列與設(shè)備描述符相關(guān)文件,并給創(chuàng)建的配置、功能等目錄添加config_item_type結(jié)構(gòu)體。test_serial目錄如圖3所示。
在test_serial目錄下執(zhí)行echo ${VENDOR_ID}gt;idVendor等命令,會(huì)調(diào)用到configfs_attribute結(jié)構(gòu)體中函數(shù)指針show和store讀寫設(shè)備描述符文件,完成設(shè)備描述符的配置工作;在test_serial/configs目錄下執(zhí)行mkdir c.1命令會(huì)創(chuàng)建名為c.1的目錄,最終會(huì)調(diào)用到configs目錄的目錄操作結(jié)構(gòu)體configfs_group_operation中make_group所指向的config_desc_make函數(shù),其通過執(zhí)行usb_add_config_only函數(shù)給設(shè)備添加配置;在test_serial/functions目錄下執(zhí)行mkdir acm.t_est1命令會(huì)創(chuàng)建名為acm.t_est1的文件,最終會(huì)調(diào)用到function目錄的目錄操作結(jié)構(gòu)體中make_group所指向的function_make函數(shù),函數(shù)中執(zhí)行usb_get_function_instance(func_name)會(huì)根據(jù)功能名acm找到對(duì)應(yīng)gadget功能層gadget\function\f_acm.c的虛擬串口功能驅(qū)動(dòng),并在test_serial目錄下安裝對(duì)應(yīng)的usb_f_acm.ko驅(qū)動(dòng);在test_serial目錄下執(zhí)行l(wèi)n ?s functions/acm.t_est1 configs/c.1/命令會(huì)創(chuàng)建符號(hào)鏈接,最終會(huì)調(diào)用到鏈接文件操作結(jié)構(gòu)體configfs_item_operations中allow_link所指向的config_usb_cfg_link函數(shù),其通過執(zhí)行usb_get_function函數(shù)給c.1配置添加acm功能,并通過list_add_tail(amp;f?gt;list, amp;cfg?gt;func_list)將功能放入配置功能鏈表中;在test_serial目錄下執(zhí)行echo ci_hdrc.0 gt; UDC命令會(huì)使能名為ci_hdrc.0的UDC,實(shí)質(zhì)是根據(jù)UDC名最終調(diào)用usb_gadget_probe_driver函數(shù)使能對(duì)應(yīng)UDC。
3" 串口測(cè)試程序設(shè)計(jì)
在PC?Linux端編寫串口測(cè)試程序,首先打開串口,設(shè)置串口為阻塞模式;再通過函數(shù)set_opt(fd, 115 200, 8, 'N', 1)規(guī)定好串口的波特率、數(shù)據(jù)位、停止位等通信參數(shù)[12?14],在函數(shù)中使用tcgetattr()、tcsetattr()等函數(shù)獲取與設(shè)置termios結(jié)構(gòu)體中與串口通信相關(guān)的行規(guī)程配置參數(shù)。termios結(jié)構(gòu)體主要成員如下:
structtermios{
tcflag_tc_iflag; " " " " " " " " " " " " " " " "/*輸入模式標(biāo)志*/
tcflag_tc_oflag; " " " " " " " " " " " " " " " "/*輸出模式標(biāo)志*/
tcflag_tc_cflag; " " " " " " " " " " " " " " " "/*控制模式標(biāo)志*/
tcflag_tc_lflag; " " " " " " " " " " " " " " " "/*局部模式標(biāo)志*/
cc_tc_line; " " " " " " " " " " " " " " " " " " " " " " " "/*行控制*/
cc_tc_cc[NCCS];" " " " " " " " " " " " " " " " "/*控制字符特性*/
};
之后創(chuàng)建讀寫線程,調(diào)用read/write函數(shù)實(shí)現(xiàn)串口的收發(fā)數(shù)據(jù),完成操作后執(zhí)行tcsetattr(0,tcsnow,amp;old),恢復(fù)終端的原始設(shè)置。
4" 測(cè)試驗(yàn)證
使用USB線連接I.MX6ULL的OTG口、PC的USB口,在usb_gadget目錄下執(zhí)行所編寫腳本程序,執(zhí)行完echo ci_hdrc.0 gt; UDC命令后,PC機(jī)會(huì)提示選擇安裝USB設(shè)備端模擬的串口設(shè)備的位置,若選擇Windows主機(jī)端安裝,在設(shè)備管理器會(huì)出現(xiàn)新的串口設(shè)備圖標(biāo),如圖4所示。本次測(cè)試選擇安裝在VMware的Linux系統(tǒng)上,開發(fā)板上出現(xiàn)設(shè)備節(jié)點(diǎn)/dev/ttyGS0,Ubuntu上出現(xiàn)設(shè)備節(jié)點(diǎn)/dev/ttyACM0,串口信息及設(shè)備節(jié)點(diǎn)如圖5所示。
將PC?Linux端編寫好的串口測(cè)試程序,在以下PC?Linux端分別執(zhí)行:
gcc ?o serial_send_recv_pcserial_test.c ?lpthreadarm?buildroot?linux?gnueabihf?gcc ?o serial_send_recvserial_test.c ?lpthread
將測(cè)試程序編譯為PC?Linux和ARM兩個(gè)版本,分別在Ubuntu和開發(fā)板上執(zhí)行,在Ubuntu上發(fā)送一個(gè)字符串“This is Ubuntu send”加上4個(gè)退格鍵,開發(fā)板能夠正常接收并顯示字符串“This is Ubuntu”,如圖6所示。在開發(fā)板上發(fā)送一個(gè)字符串“This is usb gadget se”加上2個(gè)退格鍵與“aa”字符,Ubuntu同樣能夠正常接收并顯示字符串“This is usb gadget aa”,如圖7所示。這兩張圖說明經(jīng)過Configfs配置后,USB設(shè)備虛擬成的串口傳輸正常。
5" 結(jié)" 語(yǔ)
本文采用Configfs來配置USB Gadget所模擬的串口設(shè)備。介紹了USB Gadget框架與原理;重點(diǎn)分析了Configfs的數(shù)據(jù)結(jié)構(gòu)和內(nèi)部實(shí)現(xiàn)機(jī)制,并給出了通過Configfs配置實(shí)現(xiàn)USB接口虛擬成USB串口設(shè)備的具體操作方法,結(jié)果表明傳輸正常。針對(duì)目前使用USB Gadget模擬其他類型設(shè)備的需求增加,而Configfs參考資料較少的情況,開發(fā)人員可以參考本文所使用的Configfs配置USB Gadget的方法降低開發(fā)難度,提高開發(fā)效率。
注:本文通訊作者為鮑愛達(dá)。
參考文獻(xiàn)
[1] 黃少濱.嵌入式系統(tǒng)USB Gadget驅(qū)動(dòng)研究與實(shí)現(xiàn)[D].廣州:華南理工大學(xué),2012.
[2] 焦新泉,袁小康,儲(chǔ)成群.基于ARM?Linux平臺(tái)的USB數(shù)據(jù)存儲(chǔ)設(shè)計(jì)與實(shí)現(xiàn)[J].現(xiàn)代電子技術(shù),2019,42(6):6?9.
[3] 李校林,孫文華,李銀.基于Linux平臺(tái)的OV2715驅(qū)動(dòng)研究與實(shí)現(xiàn)[J].現(xiàn)代電子技術(shù),2016,39(22):71?75.
[4] 付志鵬,馮丹,陳紅,等.基于國(guó)產(chǎn)服務(wù)器的國(guó)產(chǎn)Linux操作系統(tǒng)適配研究[J].計(jì)算機(jī)技術(shù)與發(fā)展,2023,33(4):89?95.
[5] 鄧佳偉,張梅娟,王琪,等.PowperPC架構(gòu)下USB 3.0的IP核軟硬件協(xié)作驗(yàn)證[J].電子技術(shù)應(yīng)用,2022,48(5):37?41.
[6] 姜悅.基于龍芯2K1000的嵌入式Linux系統(tǒng)移植和驅(qū)動(dòng)程序設(shè)計(jì)[D].哈爾濱:哈爾濱工業(yè)大學(xué),2021.
[7] 趙昕,郭恩全,林成文.Linux集成下USB Gadget的USB虛擬網(wǎng)口設(shè)計(jì)[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2021,21(7):2?6.
[8] 蒲澤坤,沈勇,陳旅超.USB接口的CDCamp;MSC復(fù)合設(shè)備設(shè)計(jì)與應(yīng)用[J].單片機(jī)與嵌入式系統(tǒng)應(yīng)用,2023,23(7):53?56.
[9] 陳剛,肖鐵軍,郭林.Linux Gadget框架的研究及在USB程控中的應(yīng)用[J].計(jì)算機(jī)測(cè)量與控制,2013,21(7):2011?2014.
[10] 秦建鑫.基于USB 3.0的平板顯示器系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)[D].上海:上海交通大學(xué),2017.
[11] 王振麗.Android底層開發(fā)技術(shù)實(shí)戰(zhàn)詳解[M].北京:電子工業(yè)出版社,2015.
[12] GAO J, XU J. The multi?serial port driving research of video monitoring system based on the Internet of things [J]. MATEC web of conferences, 2018, 173: 02014.
[13] 陳偉.基于申威831非POSIX標(biāo)準(zhǔn)串口波特率的研究與實(shí)現(xiàn)[J].網(wǎng)絡(luò)安全技術(shù)與應(yīng)用,2023(7):56?58.
[14] 宋躍,楊雷,雷瑞庭,等.基于ARM9與LINUX的RS 485總線的通信接口設(shè)計(jì)[J].儀表技術(shù)與傳感器,2014(5):35?37.