TTY 行規程

TTY 行規程處理來自/到 tty 裝置的所有傳入和傳出字元。預設行規程是 N_TTY。如果為 tty 建立任何其他規程失敗,它也是一個後備選項。如果甚至 N_TTY 都失敗,則 N_NULL 接管。這永遠不會失敗,但也不會處理任何字元 - 它會丟棄它們。

註冊

行規程使用 tty_register_ldisc() 註冊,傳遞 ldisc 結構。在註冊時,規程必須準備好使用,並且可能會在呼叫返回成功之前被使用。如果呼叫返回錯誤,則不會被呼叫。不要重複使用 ldisc 編號,因為它們是使用者空間 ABI 的一部分,並且覆蓋現有的 ldisc 將導致守護程式吃掉您的計算機。即使使用相同的資料,您也不得重新註冊到行規程的頂部,否則您的計算機再次會被守護程式吃掉。為了刪除行規程,呼叫 tty_unregister_ldisc()

請注意此警告:ldisc 表中 tty_ldisc 結構的已註冊副本的引用計數字段計算使用此規程的行數。tty 內 tty_ldisc 結構的引用計數計算此時 ldisc 的活動使用者數。實際上,它計算 ldisc 方法中的執行執行緒數(加上即將進入和退出的執行緒,儘管此細節並不重要)。

int tty_register_ldisc(struct tty_ldisc_ops *new_ldisc)

安裝行規程

引數

struct tty_ldisc_ops *new_ldisc

指向 ldisc 物件的指標

描述

將新的行規程安裝到核心中。該規程設定為未引用,然後從此時開始可供核心使用。

鎖定:使用 tty_ldiscs_lock 來防止 ldisc 競爭

void tty_unregister_ldisc(struct tty_ldisc_ops *ldisc)

解除安裝行規程

引數

struct tty_ldisc_ops *ldisc

ldisc 編號

描述

從核心中刪除行規程,如果它當前未使用。

鎖定:使用 tty_ldiscs_lock 來防止 ldisc 競爭

其他函式

void tty_ldisc_flush(struct tty_struct *tty)

重新整理行規程佇列

引數

struct tty_struct *tty

要為其重新整理 ldisc 的 tty

描述

重新整理此 tty 的行規程佇列(如果有)和 tty 翻轉緩衝區。

int tty_set_ldisc(struct tty_struct *tty, int disc)

設定行規程

引數

struct tty_struct *tty

要設定的終端

int disc

行規程編號

描述

設定 tty 線的規程。必須從程序上下文中呼叫。ldisc 更改邏輯必須保護自身免受任何重疊的 ldisc 更改(包括 pty 對的另一端),tty/pty 對的一側關閉,以及最終的掛起。

行規程操作參考

struct tty_ldisc_ops

ldisc 操作

定義:

struct tty_ldisc_ops {
    char *name;
    int num;
    int (*open)(struct tty_struct *tty);
    void (*close)(struct tty_struct *tty);
    void (*flush_buffer)(struct tty_struct *tty);
    ssize_t (*read)(struct tty_struct *tty, struct file *file, u8 *buf, size_t nr, void **cookie, unsigned long offset);
    ssize_t (*write)(struct tty_struct *tty, struct file *file, const u8 *buf, size_t nr);
    int (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
    int (*compat_ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
    void (*set_termios)(struct tty_struct *tty, const struct ktermios *old);
    __poll_t (*poll)(struct tty_struct *tty, struct file *file, struct poll_table_struct *wait);
    void (*hangup)(struct tty_struct *tty);
    void (*receive_buf)(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count);
    void (*write_wakeup)(struct tty_struct *tty);
    void (*dcd_change)(struct tty_struct *tty, bool active);
    size_t (*receive_buf2)(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count);
    void (*lookahead_buf)(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count);
    struct module *owner;
};

成員

名稱

此 ldisc 在 /proc/tty/ldiscs 中呈現的名稱

編號

N_* 編號(N_TTYN_HDLC、...)保留給此 ldisc

開啟

[TTY] int ()(struct tty_struct *tty)

當行規程與 tty 關聯時,會呼叫此函式。在此函式成功完成之前,不會對該 tty 的行規程進行其他呼叫。它應該初始化 ldisc 所需的任何狀態,並將 tty->receive_room 設定為行規程願意接受來自驅動程式的單個 receive_buf() 呼叫的最大資料量。返回錯誤將阻止附加 ldisc。

可選。可以休眠。

關閉

[TTY] void ()(struct tty_struct *tty)

當正在關閉行規程時,會呼叫此函式,原因是 tty 正在關閉,或者因為 tty 正在更改為使用新的行規程。在執行時,沒有其他使用者會進入此 tty 的 ldisc 程式碼。

可選。可以休眠。

flush_buffer

[TTY] void ()(struct tty_struct *tty)

此函式指示行規程清除其緩衝區中可能已排隊以傳遞給使用者模式程序的任何輸入字元。它可以在開啟和關閉之間的任何時間點呼叫。

可選。

讀取

[TTY] ssize_t ()(struct tty_struct *tty, struct file *file, u8 *buf, size_t nr)

當用戶請求從 tty 讀取時,會呼叫此函式。行規程將返回它為使用者緩衝的所有字元。如果未定義此函式,使用者將收到 EIO 錯誤。多個讀取呼叫可以並行發生,ldisc 必須處理序列化問題。

可選:除非提供,否則為 EIO。可以休眠。

寫入

[TTY] ssize_t ()(struct tty_struct *tty, struct file *file, const u8 *buf, size_t nr)

當用戶請求寫入 tty 時,會呼叫此函式。行規程將字元傳遞給低階 tty 裝置以進行傳輸,可以選擇先對字元執行一些處理。如果未定義此函式,使用者將收到 EIO 錯誤。

可選:除非提供,否則為 EIO。可以休眠。

ioctl

[TTY] int ()(struct tty_struct *tty, unsigned int cmd, unsigned long arg)

當用戶請求 ioctl 時,會呼叫此函式,該 ioctl 不由 tty 層或低階 tty 驅動程式處理。它用於影響行規程操作的 ioctl。請注意,ioctl 的搜尋順序為 (1) tty 層,(2) tty 低階驅動程式,(3) 行規程。因此,低階驅動程式可以在行規程有機會看到它之前“獲取”ioctl 請求。

可選。

compat_ioctl

[TTY] int ()(struct tty_struct *tty, unsigned int cmd, unsigned long arg)

處理來自 64 位系統上的 32 位程序的 ioctl 呼叫。

請注意,只有既不是“指向相容結構的指標”也不是 tty 通用的 ioctl 屬於此處。某些採用整數或指向字長敏感結構的指標的私有內容屬於此處,但大多數 ldisc 會很高興地將其保留為 NULL

可選。

set_termios

[TTY] void ()(struct tty_struct *tty, const struct ktermios *old)

此函式通知行規程已對 termios 結構進行了更改。

可選。

輪詢

[TTY] int ()(struct tty_struct *tty, struct file *file, struct poll_table_struct *wait)

當用戶嘗試在 tty 裝置上選擇/輪詢時,會呼叫此函式。完全由行規程負責處理輪詢請求。

可選。

結束通話

[TTY] void ()(struct tty_struct *tty)

在結束通話時呼叫。告訴規程它應該停止對 tty 驅動程式的 I/O。驅動程式應設法快速執行此操作,但應等到任何掛起的驅動程式 I/O 完成。不會再呼叫 ldisc 程式碼。

可選。可以休眠。

receive_buf

[DRV] void ()(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count)

低階 tty 驅動程式會呼叫此函式,以將硬體接收的字元傳送到行規程進行處理。cp 是指向裝置接收的輸入字元緩衝區的指標。fp 是指向標誌位元組陣列的指標,這些位元組指示是否以奇偶校驗錯誤等方式接收到字元。fp 可以是 NULL,表示接收到的所有資料均為 TTY_NORMAL

可選。

write_wakeup

[DRV] void ()(struct tty_struct *tty)

低階 tty 驅動程式會呼叫此函式,以指示行規程應嘗試將更多字元傳送到低階驅動程式進行傳輸。如果行規程沒有更多資料要傳送,則可以只返回。如果行規程確實有一些資料要傳送,請引發一個 tasklet 或工作佇列來執行實際的資料傳輸。不要在此掛鉤中傳送資料,這可能會導致死鎖。

可選。

dcd_change

[DRV] void ()(struct tty_struct *tty, bool active)

告訴規程 DCD 引腳已更改其狀態。僅由 N_PPS(每秒脈衝)行規程使用。

可選。

receive_buf2

[DRV] ssize_t ()(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count)

低階 tty 驅動程式會呼叫此函式,以將硬體接收的字元傳送到行規程進行處理。cp 是指向裝置接收的輸入字元緩衝區的指標。fp 是指向標誌位元組陣列的指標,這些位元組指示是否以奇偶校驗錯誤等方式接收到字元。fp 可以是 NULL,表示接收到的所有資料均為 TTY_NORMAL。如果已分配,則優先使用此函式進行自動流量控制。

可選。

lookahead_buf

[DRV] void ()(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count)

低階 tty 驅動程式會呼叫此函式來處理未被 ->receive_buf() 或 ->receive_buf2() 使用的字元。它對於處理高優先順序字元非常有用,例如軟體流量控制字元,否則這些字元可能會卡在中間緩衝區中,直到 tty 有空間接收它們。Ldisc 必須能夠稍後處理對相同字元的 ->receive_buf() 或 ->receive_buf2() 呼叫(例如,透過跳過已由 ->lookahead_buf() 處理的高優先順序字元的操作)。

可選。

所有者

包含此 ldisc 的模組(用於引用計數)

描述

此結構定義了 tty 行規程實現與 tty 例程之間的介面。可以定義上述例程。除非另有說明,否則它們是可選的,並且可以使用 NULL 指標填充。

標有 [TTY] 的掛鉤是從 TTY 核心呼叫的,標有 [DRV] 的掛鉤是從 tty_driver 端呼叫的。

驅動程式訪問

行規程方法可以呼叫底層硬體驅動程式的方法。這些方法記錄為 struct tty_operations 的一部分。

TTY 標誌

行規程方法可以訪問 tty_struct.flags 欄位。請參閱 TTY 結構體

鎖定

來自 tty 層的行規程函式的呼叫者需要獲取行規程鎖。來自驅動程式端的呼叫也是如此,但尚未強制執行。

struct tty_ldisc * tty_ldisc_ref_wait(struct tty_struct *tty)

等待 tty ldisc

引數

struct tty_struct *tty

tty 裝置

描述

取消引用終端的行規程並獲取對其的引用。如果行規程不穩定,則耐心等待直到它更改。

注意 1:不得從 IRQ/定時器上下文中呼叫。呼叫者還必須小心不要持有其他鎖,這些鎖會因規程更改而導致死鎖,例如現有的 ldisc 引用(我們會對此進行檢查)。

注意 2:file_operations 例程(讀取/輪詢/寫入)應使用此函式等待任何 ldisc 生存期事件完成。

返回

如果 tty 已結束通話且未使用新的檔案描述符重新開啟,則為 NULL,否則為有效的 ldisc 引用

struct tty_ldisc * tty_ldisc_ref(struct tty_struct *tty)

獲取 tty ldisc

引數

struct tty_struct *tty

tty 裝置

描述

取消引用終端的行規程並獲取對其的引用。如果行規程不穩定,則返回 NULL。可以從 IRQ 和定時器函式中呼叫。

void tty_ldisc_deref(struct tty_ldisc *ld)

釋放 tty ldisc 引用

引數

struct tty_ldisc *ld

要釋放的引用

描述

撤消 tty_ldisc_ref()tty_ldisc_ref_wait() 的效果。可以在 IRQ 上下文中呼叫。

雖然這些函式比舊程式碼稍慢,但它們應該具有最小的影響,因為大多數接收邏輯都使用翻轉緩衝區,並且它們只需要在透過驅動程式向上推送位時才進行引用。

警告:tty_ldisc_ops.open()tty_ldisc_ops.close()tty_driver.set_ldisc() 函式在 ldisc 不可用時被呼叫。因此,如果在這些函式中使用,tty_ldisc_ref() 將失敗。在這種情況下,呼叫其自身函式的 Ldisc 和驅動程式程式碼必須小心。

內部函式

struct tty_ldisc * tty_ldisc_get(struct tty_struct *tty, int disc)

獲取 ldisc 的引用

引數

struct tty_struct *tty

tty 裝置

int disc

ldisc 編號

描述

獲取對行規程的引用。處理引用計數和模組鎖定計數。如果該規程不可用,則儘可能載入其模組。

鎖定:使用 tty_ldiscs_lock 來防止 ldisc 競爭

返回

  • - 如果規程索引不是 [N_TTY .. NR_LDISCS],或者如果未註冊該規程,則返回 EINVAL

  • - 如果 request_module() 無法載入或註冊該規程,則返回 EAGAIN

  • - 如果分配失敗,則返回 ENOMEM

  • 否則,返回指向規程的指標並增加引用計數

void tty_ldisc_put(struct tty_ldisc *ld)

釋放 ldisc

引數

struct tty_ldisc *ld

要釋放的 lisdsc

描述

tty_ldisc_get() 的補充。

void tty_set_termios_ldisc(struct tty_struct *tty, int disc)

設定 ldisc 欄位

引數

struct tty_struct *tty

tty 結構體

int disc

行規程編號

描述

對於現實世界的處理器來說,這可能有點矯枉過正,但它們不在熱路徑上,因此稍微規程化不會造成任何危害。

重置與行規程相關的 tty_struct 欄位,以防止 ldisc 驅動程式為新的 ldisc 例項重用過時的資訊。

鎖定:使用 termios_rwsem

int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)

開啟行規程

引數

struct tty_struct *tty

我們要在其上開啟 ldisc 的 tty

struct tty_ldisc *ld

要開啟的規程

描述

一個幫助開啟方法。也是一個方便的除錯和檢查點。

鎖定:始終在已持有 BTM 的情況下呼叫。

void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)

