📄 tty_io.c
字号:
/** linux/kernel/tty_io.c** (C) 1991 Linus Torvalds*//** 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles* or rs-channels. It also implements echoing, cooked mode etc.** Kill-line thanks to John T Kohl.*//** 'tty_io.c'给tty 一种非相关的感觉,是控制台还是串行通道。该程序同样* 实现了回显、规范(熟)模式等。** Kill-line,谢谢John T Kahl。*/#include <ctype.h> // 字符类型头文件。定义了一些有关字符类型判断和转换的宏。#include <errno.h> // 错误号头文件。包含系统中各种出错号。(Linus 从minix 中引进的)。#include <signal.h> // 信号头文件。定义信号符号常量,信号结构以及信号操作函数原型。// 下面给出相应信号在信号位图中的对应比特位。#define ALRMMASK (1<<(SIGALRM-1)) // 警告(alarm)信号屏蔽位。#define KILLMASK (1<<(SIGKILL-1)) // 终止(kill)信号屏蔽位。#define INTMASK (1<<(SIGINT-1)) // 键盘中断(int)信号屏蔽位。#define QUITMASK (1<<(SIGQUIT-1)) // 键盘退出(quit)信号屏蔽位。#define TSTPMASK (1<<(SIGTSTP-1)) // tty 发出的停止进程(tty stop)信号屏蔽位。#include <linux/sched.h> // 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。#include <linux/tty.h> // tty 头文件,定义了有关tty_io,串行通信方面的参数、常数。#include <asm/segment.h> // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。#include <asm/system.h> // 系统头文件。定义了设置或修改描述符/中断门等的嵌入式汇编宏。#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f) // 取termios 结构中的本地模式标志。#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f) // 取termios 结构中的输入模式标志。#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f) // 取termios 结构中的输出模式标志。// 取termios 结构中本地模式标志集中的一个标志位。#define L_CANON(tty) _L_FLAG((tty),ICANON) // 取本地模式标志集中规范(熟)模式标志位。#define L_ISIG(tty) _L_FLAG((tty),ISIG) // 取信号标志位。#define L_ECHO(tty) _L_FLAG((tty),ECHO) // 取回显字符标志位。#define L_ECHOE(tty) _L_FLAG((tty),ECHOE) // 规范模式时,取回显擦出标志位。#define L_ECHOK(tty) _L_FLAG((tty),ECHOK) // 规范模式时,取KILL 擦除当前行标志位。#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) // 取回显控制字符标志位。#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) // 规范模式时,取KILL 擦除行并回显标志位。// 取termios 结构中输入模式标志中的一个标志位。#define I_UCLC(tty) _I_FLAG((tty),IUCLC) // 取输入模式标志集中大写到小写转换标志位。#define I_NLCR(tty) _I_FLAG((tty),INLCR) // 取换行符NL 转回车符CR 标志位。#define I_CRNL(tty) _I_FLAG((tty),ICRNL) // 取回车符CR 转换行符NL 标志位。#define I_NOCR(tty) _I_FLAG((tty),IGNCR) // 取忽略回车符CR 标志位。// 取termios 结构中输出模式标志中的一个标志位。#define O_POST(tty) _O_FLAG((tty),OPOST) // 取输出模式标志集中执行输出处理标志。#define O_NLCR(tty) _O_FLAG((tty),ONLCR) // 取换行符NL 转回车换行符CR-NL 标志。#define O_CRNL(tty) _O_FLAG((tty),OCRNL) // 取回车符CR 转换行符NL 标志。#define O_NLRET(tty) _O_FLAG((tty),ONLRET) // 取换行符NL 执行回车功能的标志。#define O_LCUC(tty) _O_FLAG((tty),OLCUC) // 取小写转大写字符标志。// tty 数据结构的tty_table 数组。其中包含三个初始化项数据,分别对应控制台、串口终端1 和// 串口终端2 的初始化数据。struct tty_struct tty_table[] = { { {ICRNL, /* change incoming CR to NL *//* 将输入的CR 转换为NL */ OPOST | ONLCR, /* change outgoing NL to CRNL *//* 将输出的NL 转CRNL */ 0, // 控制模式标志初始化为0。 ISIG | ICANON | ECHO | ECHOCTL | ECHOKE, // 本地模式标志。 0, /* console termio */// 控制台termio。 INIT_C_CC}, // 控制字符数组。 0, /* initial pgrp */// 所属初始进程组。 0, /* initial stopped */// 初始停止标志。 con_write, // tty 写函数指针。 {0, 0, 0, 0, ""}, /* console read-queue */// tty 控制台读队列。 {0, 0, 0, 0, ""}, /* console write-queue */// tty 控制台写队列。 {0, 0, 0, 0, ""} /* console secondary queue */// tty 控制台辅助(第二)队列。 }, { {0, /* no translation */// 输入模式标志。0,无须转换。 0, /* no translation */// 输出模式标志。0,无须转换。 B2400 | CS8, // 控制模式标志。波特率2400bps,8 位数据位。 0, // 本地模式标志0。 0, // 行规程0。 INIT_C_CC}, // 控制字符数组。 0, // 所属初始进程组。 0, // 初始停止标志。 rs_write, // 串口1 tty 写函数指针。 {0x3f8, 0, 0, 0, ""}, /* rs 1 */// 串行终端1 读缓冲队列。 {0x3f8, 0, 0, 0, ""}, // 串行终端1 写缓冲队列。 {0, 0, 0, 0, ""} // 串行终端1 辅助缓冲队列。 }, { {0, /* no translation */// 输入模式标志。0,无须转换。 0, /* no translation */// 输出模式标志。0,无须转换。 B2400 | CS8, // 控制模式标志。波特率2400bps,8 位数据位。 0, // 本地模式标志0。 0, // 行规程0。 INIT_C_CC}, // 控制字符数组。 0, // 所属初始进程组。 0, // 初始停止标志。 rs_write, // 串口2 tty 写函数指针。 {0x2f8, 0, 0, 0, ""}, /* rs 2 */// 串行终端2 读缓冲队列。 {0x2f8, 0, 0, 0, ""}, // 串行终端2 写缓冲队列。 {0, 0, 0, 0, ""} // 串行终端2 辅助缓冲队列。 }};/** these are the tables used by the machine code handlers.* you can implement pseudo-tty's or something by changing* them. Currently not done.*//** 下面是汇编程序使用的缓冲队列地址表。通过修改你可以实现* 伪tty 终端或其它终端类型。目前还没有这样做。*/// tty 缓冲队列地址表。rs_io.s 汇编程序使用,用于取得读写缓冲队列地址。struct tty_queue *table_list[] = { &tty_table[0].read_q, &tty_table[0].write_q, // 控制台终端读、写缓冲队列地址。 &tty_table[1].read_q, &tty_table[1].write_q, // 串行口1 终端读、写缓冲队列地址。 &tty_table[2].read_q, &tty_table[2].write_q // 串行口2 终端读、写缓冲队列地址。};//// tty 终端初始化函数。// 初始化串口终端和控制台终端。voidtty_init (void){ rs_init (); // 初始化串行中断程序和串行接口1 和2。(serial.c, 37) con_init (); // 初始化控制台终端。(console.c, 617)}//// tty 键盘终端字符处理函数。// 参数:tty - 相应tty 终端结构指针;mask - 信号屏蔽位。voidtty_intr (struct tty_struct *tty, int mask){ int i;// 如果tty 所属组号小于等于0,则退出。 if (tty->pgrp <= 0) return;// 扫描任务数组,向tty 相应组的所有任务发送指定的信号。 for (i = 0; i < NR_TASKS; i++)// 如果该项任务指针不为空,并且其组号等于tty 组号,则设置该任务指定的信号mask。 if (task[i] && task[i]->pgrp == tty->pgrp) task[i]->signal |= mask;}//// 如果队列缓冲区空则让进程进入可中断的睡眠状态。// 参数:queue - 指定队列的指针。// 进程在取队列缓冲区中字符时调用此函数。static voidsleep_if_empty (struct tty_queue *queue){ cli (); // 关中断。// 若当前进程没有信号要处理并且指定的队列缓冲区空,则让进程进入可中断睡眠状态,并让// 队列的进程等待指针指向该进程。 while (!current->signal && EMPTY (*queue)) interruptible_sleep_on (&queue->proc_list); sti (); // 开中断。}//// 若队列缓冲区满则让进程进入可中断的睡眠状态。// 参数:queue - 指定队列的指针。// 进程在往队列缓冲区中写入时调用此函数。static voidsleep_if_full (struct tty_queue *queue){// 若队列缓冲区不满,则返回退出。 if (!FULL (*queue)) return; cli (); // 关中断。// 如果进程没有信号需要处理并且队列缓冲区中空闲剩余区长度<128,则让进程进入可中断睡眠状态,// 并让该队列的进程等待指针指向该进程。 while (!current->signal && LEFT (*queue) < 128) interruptible_sleep_on (&queue->proc_list); sti (); // 开中断。}//// 等待按键。// 如果控制台的读队列缓冲区空则让进程进入可中断的睡眠状态。voidwait_for_keypress (void){ sleep_if_empty (&tty_table[0].secondary);}//// 复制成规范模式字符序列。// 将指定tty 终端队列缓冲区中的字符复制成规范(熟)模式字符并存放在辅助队列(规范模式队列)中。// 参数:tty - 指定终端的tty 结构。voidcopy_to_cooked (struct tty_struct *tty){ signed char c;// 如果tty 的读队列缓冲区不空并且辅助队列缓冲区为空,则循环执行下列代码。 while (!EMPTY (tty->read_q) && !FULL (tty->secondary)) {// 从队列尾处取一字符到c,并前移尾指针。 GETCH (tty->read_q, c);// 下面对输入字符,利用输入模式标志集进行处理。// 如果该字符是回车符CR(13),则:若回车转换行标志CRNL 置位则将该字符转换为换行符NL(10);// 否则若忽略回车标志NOCR 置位,则忽略该字符,继续处理其它字符。 if (c == 13) if (I_CRNL (tty)) c = 10; else if (I_NOCR (tty)) continue; else;// 如果该字符是换行符NL(10)并且换行转回车标志NLCR 置位,则将其转换为回车符CR(13)。 else if (c == 10 && I_NLCR (tty)) c = 13;// 如果大写转小写标志UCLC 置位,则将该字符转换为小写字符。 if (I_UCLC (tty)) c = tolower (c);// 如果本地模式标志集中规范(熟)模式标志CANON 置位,则进行以下处理。 if (L_CANON (tty)) {// 如果该字符是键盘终止控制字符KILL(^U),则进行删除输入行处理。 if (c == KILL_CHAR (tty)) {/* deal with killing the input line *//* 删除输入行处理 */// 如果tty 辅助队列不空,或者辅助队列中最后一个字符是换行NL(10),或者该字符是文件结束字符// (^D),则循环执行下列代码。 while (!(EMPTY (tty->secondary) || (c = LAST (tty->secondary)) == 10 || c == EOF_CHAR (tty))) {// 如果本地回显标志ECHO 置位,那么:若字符是控制字符(值<32),则往tty 的写队列中放入擦除// 字符ERASE。再放入一个擦除字符ERASE,并且调用该tty 的写函数。 if (L_ECHO (tty)) { if (c < 32) PUTCH (127, tty->write_q); PUTCH (127, tty->write_q); tty->write (tty); }// 将tty 辅助队列头指针后退1 字节。 DEC (tty->secondary.head); } continue; // 继续读取并处理其它字符。 }// 如果该字符是删除控制字符ERASE(^H),那么: if (c == ERASE_CHAR (tty)) {// 若tty 的辅助队列为空,或者其最后一个字符是换行符NL(10),或者是文件结束符,继续处理// 其它字符。 if (EMPTY (tty->secondary) || (c = LAST (tty->secondary)) == 10 || c == EOF_CHAR (tty)) continue;// 如果本地回显标志ECHO 置位,那么:若字符是控制字符(值<32),则往tty 的写队列中放入擦除// 字符ERASE。再放入一个擦除字符ERASE,并且调用该tty 的写函数。 if (L_ECHO (tty)) { if (c < 32) PUTCH (127, tty->write_q); PUTCH (127, tty->write_q); tty->write (tty); }// 将tty 辅助队列头指针后退1 字节,继续处理其它字符。 DEC (tty->secondary.head); continue; }//如果该字符是停止字符(^S),则置tty 停止标志,继续处理其它字符。 if (c == STOP_CHAR (tty)) { tty->stopped = 1; continue; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -