Linux單裝置多路USB串列埠的實現方法介紹

bigfish99發表於2021-05-23

某裝置需要提供多路USB串列埠的功能給主機端使用,比如一路用作業務1通訊功能,一路用作業務2通訊功能,一路用作debug抓log用途,諸如此類。如下圖所示。

Linux單裝置多路USB串列埠的實現方法介紹

 

要實現上述裝置功能,可以參考如下步驟。

 

1)首先,瞭解一下背景知識。Linux kernel為裝置端USB驅動提供了名為USB Gadget的驅動框架,裝置端要基於Linux系統實現USB device功能,都需要基於Gadget框架。各種USB class定義的功能,在裝置端的實現,稱之為USB function。常見的USB function,比如 serial, ecm, storage, video, audio等,kernel原生程式碼都已經實現了。產品開發的大部分工作是放在理解並使用這些程式碼,並除錯可能出現的bug;以及針對某些usb controller的特性,需要在function driver層面處理的時候,打一些補丁,當然這種補丁是很難合入kernel社群的,只能是在自家的產品上用用。

2)其次,瞭解一下gadget驅動程式碼目錄結構。如下圖所示。

gadget驅動包含三大部分:

  • function驅動 —— 實現各種usb class功能

  • udc驅動 —— 實現usb controller driver 

  • 輔助驅動 —— configfs.c實現使用者空間配置usb, composite.c實現複合裝置

 

Linux單裝置多路USB串列埠的實現方法介紹

 

進入function目錄,可以看到各種已經實現的function,接下來我們要用到的serial function也在其中。

 

Linux單裝置多路USB串列埠的實現方法介紹

 

3)瞭解具體如何開啟usb串列埠的功能。其實很簡單,要開啟usb serial function driver,在kernel config中開啟以下CONFIG即可:

CONFIG_USB_GADGET=y

CONFIG_USB_U_SERIAL=y

CONFIG_USB_F_SERIAL=y

 

開啟以上CONFIG後,只是開啟了usb driver支援serial的能力;要生成多路串列埠,還需要通過configfs動態配置相關功能,以下就是生成三路USB generic serial串列埠的示例:

mkdir -p /sys/kernel/config/usb_gadget/g1/functions/gser.gs0
chmod 755 /sys/kernel/config/usb_gadget/g1/functions/gser.gs0
mkdir -p /sys/kernel/config/usb_gadget/g1/functions/gser.gs1
chmod 755 /sys/kernel/config/usb_gadget/g1/functions/gser.gs1
mkdir -p /sys/kernel/config/usb_gadget/g1/functions/gser.gs2
chmod 755 /sys/kernel/config/usb_gadget/g1/functions/gser.gs2
​
​
ln -s /sys/kernel/config/usb_gadget/g1/functions/gser.gs2 /sys/kernel/config/usb_gadget/g1/configs/b.1/f1
ln -s /sys/kernel/config/usb_gadget/g1/functions/gser.gs0 /sys/kernel/config/usb_gadget/g1/configs/b.1/f2
ln -s /sys/kernel/config/usb_gadget/g1/functions/gser.gs1 /sys/kernel/config/usb_gadget/g1/configs/b.1/f3
​

  

configfs本身的介紹,可參考kernel文件:

Documentation\filesystems\configfs\configfs.txt

USB gadget configfs的使用介紹,可以參考kernel文件:

Documentation\ABI\testing\configfs-usb-gadget

Documentation\ABI\testing\configfs-usb-gadget-serial

 

三路USB串列埠啟用成功後,在裝置端會生成三個ttyGS裝置:

  • /dev/ttyGS0

  • /dev/ttyGS1

  • /dev/ttyGS2

 

4)主機端看到的情況

主機端識別USB串列埠和載入相關驅動的方法,可以參考我的另一篇文章

載入usbserial驅動後,為什麼adb不可用了

這裡主要講一講主機端生成了多個名為ttyUSBx(x=0~n)的裝置,我們如何確定它們與裝置端多路USB串列埠(ttyGSx)的對應關係?