關閉行規程

引數

struct tty_struct *tty

我們要在其上開啟 ldisc 的 tty

struct tty_ldisc *ld

要關閉的規程

描述

一個幫助關閉方法。也是一個方便的除錯和檢查點。

int tty_ldisc_failto(struct tty_struct *tty, int ld)

ldisc 回退的幫助程式

引數

struct tty_struct *tty

要在其上開啟 ldisc 的 tty

int ld

我們正在嘗試回退到的 ldisc

描述

在切換回舊 ldisc 失敗並且我們需要附加某些東西時,嘗試恢復 tty 的幫助程式。

void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)

tty ldisc 更改的幫助程式

引數

struct tty_struct *tty

要恢復的 tty

struct tty_ldisc *old

先前的 ldisc

描述

當由於開啟錯誤而導致行規程更改失敗時,恢復先前的行規程或 N_TTY

void tty_ldisc_kill(struct tty_struct *tty)

拆卸 ldisc

引數

struct tty_struct *tty

正在釋放的 tty

描述

執行 ldisc 的最終關閉並重置 tty->ldisc

void tty_reset_termios(struct tty_struct *tty)

重置終端狀態

引數

struct tty_struct *tty

要重置的 tty

描述

將終端恢復到驅動程式預設狀態。

int tty_ldisc_reinit(struct tty_struct *tty, int disc)

重新初始化 tty ldisc

引數

struct tty_struct *tty

要重新初始化的 tty

int disc

要重新初始化的行規程

描述

透過關閉當前例項(如果有)並開啟一個新例項來完全重新初始化行規程狀態。如果在開啟新的非 N_TTY 例項時發生錯誤,則會刪除該例項並將 tty->ldisc 重置為 NULL。然後,呼叫者可以使用 N_TTY 重試。

返回

如果成功,則為 0,否則為錯誤程式碼 < 0

void tty_ldisc_hangup(struct tty_struct *tty, bool reinit)

結束通話 ldisc 重置

引數

struct tty_struct *tty

正在結束通話的 tty

bool reinit

是否重新初始化 tty

描述

有些 tty 裝置在接收到結束通話事件時會重置其 termios。在這種情況下,我們還必須在重置 termios 資料之前切換回 N_TTY

鎖定:我們可以使用 ldisc 互斥鎖,因為其餘程式碼都注意允許這樣做。

在 pty 對的情況下,這發生在 tty 本身的 close() 路徑中,因此我們必須小心鎖定規則。

int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)

開啟線路規程

引數

struct tty_struct *tty

tty 正在關閉

struct tty_struct *o_tty

pty/tty 對的配對 tty

描述

在 tty/pty 對的初始開啟期間呼叫,以便設定線路規程並將它們繫結到 tty。這沒有鎖定問題,因為裝置尚未啟用。

void tty_ldisc_release(struct tty_struct *tty)

釋放線路規程

引數

struct tty_struct *tty

tty 正在關閉(或 pty 對的一端)

描述

在 tty 或 pty 對的最終關閉期間呼叫,以便關閉線路規程層。退出時,每個 tty 的 ldisc 均為 NULL

int tty_ldisc_init(struct tty_struct *tty)

新 tty 的 ldisc 設定

引數

struct tty_struct *tty

正在分配 tty

描述

為新分配的 tty 設定線路規程物件。請注意,進行此呼叫時,tty 結構尚未完全設定。

void tty_ldisc_deinit(struct tty_struct *tty)

新 tty 的 ldisc 清理

引數

struct tty_struct *tty

最近分配的 tty

描述

進行此呼叫時,tty 結構不得完全設定(tty_ldisc_setup())。