📄 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 终端初始化函数。// 初始化串口终端和控制台终端。void tty_init(void){rs_init(); // 初始化串行中断程序和串行接口1 和2。(serial.c, 37)con_init(); // 初始化控制台终端。(console.c, 617)}//// tty 键盘终端字符处理函数。// 参数:tty - 相应tty 终端结构指针;mask - 信号屏蔽位。void tty_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 void sleep_if_empty(struct tty_queue * queue){cli(); // 关中断。// 若当前进程没有信号要处理并且指定的队列缓冲区空,则让进程进入可中断睡眠状态,并让// 队列的进程等待指针指向该进程。while (!current->signal && EMPTY(*queue))interruptible_sleep_on(&queue->proc_list);sti(); // 开中断。}//// 若队列缓冲区满则让进程进入可中断的睡眠状态。// 参数:queue - 指定队列的指针。// 进程在往队列缓冲区中写入时调用此函数。static void sleep_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(); // 开中断。}//// 等待按键。// 如果控制台的读队列缓冲区空则让进程进入可中断的睡眠状态。void wait_for_keypress(void){sleep_if_empty(&tty_table[0].secondary);}//// 复制成规范模式字符序列。// 将指定tty 终端队列缓冲区中的字符复制成规范(熟)模式字符并存放在辅助队列(规范模式队列)中。// 参数:tty - 指定终端的tty 结构。void copy_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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -