⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tty_io.c

📁 一个用于学习的操作系统
💻 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 + -