linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動

在上一章我們已經說明了uart驅動的開發流程,本章我們就不再介紹uart相關的接口實現,僅通過實現一個虛擬的串口控制器程序,用以說明虛擬串口的開發流程。

本次開發的虛擬串口提供的功能如下:

  1. 提供兩個串口實例
  2. 串口名稱的前綴為vttyU
  3. 為了驗證串口收發,提供了loopback機制,即應用程序向虛擬串口寫入數據後,數據再回環至應用程序;
  4. 在/sys目錄下提供數據寫入屬性文件,可向虛擬串口中寫入數據,用以模擬串口接收數據的功能

本次開發的代碼涉及的模塊包括:

  1. 創建兩個platform device,分別對應兩個虛擬串口的platform device;
  2. 創建一個platform driver,在platform driver的probe接口中,完成虛擬串口的註冊,主要是完成uart_add_one_port接口完成虛擬串口的註冊;在platform driver的remove接口中,完成虛擬串口的註銷;
  3. 在串口驅動的初始化函數中,調用uart_register_driver,uart driver的註冊。


數據結構說明


在虛擬串口驅動中,定義了數據結構virtual_uart_port,該數據結構中包含了uart_port。並定義了tx_enable_flag、rx_enable_flag,分別用於控制串口收發,因是虛擬串口所以使用這兩個變量進行表示,若是真是的串口控制器,則不需要這兩個變量,而只需要在uart_ops->startup、uart_ops->stop_rx、uart_ops->stop_tx中關閉中斷即可,這兩個變量由自旋鎖write_lock進行保護。

linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動


uart_driver定義及註冊

定義virtual_uart_driver,本串口控制器驅動支持的串口個數為MAX_VIRTUAL_UART(6個)、而dev_name則為該虛擬串口對應字符設備文件名稱的前綴,本次定義前綴為“vttyU”,本串口不支持控制檯。調用uart_register_driver即完成本串口控制器驅動的註冊與註銷

linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動


Platform device定義及註冊


本次我們主要完成了串口的註冊,因此我們定義並註冊了兩個platform device,而傳遞的參數為串口的index的。接口定義如下所示,platform device的name為“virtual_uart_port_dev”,根據該名稱可完成與platform driver的匹配及探測功能(因我們在ubuntu16.04下完成的驗證,沒有設備樹概念,因此就定義了這兩個platform device,若支持設備樹,則無須我們手動定義這兩個platform device,只需要修改設備樹文件即可)。

linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動


Platform driver的定義及註冊

我們的platform driver的定義如下,支持probe、remove接口,probe接口主要完成uart port的註冊、remove接口主要完成uart port註銷,該platform driver的name為“virtual_uart_port_dev”,通過該名稱可進行platform device與platform driver的匹配檢測,同時我們也定義了of_device_id,若內核支持設備樹,則在設備樹中的compatible中也設置“jerry_chg,virtual-uart”,即可完成platform device與platform driver的匹配檢測。

linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動


virtual_uart_port_platform_probe接口的定義如下,主要實現的功能如下:


  1. 為uart_port申請內存,並設置uart_port的ops、fifosize、type、line等值,另外還可為該uart_port創建uart_port相關的私有屬性文件(sysfs下),而在linux3.10的內核中uart_port中並沒有定義attr_group變量,導致uart_add_one_port接口只能在tty_port對應device中創建uart核心定義的屬性文件,而不能創建uart port私有的屬性文件,而在後面的內核中特意增加了attr_group,用於創建uart port私有的屬性文件,這個成員變量增加的挺好,本次虛擬串口就藉助該變量創建了屬性文件uart_receive_buff,用於模擬串口接收數據。
  2. 初始化一個工作隊列及其回調函數virtual_uart_flush_to_port,該接口主要是模擬串口發送中斷的功能,在真實的串口控制器中,則是申請串口中斷,在串口中斷的處理函數中進行數據的發送,而我們這個工作隊列則是模擬串口中斷函數的功能;
  3. 調用uart_add_one_port,完成串口的添加。


linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動


Uart port操作接口定義

我們為虛擬串口定義的操作接口如下

  1. 其中tx_empty接口用於測試發送緩存是否為空(uart_port的環形緩衝區);
  2. stop_tx用於停止發送操作(在真實串口控制器驅動中,則關閉發送中斷即可);
  3. start_tx用於啟動發送操作(在真實串口控制器驅動中,則開啟發送中斷,然後則觸發發送中斷,繼而在發送中斷處理接口中執行數據的發送操作,而在我們的虛擬串口中,則調用schedule_work,啟動工作隊列,調用工作隊列的回調函數進行數據發送);
  4. throttle、unthrottle則是流控操作,在真實串口中則設置相應定時器即可(本驅動未實現該功能);
  5. stop_rx用於停止接收操作(在真實串口控制器驅動中,則關閉接收中斷即可)
  6. startup接口主要是啟動串口功能(在真實串口控制器驅動中,則申請中斷,並使能接收中斷;而發送中斷的使能在start_tx中實現,而在我們虛擬串口驅動中,則是使能收發數據的flag);
  7. set_termios則主要是設置termios相關參數(包括字節寬度、波特率等參數的設置);
linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動

而set_mctrl接口則一定要定義,即使是一個空函數也要定義。


模擬串口接收功能實現

因為我們是虛擬串口,但是又要模擬串口接收功能,因此我們在tty port對應的device中,定義了模擬串口接收數據的屬性文件,定義如下:

當用戶向該屬性文件(uart_receive_buff)中寫數據時,則在該屬性文件的store接口中,將寫入的數據發送到tty_port的接收緩存中,並通過調用tty_flip_buffer_push接口,將tty_port接收緩存中的數據通過線路規程的receive_buff接口將數據刷新到tty_struct的接收緩存中,並wakeup讀等待隊列中sleep的讀線程(這一系統的操作流程可參考我之前寫的幾篇文檔),即將數據發送的該串口上的讀線程中。

linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動

我們可以通過如下腳本向uart_receive_buff寫數據,從而模擬串口接收數據

linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動


測試驗證

  1. 在應用程序中打開/dev/vttyU1,進行數據接收;
  2. 向/sys/class/tty/vttyU1/uart_receive_buff寫數據,則上述1中的進程即會接收到數據,測試截圖如下
linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動


linux虛擬串口控制器驅動實現——適用於無開發板學習串口驅動


至此我們完成了虛擬串口驅動代碼的實現以及驗證工作,我們也完成了tty子系統、uart子系統架構內部實現流程的分析,也完成了對應虛擬控制器驅動的實現及驗證,下一次我們開始進行input子系統的分析(關於本驅動的源碼,後續我們會把鏈接發出來)。


分享到:


相關文章: