linux tty驅動架構分析

2021-09-30 16:19:43 字數 3501 閱讀 7484

**:

前一陣子移植乙個串列埠驅動,發現linux的驅動構架中,物件導向的思想已經根深蒂固。就比如這串列埠驅動,**中經常有一些貌似和串列埠無關的**,比如,tty_register_driver等。但我們卻刪它不得。因為正是這些**實現了tty core和具體的tty driver(比如串列埠驅動)的聯絡和紐帶。以前看ldd3,裡邊有術語tty core和tty driver,當是不清楚各指的是什麼,但是後來看了**,才知道,tty core指的是所有tty型別的驅動的頂層架構,它的**由核心實現,我們無需修改,**主要分布在drivers/char下的n_tty.c,tty_io.c等檔案中。而tty driver就指具體的裝置驅動,比如串列埠驅動,console驅動等。以下總結只是對tty構架的總體分析,希望對大家有所啟發。

tty的架構其實分為三層:

第一層:

tty_core

所有tty型別的驅動的頂層構架,向應用曾提供了統一的介面,應用層的read/write等呼叫首先會到達這裡。此層由核心實現,**主要分布在

drivers/char目錄下的n_tty.c,tty_io.c等檔案中

static const struct file_operations tty_fops = ;

每個tty型別的驅動註冊時都呼叫tty_register_driver函式

int tty_register_driver(struct     tty_driver * driver)

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,

loff_t *ppos)

static ssize_t tty_write(struct file *file, const char __user *buf,

size_t count, loff_t *ppos)

static inline ssize_t do_tty_write(

ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),

struct tty_struct *tty,

struct file *file,

const char __user *buf,

size_t count)

第二層:線路規程

不同的tty型別的裝置,具有不同的線路規程。這一層也由核心實現,主要**在drivers/char.tty_ldisc.c檔案中

從tty_read/tty_write函式可以看出,他們最後呼叫到了線路規程的read/write函式

struct tty_ldisc_ops tty_ldisc_n_tty = ;

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,

const unsigned char *buf, size_t nr)

//進入此處繼續執行的原因可能是被訊號打斷,而不是條件得到了滿足。

//只有條件得到了滿足,我們才會繼續,否則,直接返回!

if (tty_hung_up_p(file) || (tty->link && !tty->link->count))

if (o_opost(tty) && !(test_bit(tty_hw_cook_out, &tty->flags)))

b += num;

nr -= num;

if (nr == 0)

break;

c = *b;

if (process_output(c, tty) < 0)

break;

b++; nr--;

}if (tty->ops->flush_chars)

tty->ops->flush_chars(tty);

} else

if (!c)

break;

b += c;

nr -= c;}}

if (!nr)

break;

//全部寫入,返回

if (file->f_flags & o_nonblock)

/* 假如是以非阻塞的方式開啟的,那麼也直接返回。否則,讓出cpu,等條件滿足以後再繼續執行。

*/        

schedule();//執行到這裡,當前程序才會真正讓出cpu!!!

}break_out:

__set_current_state(task_running);

remove_wait_queue(&tty->write_wait, &wait);

...}

關於此段**的具體分析在

這段**中使用了wait等待佇列,為什麼要使用等待佇列呢?大家想想看,我們在應用層開啟乙個裝置檔案的時候,有兩種方式,阻塞和非阻塞,非阻塞很簡單,不管結果怎樣直接返回。但阻塞則有點死皮賴臉的意思,會一直等待,直到操作完成。那write函式的「阻塞」版本在核心裡邊是怎麼實現的呢?就是使用等待佇列,只要條件沒有得到滿足(驅動層呼叫write函式失敗),那麼就一直讓出cpu,直到條件滿足了才會繼續執行,並將寫操作的結果返回給上層。

通過以上分析,我們也可以得到如下結論:阻塞是在ldisc層也就是線路規程裡邊實現的。出於代價和操作性的考慮,我們不會再驅動裡邊實現阻塞型別的write/read函式

上述**中有一句:

c = tty->ops->write(tty, b, nr);

這句**呼叫到了tty_struct結構的ops->write函式。但是tty_struct結構的ops->write和具體的驅動裡邊定義的write函式有什麼關係呢?

tty_open -> tty_init_dev -> initialize_tty_struct

driver/char/tty_io.c

void initialize_tty_struct(struct tty_struct *tty,

struct tty_driver *driver, int idx)

可見,tty裝置開啟的時候,就將驅動的ops指標賦給了tty裝置的結構體tty_struct的ops

這樣,tty->ops->write()其實呼叫到了具體的驅動的write函式,比如,假如是個串列埠驅動,那麼就會呼叫到串列埠驅動的write函式!

n_tty_read的操作比較複雜,暫時不討論,但是它最終也會呼叫到具體的tty驅動的read函式

第三層:

具體的tty型別的驅動,由我們實現

比如,以下是摘自serial_core.c的一段**,描述的是串列埠驅動:

static const struct tty_operations uart_ops = ;

int uart_register_driver(struct uart_driver *drv)

我們主要實現這一層的功能,前兩層是kernel中已經實現的,我們僅僅需要套用之。當我們按照tty driver的格式書寫這一層驅動,並實現幾個必要的函式,這個驅動就可以成功運轉了。

Linux tty驅動程式《一》 架構

tty 核心概覽如下圖所示 可以看到,tty 架構的劃分層次,由下至上的邏輯關係為 硬體 tty 驅動 tty 線路規程 也譯為行規程 tty 核心 使用者層。核心負責控制 tty 裝置的資料流,並且格式化這些資料。這使得 tty 驅動程式把重點放在硬體資料處理上,而不必重點考慮使用常規方法與使用者...

u boot nand flash驅動架構分析一

在移植nand flash驅動之前,我們要先熟悉u boot中nand flash驅動架構以及nand flash操作原理。在u boot啟動過程中呼叫了nand init函式,這就是nand flash驅動初始化的入口點。if defined config cmd nand puts nand n...

字元裝置驅動 架構分析

好長時間沒怎麼看書了,最近把字元裝置驅動部分又複習了一下,寫個筆記.char device driver 相關資料結構 struct cdev struct kobj map probes 255 struct mutex lock static struct char device struct ...