Linux單裝置多路USB串列埠的實現方法介紹

 

方法之一,當然可以通過遍歷測試串列埠通訊的方式來找到對應關係。比如主機端用串列埠工具或者echo指令傳送資料,裝置端用串列埠工具或者cat指令接收資料,一個一個遍歷嘗試,能正常通訊的,就說明兩邊是對應的。

 

方法之二,通過主機端和裝置端的USB interface number (介面號)找到對應關係。以Ubuntu主機為例:

 

在Ubuntu端執行 ls -l /sys/class/tty/ttyUSB*,可以看到ttyUSBx和USB介面號的對應關係。比如ttyUSB1對應3-6:1.2,這個末尾的數字2就表示介面2(簡稱f2)。

user@PC1002:~$ ls -l /sys/class/tty/ttyUSB*
lrwxrwxrwx 1 root root 0 5月 21 13:15 /sys/class/tty/ttyUSB0 -> ../../devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6:1.1/ttyUSB0/tty/ttyUSB0
lrwxrwxrwx 1 root root 0 5月 21 13:15 /sys/class/tty/ttyUSB1 -> ../../devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6:1.2/ttyUSB1/tty/ttyUSB1
lrwxrwxrwx 1 root root 0 5月 21 13:15 /sys/class/tty/ttyUSB2 -> ../../devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6:1.3/ttyUSB2/tty/ttyUSB2

 

類似的,在裝置端也可以獲得ttyGSx與介面號的對應關係。進入裝置端shell,執行如下指令。

ls -l /sys/kernel/config/usb_gadget/g1/configs/b.1/
-rw-r--r--    1 root     root          4096 May  4 10:40 MaxPower
-rw-r--r--    1 root     root          4096 May  4 10:40 bmAttributes
lrwxrwxrwx    1 root     root             0 May  4 10:40 f1 -> ../../../../usb_gadget/g1/functions/gser.gs2
lrwxrwxrwx    1 root     root             0 May  4 10:40 f2 -> ../../../../usb_gadget/g1/functions/gser.gs0
lrwxrwxrwx    1 root     root             0 May  4 10:40 f3 -> ../../../../usb_gadget/g1/functions/gser.gs1

  

可以看到f2對應gser.gs0,表示gser.gs0對應介面2。那麼gser.gs0是不是一定就與ttyGS0對應呢?先說答案,不一定。準確的做法是讀取gser.gs0目錄下的port_num的值來獲知是ttyGS幾。比如port_num是0,那就說明gser.gs0對應/dev/ttyGS0,如果port_num是1,那就說明gser.gs0對應/dev/ttyGS1。

cat /sys/kernel/config/usb_gadget/g1/functions/gser.gs0/port_num
0

  

這裡假定gser.gs0對應/dev/ttyGS0,那麼到此就可以得知主機和裝置的對應關係:ttyUSB1對應ttyGS0。如果裝置端的業務2程式碼是通過讀寫/dev/ttyGS0來實現通訊,那麼主機端的業務2程式碼就需要通過讀寫/dev/ttyUSB1來實現和裝置端業務2的互動。


至於port_num編號的背後規律,與每個gser.gsN這個末尾數字N無關;與我們前面編寫configfs配置USB的指令時,mkdir指令執行的次序有關,port_num從0開始,依次+1。從我們前面的編寫的指令看,先mkdir gser.gs0,再mkdir gser.gs1,那麼gser.gs0的port_num是0,gser.gs1的port_num就是1。如果先mkdir gser.gs1,再mkdir gser.gs0,那麼就會反過來,gser.gs1的port_num是0,gser.gs0的port_num是1。

 

5)USB gadget serial function的驅動實現細節,不是本文的重點,暫且不講,後續會專門介紹USB gadget function driver。

 

以上是本文全部內容,謝謝閱讀,希望能幫到你。

 

文章會在公眾號“大魚嵌入式”同步釋出,歡迎關注,一起交流。

相關文章