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())。