123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- #include <algorithm>
- #include <stdint.h>
- #include <stdio.h>
- #include <termios.h>
- #include <types/lock.hpp>
- #include <kernel/event/evtqueue.hpp>
- #include <kernel/hw/serial.h>
- #include <kernel/process.hpp>
- #include <kernel/tty.hpp>
- #include <kernel/vga.hpp>
- #include <kernel/log.hpp>
- #define CTRL(key) ((key)-0x40)
- #define TERMIOS_ISET(termios, option) ((option) == ((termios).c_iflag & (option)))
- #define TERMIOS_OSET(termios, option) ((option) == ((termios).c_oflag & (option)))
- #define TERMIOS_CSET(termios, option) ((option) == ((termios).c_cflag & (option)))
- #define TERMIOS_LSET(termios, option) ((option) == ((termios).c_lflag & (option)))
- #define TERMIOS_TESTCC(c, termios, cc) ((c != 0xff) && (c == ((termios).c_cc[cc])))
- tty::tty()
- : termio {
- .c_iflag = ICRNL | IXOFF,
- .c_oflag = OPOST | ONLCR,
- .c_cflag = B38400 | CS8 | CREAD | HUPCL,
- .c_lflag = ISIG | ICANON | ECHO | ECHOE |
- ECHOK | ECHOCTL | ECHOKE | IEXTEN,
- .c_line = N_TTY,
- .c_cc {},
- .c_ispeed = 38400,
- .c_ospeed = 38400,
- }
- , buf(BUFFER_SIZE)
- , fg_pgroup { 0 }
- {
- memset(this->termio.c_cc, 0x00, sizeof(this->termio.c_cc));
- // other special characters is not supported for now
- this->termio.c_cc[VINTR] = CTRL('C');
- this->termio.c_cc[VQUIT] = CTRL('\\');
- this->termio.c_cc[VERASE] = 0x7f;
- this->termio.c_cc[VKILL] = CTRL('U');
- this->termio.c_cc[VEOF] = CTRL('D');
- this->termio.c_cc[VSUSP] = CTRL('Z');
- this->termio.c_cc[VMIN] = 1;
- }
- void tty::print(const char* str)
- {
- while (*str != '\0')
- this->putchar(*(str++));
- }
- size_t tty::read(char* buf, size_t buf_size, size_t n)
- {
- n = std::max(buf_size, n);
- size_t orig_n = n;
- do {
- if (n == 0)
- break;
- auto& mtx = this->m_cv.mtx();
- types::lock_guard lck(mtx);
- if (this->buf.empty()) {
- bool interrupted = !this->m_cv.wait(mtx);
- if (interrupted)
- break;
- }
- if (this->buf.empty())
- break;
- if (!TERMIOS_LSET(this->termio, ICANON)) {
- --n, *buf = this->buf.get();
- break;
- }
- while (n &&!this->buf.empty()) {
- int c = this->buf.get();
- --n, *(buf++) = c;
- // canonical mode
- if (c == '\n')
- break;
- }
- } while (false);
- return orig_n - n;
- }
- int tty::_do_erase(bool should_echo)
- {
- if (buf.empty())
- return -1;
- int back = buf.back();
- if (back == '\n' || back == this->termio.c_cc[VEOF])
- return -1;
- if (back == this->termio.c_cc[VEOL] || back == this->termio.c_cc[VEOL2])
- return -1;
- buf.pop();
- if (should_echo && TERMIOS_LSET(this->termio, ECHO | ECHOE)) {
- this->show_char('\b'); // backspace
- this->show_char(' '); // space
- this->show_char('\b'); // backspace
- // xterm's way to show backspace
- // serial_send_data(id, '\b');
- // serial_send_data(id, CTRL('['));
- // serial_send_data(id, '[');
- // serial_send_data(id, 'K');
- }
- return back;
- }
- void tty::_real_commit_char(int c)
- {
- switch (c) {
- case '\n':
- buf.put(c);
- if (TERMIOS_LSET(this->termio, ECHONL) || TERMIOS_LSET(this->termio, ECHO))
- this->_echo_char(c);
- if (TERMIOS_LSET(this->termio, ICANON))
- this->m_cv.notify();
- break;
- default:
- buf.put(c);
- if (TERMIOS_LSET(this->termio, ECHO))
- this->_echo_char(c);
- break;
- }
- }
- void tty::_echo_char(int c)
- {
- // ECHOCTL
- do {
- if (c < 0 || c >= 32 || !TERMIOS_LSET(this->termio, ECHO | ECHOCTL | IEXTEN))
- break;
- if (c == '\t' || c == '\n' || c == CTRL('Q') || c == CTRL('S'))
- break;
- this->show_char('^');
- this->show_char(c + 0x40);
- return;
- } while (false);
- this->show_char(c);
- }
- // do some ignore and remapping work
- // real commit operation is in _real_commit_char()
- void tty::commit_char(int c)
- {
- // check special control characters
- // if handled, the character is discarded
- if (TERMIOS_LSET(this->termio, ISIG)) {
- if (TERMIOS_TESTCC(c, this->termio, VINTR)) {
- if (!TERMIOS_LSET(this->termio, NOFLSH))
- this->clear_read_buf();
- this->_echo_char(c);
- procs->send_signal_grp(fg_pgroup, SIGINT);
- return;
- }
- if (TERMIOS_TESTCC(c, this->termio, VSUSP)) {
- if (!TERMIOS_LSET(this->termio, NOFLSH))
- this->clear_read_buf();
- this->_echo_char(c);
- procs->send_signal_grp(fg_pgroup, SIGTSTP);
- return;
- }
- if (TERMIOS_TESTCC(c, this->termio, VQUIT)) {
- if (!TERMIOS_LSET(this->termio, NOFLSH))
- this->clear_read_buf();
- this->_echo_char(c);
- procs->send_signal_grp(fg_pgroup, SIGQUIT);
- return;
- }
- }
- // if handled, the character is discarded
- if (TERMIOS_LSET(this->termio, ICANON)) {
- if (TERMIOS_TESTCC(c, this->termio, VEOF)) {
- this->m_cv.notify();
- return;
- }
- if (TERMIOS_TESTCC(c, this->termio, VKILL)) {
- if (TERMIOS_LSET(this->termio, ECHOKE | IEXTEN)) {
- while (this->_do_erase(true) != -1)
- ;
- }
- else if (TERMIOS_LSET(this->termio, ECHOK)) {
- while (this->_do_erase(false) != -1)
- ;
- this->show_char('\n');
- }
- return;
- }
- if (TERMIOS_TESTCC(c, this->termio, VERASE)) {
- this->_do_erase(true);
- return;
- }
- }
- switch (c) {
- case '\r':
- if (TERMIOS_ISET(this->termio, IGNCR))
- break;
- if (TERMIOS_ISET(this->termio, ICRNL)) {
- this->_real_commit_char('\n');
- break;
- }
- this->_real_commit_char('\r');
- break;
- case '\n':
- if (TERMIOS_ISET(this->termio, INLCR)) {
- this->_real_commit_char('\r');
- break;
- }
- this->_real_commit_char('\n');
- break;
- default:
- this->_real_commit_char(c);
- break;
- }
- }
- void tty::show_char(int c)
- {
- this->putchar(c);
- }
- vga_tty::vga_tty()
- {
- snprintf(this->name, sizeof(this->name), "ttyVGA");
- }
- serial_tty::serial_tty(int id)
- : id(id)
- {
- snprintf(this->name, sizeof(this->name), "ttyS%x", (int)id);
- }
- void serial_tty::putchar(char c)
- {
- serial_send_data(id, c);
- }
- void vga_tty::putchar(char c)
- {
- static struct vga_char vc = { .c = '\0', .color = VGA_CHAR_COLOR_WHITE };
- vc.c = c;
- vga_put_char(&vc);
- }
- void tty::clear_read_buf(void)
- {
- this->buf.clear();
- }
|