📄 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.
*/
#include <ctype.h>
#include <fairysky/errno.h>
#include <fairysky/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 <fairysky/scheduler.h>
#include <fairysky/tty.h>
#include <asm/segment.h>
#include <asm/system.h>
#define _LOCAL_FLAG(tty, f) ((tty)->termios.c_lflag & f) //取termios结构中的本地模式标志
#define _INPUT_FLAG(tty, f) ((tty)->termios.c_iflag & f) //取termios结构中的输入模式标志
#define _OUTPUT_FLAG(tty, f) ((tty)->termios.c_oflag & f) //取termios结构中的输出模式标志
#define LOCAL_CANON(tty) _LOCAL_FLAG((tty), ICANON) //取本地模式标志集中规范模式标志
#define LOCAL_ISIG(tty) _LOCAL_FLAG((tty), ISIG) //取信号标志
#define LOCAL_ECHO(tty) _LOCAL_FLAG((tty), ECHO) //取回显字符标志
#define LOCAL_ECHOE(tty) _LOCAL_FLAG((tty), ECHOE) //规范模式时,取回显檫除标志
#define LOCAL_ECHOK(tty) _LOCAL_FLAG((tty), ECHOK) //规范模式时,取KILL檫除当前行标志
#define LOCAL_ECHOCTL(tty) _LOCAL_FLAG((tty), ECHOCTL) //取回显控制字符标志
#define LOCAL_ECHOKE(tty) _LOCAL_FLAG((tty), ECHOKE) //规范模式时,取KILL檫除行并回显标志
#define INPUT_UCLC(tty) _INPUT_FLAG((tty), IUCLC) //取输入模式标志集中大写到小写转换标志
#define INPUT_NLCR(tty) _INPUT_FLAG((tty), INLCR) //取换行符NL转回车附CR-NL标志
#define INPUT_CRNL(tty) _INPUT_FLAG((tty), ICRNL) //取回车附CR转换行附NL标志
#define INPUT_NOCR(tty) _INPUT_FLAG((tty), IGNCR) //取忽略回车附CR标志
#define OUTPUT_POST(tty) _OUTPUT_FLAG((tty), OPOST) //取输出模式标志集中执行输出处理标志
#define OUTPUT_NLCR(tty) _OUTPUT_FLAG((tty), ONLCR) //取换行附NL转回车换行附CR-NL标志
#define OUTPUT_CRNL(tty) _OUTPUT_FLAG((tty), OCRNL) //取回车附CR转转换行附NL标志
#define OUTPUT_NLRET(tty) _OUTPUT_FLAG((tty), ONLRET) //取换行附NL执行回车功能标志
#define OUTPUT_LCUC(tty) _OUTPUT_FLAG((tty), OLCUC) //取小写转大写字符标志
struct tty_struct tty_table[] = {
{
{ICRNL, //将输入的CR转换为NL
OPOST | ONLCR, //将输出的NL转CRNL
0, //控制模式标志初始化为0
ISIG | ICANON | ECHO | ECHOCTL | ECHOKE, //本地模式标志
//ISIG | ICANON | ECHO | ECHOKE, //本地模式标志
0, //控制台termio
INIT_C_CC}, //控制字符数组
0, //所属初始进程组
0, //初始停止标志
con_write, //tty写函数指针
{0, 0, 0, 0, ""}, //tty控制台读队列
{0, 0, 0, 0, ""}, //tty控制台写队列
{0, 0, 0, 0, ""} //tty控制台辅助队列
}
};
//控制台终端读写缓存队列地址
struct tty_queue *table_list[] = {
&tty_table[0].read_q, &tty_table[0].write_q
};
//给进程设置信号
void tty_intr(struct tty_struct *tty, int mask)
{
int i;
task_struct_t *tmp;
if (tty->pgrp <= 0) {
return;
}
//遍历睡眠进程队列
TASK_EACH(sleep_task, tmp) {
if (tmp && tmp->pgrp == tty->pgrp) {
tmp->signal |= mask;
}
}
//遍历可运行进程队列
TASK_EACH(pinit_task, tmp) {
if (tmp && tmp->pgrp == tty->pgrp) {
tmp->signal |= mask;
}
}
}
//若队列缓存区空,则让进程进入可中断的睡眠
static void sleep_if_empty(struct tty_queue *queue)
{
cli();
while (!current->signal && EMPTY(*queue)) {
queue->proc_list = current;
interruptible_sleep_on(queue->proc_list);
}
sti();
}
//若队列缓存区满,则让进程进入可中断的睡眠
static void sleep_if_full(struct tty_queue *queue)
{
if (!FULL(*queue)) {
return;
}
cli();
while (!current->signal && LEFT(*queue) < 128) {
queue->proc_list = current;
interruptible_sleep_on(queue->proc_list);
}
sti();
}
//等待按键,如果控制台的读队列缓存区空,则让进程进入可中断的睡眠
void wait_for_keypress(void)
{
sleep_if_empty(&tty_table[0].secondary);
}
//复制规范模式字符序列。
void copy_to_cooked(struct tty_struct *tty)
{
signed char c;
//如果tty的读队列缓存区不空并且辅助队列为空,则循环执行以下代码
while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) {
GETCH(tty->read_q, c);
//printk("|c:%c|", c);
if (c == 13) { //如果是回车符号
if (INPUT_CRNL(tty)) { //如果回车附CR转换行附NL
c = 10;
} else if (INPUT_NOCR(tty)) { //如果忽略回车附
continue;
} else {
;
}
} else if (c == 10 && INPUT_NLCR(tty)) { //如果换行符NL转回车附CR-NL
c = 13;
}
if (INPUT_UCLC(tty)) { //如果大写转换成小写
c = tolower(c);
}
if (LOCAL_CANON(tty)) { //本地模式是规范模式
if (c == KILL_CHAR(tty)) { //删除行处理
//如果tty规范队列不空,或者规范队列中最后一个字符是换行NL10,或者该字符是文件结束字符
while (!(EMPTY(tty->secondary) ||
(c = LAST(tty->secondary)) == 10 ||
c == EOF_CHAR(tty))) {
if (LOCAL_ECHO(tty)) { //如果要回显
if (c < 32) {
PUTCH(127, tty->write_q);
}
PUTCH(127, tty->write_q);
tty->write(tty);
}
DEC(tty->secondary.head);
}
continue; //继续处理下一个字符
}
if (c == ERASE_CHAR(tty)) { //如果是删除控制字符
if (EMPTY(tty->secondary) ||
(c = LAST(tty->secondary)) == 10 ||
c == EOF_CHAR(tty)) { //如果辅助队列,或者对后一个字符为NL或文件结束符
continue; //继续处理下一个字符
}
if (LOCAL_ECHO(tty)) { //回显处理
if (c < 32) {
PUTCH(127, tty->write_q);
}
PUTCH(127, tty->write_q);
tty->write(tty);
}
DEC(tty->secondary.head);
continue;
}
if (c == STOP_CHAR(tty)) { //停止字符
tty->stopped = 1;
continue;
}
if (c == START_CHAR(tty)) { //开始字符
tty->stopped = 0;
continue;
}
}
//若输入模式标志集中ISIG标志置位,则在收到INTR, QUIT, SUSP, DSUSP字符时,需要为进程产生相应的信号
if (LOCAL_ISIG(tty)) {
//如果该字符时键盘中断附(^C),则象当前进程发送键盘中断信号,并继续处理下个字符
if (c == INTR_CHAR(tty)) {
tty_intr(tty, INTMASK);
continue;
}
//如果该字符时键盘退出附,则象当前进程发送键盘退出信号,并继续处理下个字符
if (c == QUIT_CHAR(tty)) {
tty_intr(tty, QUITMASK);
continue;
}
}
//如果该字符是换行附,或者是文件结束符,辅助队列字符行数加1
if (c == 10 || c == EOF_CHAR(tty)) {
tty->secondary.data++;
}
if (LOCAL_ECHO(tty)) { //如果回显
//printk("1");
if (c == 10) { //那么字符NL,则将NL,CR放入tty写队列中
PUTCH(10, tty->write_q);
PUTCH(13, tty->write_q);
} else if (c < 32) { //如果字符是控制字符
if (LOCAL_ECHOCTL(tty)) { //回显控制字符
//printk("2 c:%c, %d|", c, c);
PUTCH('^', tty->write_q); //将显示控制字符的字符放入tty写队列中
PUTCH(c + 64, tty->write_q);
} else {
PUTCH(c, tty->write_q); //这个是我加的
}
} else {
//printk("|write_q c:%c|", c);
PUTCH(c, tty->write_q);
}
//printk("...tty->write(tty);...");
tty->write(tty); //进行回显处理
}
PUTCH(c, tty->secondary); //将字符放入规范队列
}
wake_up(tty->secondary.proc_list);
}
//tty读函数
static int read_tty_channel(unsigned channel, char *buf, int nr)
{
struct tty_struct *tty;
char c, *b = buf;
int minimum, time, flag = 0;
long oldalarm;
//printk("nr:%d ", nr);
if (channel > sizeof(table_list) || nr < 0) {
return -1;
}
tty = &tty_table[channel];
oldalarm = current->alarm;
time = 10L * tty->termios.c_cc[VTIME];
minimum = tty->termios.c_cc[VMIN];
if (time && !minimum) {
minimum = 1;
if (flag = (!oldalarm || time + jiffies < oldalarm)) {
current->alarm = time + jiffies;
}
}
if (minimum > nr) {
minimum = nr;
}
while (nr > 0) {
if (flag && (current->signal & ALRMMASK)) {
current->signal &= ~ALRMMASK;
break;
}
if (current->signal) {
break;
}
if (EMPTY(tty->secondary) ||
(LOCAL_CANON(tty) && !tty->secondary.data && LEFT(tty->secondary) > 20)) {
sleep_if_empty(&tty->secondary);
continue;
}
do {
GETCH(tty->secondary, c);
if (c == EOF_CHAR(tty) || c == 10) {
tty->secondary.data--;
}
if (c == EOF_CHAR(tty) && LOCAL_CANON(tty)) {
//printk("b - buf");
return (b - buf);
} else {
//printk("input %c", c);
put_fs_byte(c, b++);
if (!--nr) {
break;
}
}
} while (nr > 0 && !EMPTY(tty->secondary));
if (time && !LOCAL_CANON(tty)) {
if (flag = (!oldalarm || time + jiffies < oldalarm)) {
current->alarm = time + jiffies;
} else {
current->alarm = oldalarm;
}
}
if (LOCAL_CANON(tty)) {
if (b - buf) {
break;
}
} else if (b - buf >= minimum) {
break;
}
}
current->alarm = oldalarm;
if (current->signal && !(b - buf)) {
//printk("return -EINTR");
return -EINTR;
}
//printk("end b - buf");
return (b - buf);
}
static int tty_read(struct inode * inode, struct file * file, char * buf, int count)
{
int i;
//if (MAJOR(file->f_rdev) != 4) {
// printk("tty_read: pseudo-major != 4\n");
// return -EINVAL;
//}
i = read_tty_channel(/*MINOR(file->f_rdev)*/0, /*file,*/ buf, count);
//if (i > 0)
// inode->i_atime = CURRENT_TIME;
return i;
}
//tty写函数
int write_tty_channel(unsigned channel, char *buf, int nr)
{
static cr_flag = 0;
struct tty_struct *tty;
char c, *b = buf;
if (channel > sizeof(table_list) || nr < 0) {
return -1;
}
tty = channel + tty_table;
while (nr > 0) {
sleep_if_full(&tty->write_q);
if (current->signal) {
break;
}
while (nr > 0 && !FULL(tty->write_q)) {
c = get_fs_byte(b);
//printk("%c", c);
if (OUTPUT_POST(tty)) {
if (c == '\r' && OUTPUT_CRNL(tty)) {
c = '\n';
} else if (c == '\n' && OUTPUT_NLRET(tty)) {
c = '\r';
}
if (c == '\n' && !cr_flag && OUTPUT_NLCR(tty)) {
cr_flag = 1;
PUTCH(13, tty->write_q);
continue;
}
if (OUTPUT_LCUC(tty)) {
c = toupper(c);
}
}
b++; nr--;
cr_flag = 0;
PUTCH(c, tty->write_q);
}
tty->write(tty);
if (nr > 0) {
scheduler();
}
}
return (b - buf);
}
static int tty_write(struct inode * inode, struct file * file, char * buf, int count)
{
int i;
//printk("tty_write before %s ", buf);
i = write_tty_channel(/*MINOR(file->f_rdev)*/0, /*file,*/ buf, count);
//printk("tty_write after %s ", buf);
return i;
}
/*
* Jeh, sometimes I really like the 386.
* This routine is called from an interrupt,
* and there should be absolutely no problem
* with sleeping even in an interrupt (I hope).
* Of course, if somebody proves me wrong, I'll
* hate intel for all time :-). We'll have to
* be careful and see to reinstating the interrupt
* chips before calling this, though.
*
* I don't think we sleep here under normal circumstances
* anyway, which is good, as the task sleeping might be
* totally innocent.
*/
void do_tty_interrupt(int tty)
{
copy_to_cooked(tty_table + tty);
}
void chr_dev_init(void)
{
}
static struct file_operations tty_fops = {
NULL,
tty_read,
tty_write,
NULL,
NULL,
NULL,
NULL,
NULL
};
//tty终端初始化
void init_tty(void)
{
chrdev_fops = &tty_fops; //初始化tty的读写跳转表
init_con(); //初始化控制台,即显卡的显示内存的初始信息
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -