diff --git a/apps/getty.c b/apps/getty.c index 68875c0c..10a24619 100644 --- a/apps/getty.c +++ b/apps/getty.c @@ -14,6 +14,7 @@ */ #include #include +#include #include #include #include @@ -41,6 +42,7 @@ int main(int argc, char * argv[]) { if (optind < argc) { file = argv[optind]; + optind++; } fd_serial = open(file, O_RDWR); @@ -56,6 +58,22 @@ int main(int argc, char * argv[]) { dup2(fd_serial, 2); system("stty sane"); + + if (optind < argc) { + if (*argv[optind] >= '0' && *argv[optind] <= '9' && strlen(argv[optind]) < 30) { + char tmp[100]; + snprintf(tmp, 100, "stty %s", argv[optind]); + system(tmp); + optind++; + } + } + + if (optind < argc) { + /* If there's still arguments, assume TERM value */ + setenv("TERM", argv[optind], 1); + optind++; + } + system("ttysize -q"); char * tokens[] = {"/bin/login",NULL,NULL,NULL}; diff --git a/apps/stty.c b/apps/stty.c index ee53ae46..7d2914c7 100644 --- a/apps/stty.c +++ b/apps/stty.c @@ -114,16 +114,67 @@ static int set_toggle_(tcflag_t * flag, const char * lbl, int base, int val, con #define set_ctoggle(lbl,base,val) if (set_toggle_(&(t.c_cflag), lbl, base, val, argv[i])) { i++; continue; } #define set_otoggle(lbl,base,val) if (set_toggle_(&(t.c_oflag), lbl, base, val, argv[i])) { i++; continue; } +static struct baud_table { + const char * as_str; + speed_t as_baud; +} baud_rates[] = { + { "0", B0 }, + { "50", B50 }, + { "75", B75 }, + { "110", B110 }, + { "134", B134 }, + { "134.5", B134 }, + { "150", B150 }, + { "200", B200 }, + { "300", B300 }, + { "600", B600 }, + { "1200", B1200 }, + { "1800", B1800 }, + { "2400", B2400 }, + { "4800", B4800 }, + { "9600", B9600 }, + { "19200", B19200 }, + { "38400", B38400 }, + { "57600", B57600 }, + { "115200", B115200 }, + { "230400", B230400 }, + { "460800", B460800 }, + { "921600", B921600 }, +}; + +static int set_baud_rate(struct termios * t, const char * arg) { + for (size_t j = 0; j < sizeof(baud_rates) / sizeof(*baud_rates); ++j) { + if (!strcmp(arg, baud_rates[j].as_str)) { + cfsetospeed(t, baud_rates[j].as_baud); + return 1; + } + } + return 0; +} + +static int print_baud_rate(struct termios * t) { + speed_t ospeed = cfgetospeed(t); + for (size_t j = 0; j < sizeof(baud_rates) / sizeof(*baud_rates); ++j) { + if (ospeed == baud_rates[j].as_baud) { + fprintf(stdout, "speed %s baud; ", baud_rates[j].as_str); + return 1; + } + } + return 0; +} + static int show_settings(int all) { + struct termios t; + tcgetattr(STDERR_FILENO, &t); + print_baud_rate(&t); + + /* Baud rate */ /* Size */ struct winsize w; ioctl(STDERR_FILENO, TIOCGWINSZ, &w); fprintf(stdout, "rows %d; columns %d; ypixels %d; xpixels %d;\n", w.ws_row, w.ws_col, w.ws_ypixel, w.ws_xpixel); printed = 0; - struct termios t; - tcgetattr(STDERR_FILENO, &t); - /* Keys */ print_cc(&t, "intr", VINTR, 3); print_cc(&t, "quit", VQUIT, 28); @@ -261,7 +312,7 @@ int main(int argc, char * argv[]) { t.c_iflag = ICRNL | BRKINT; t.c_oflag = ONLCR | OPOST; t.c_lflag = ECHO | ECHOE | ECHOK | ICANON | ISIG | IEXTEN; - t.c_cflag = CREAD | CS8; + t.c_cflag |= CREAD; t.c_cc[VEOF] = 4; /* ^D */ t.c_cc[VEOL] = 0; /* Not set */ t.c_cc[VERASE] = 0x7F; /* ^? */ @@ -287,6 +338,13 @@ int main(int argc, char * argv[]) { continue; } + if (*argv[i] >= '0' && *argv[i] < '9') { + if (set_baud_rate(&t, argv[i])) { + i++; + continue; + } + } + set_char("eof", VEOF); set_char("eol", VEOL); set_char("erase", VERASE); diff --git a/base/usr/include/sys/termios.h b/base/usr/include/sys/termios.h index eeb186a0..36a7d362 100644 --- a/base/usr/include/sys/termios.h +++ b/base/usr/include/sys/termios.h @@ -81,6 +81,7 @@ typedef unsigned char cc_t; #define VT1 0040000 /* baud rates */ +#define CBAUD 0100017 #define B0 0000000 #define B50 0000001 #define B75 0000002 @@ -97,6 +98,11 @@ typedef unsigned char cc_t; #define B9600 0000015 #define B19200 0000016 #define B38400 0000017 +#define B57600 0100000 +#define B115200 0100001 +#define B230400 0100002 +#define B460800 0100003 +#define B921600 0100004 /* control modes */ #define CSIZE 0000060 diff --git a/kernel/arch/x86_64/serial.c b/kernel/arch/x86_64/serial.c index 3f64e285..ccab8f58 100644 --- a/kernel/arch/x86_64/serial.c +++ b/kernel/arch/x86_64/serial.c @@ -30,21 +30,35 @@ #define SERIAL_IRQ_AC 4 #define SERIAL_IRQ_BD 3 -static pty_t * _serial_port_pty_a = NULL; -static pty_t * _serial_port_pty_b = NULL; -static pty_t * _serial_port_pty_c = NULL; -static pty_t * _serial_port_pty_d = NULL; +struct serial_port_map { + int port; + pty_t * pty; + int index; -static pty_t ** pty_for_port(int port) { + tcflag_t cflags; +}; + +static struct serial_port_map serial_ports[4] = { + { SERIAL_PORT_A, NULL, 0, 0 }, + { SERIAL_PORT_B, NULL, 1, 0 }, + { SERIAL_PORT_C, NULL, 2, 0 }, + { SERIAL_PORT_D, NULL, 3, 0 }, +}; + +static struct serial_port_map * map_entry_for_port(int port) { switch (port) { - case SERIAL_PORT_A: return &_serial_port_pty_a; - case SERIAL_PORT_B: return &_serial_port_pty_b; - case SERIAL_PORT_C: return &_serial_port_pty_c; - case SERIAL_PORT_D: return &_serial_port_pty_d; + case SERIAL_PORT_A: return &serial_ports[0]; + case SERIAL_PORT_B: return &serial_ports[1]; + case SERIAL_PORT_C: return &serial_ports[2]; + case SERIAL_PORT_D: return &serial_ports[3]; } __builtin_unreachable(); } +static pty_t ** pty_for_port(int port) { + return &map_entry_for_port(port)->pty; +} + static int serial_rcvd(int device) { return inportb(device + 5) & 1; } @@ -109,12 +123,51 @@ int serial_handler_bd(struct regs *r) { return 1; } -static void serial_enable(int port) { +#define BASE 115200 +#define D(n) { B ## n, BASE / n } +static struct divisor { + speed_t baud; + uint16_t div; +} divisors[] = { + { B0, 0 }, + D(50), D(75), D(110), + { B134, BASE * 10 / 1345 }, + D(150), D(200), D(300), D(600), D(1200), + D(1800), D(2400), D(4800), D(9600), D(19200), + D(38400), D(57600), D(115200), +}; +#undef D + +static void serial_enable(int port, tcflag_t cflags) { outportb(port + 1, 0x00); /* Disable interrupts */ outportb(port + 3, 0x80); /* Enable divisor mode */ - outportb(port + 0, 0x01); /* Div Low: 01 Set the port to 115200 bps */ - outportb(port + 1, 0x00); /* Div High: 00 */ - outportb(port + 3, 0x03); /* Disable divisor mode, set parity */ + + uint16_t divisor = 0; + for (size_t i = 0; i < sizeof(divisors) / sizeof(*divisors); ++i) { + if ((cflags & CBAUD) == divisors[i].baud) { + divisor = divisors[i].div; + break; + } + } + + outportb(port + 0, divisor & 0xFF); /* Div Low */ + outportb(port + 1, divisor >> 8); /* Div High */ + + uint8_t line_ctl = 0; + if (cflags & PARENB) { + line_ctl |= (1 << 3); /* Enable parity */ + if (!(cflags & PARODD)) line_ctl |= (1 << 4); /* Even parity */ + } + + /* Size */ + switch (cflags & CSIZE) { + case CS5: line_ctl |= 0; break; + case CS6: line_ctl |= 1; break; + case CS7: line_ctl |= 2; break; + case CS8: line_ctl |= 3; break; + } + + outportb(port + 3, line_ctl); /* set line mode */ outportb(port + 2, 0xC7); /* Enable FIFO and clear */ outportb(port + 4, 0x0B); /* Enable interrupts */ outportb(port + 1, 0x01); /* Enable interrupts */ @@ -124,32 +177,30 @@ static int have_installed_ac = 0; static int have_installed_bd = 0; static void serial_write_out(pty_t * pty, uint8_t c) { - if (pty == _serial_port_pty_a) serial_send(SERIAL_PORT_A, c); - if (pty == _serial_port_pty_b) serial_send(SERIAL_PORT_B, c); - if (pty == _serial_port_pty_c) serial_send(SERIAL_PORT_C, c); - if (pty == _serial_port_pty_d) serial_send(SERIAL_PORT_D, c); + struct serial_port_map * me = pty->_private; + if (pty->tios.c_cflag != me->cflags) { + me->cflags = pty->tios.c_cflag; + serial_enable(me->port, pty->tios.c_cflag); + } + serial_send(me->port, c); } -#define DEV_PATH "/dev/" -#define TTY_A "ttyS0" -#define TTY_B "ttyS1" -#define TTY_C "ttyS2" -#define TTY_D "ttyS3" +#define DEV_PATH "/dev/ttyS" static void serial_fill_name(pty_t * pty, char * name) { - if (pty == _serial_port_pty_a) snprintf(name, 100, DEV_PATH TTY_A); - if (pty == _serial_port_pty_b) snprintf(name, 100, DEV_PATH TTY_B); - if (pty == _serial_port_pty_c) snprintf(name, 100, DEV_PATH TTY_C); - if (pty == _serial_port_pty_d) snprintf(name, 100, DEV_PATH TTY_D); + snprintf(name, 100, DEV_PATH "%d", ((struct serial_port_map *)pty->_private)->index); } static fs_node_t * serial_device_create(int port) { pty_t * pty = pty_new(NULL, 0); - *pty_for_port(port) = pty; + + map_entry_for_port(port)->pty = pty; + pty->_private = map_entry_for_port(port); + pty->write_out = serial_write_out; pty->fill_name = serial_fill_name; - serial_enable(port); + serial_enable(port, pty->tios.c_cflag); if (port == SERIAL_PORT_A || port == SERIAL_PORT_C) { if (!have_installed_ac) { @@ -173,8 +224,8 @@ void serial_initialize(void) { serial_ac_handler = spawn_worker_thread(process_serial, "[serial ac]", (void*)(uintptr_t)SERIAL_PORT_A); serial_bd_handler = spawn_worker_thread(process_serial, "[serial bd]", (void*)(uintptr_t)SERIAL_PORT_B); - fs_node_t * ttyS0 = serial_device_create(SERIAL_PORT_A); vfs_mount(DEV_PATH TTY_A, ttyS0); - fs_node_t * ttyS1 = serial_device_create(SERIAL_PORT_B); vfs_mount(DEV_PATH TTY_B, ttyS1); - fs_node_t * ttyS2 = serial_device_create(SERIAL_PORT_C); vfs_mount(DEV_PATH TTY_C, ttyS2); - fs_node_t * ttyS3 = serial_device_create(SERIAL_PORT_D); vfs_mount(DEV_PATH TTY_D, ttyS3); + fs_node_t * ttyS0 = serial_device_create(SERIAL_PORT_A); vfs_mount(DEV_PATH "0", ttyS0); + fs_node_t * ttyS1 = serial_device_create(SERIAL_PORT_B); vfs_mount(DEV_PATH "1", ttyS1); + fs_node_t * ttyS2 = serial_device_create(SERIAL_PORT_C); vfs_mount(DEV_PATH "2", ttyS2); + fs_node_t * ttyS3 = serial_device_create(SERIAL_PORT_D); vfs_mount(DEV_PATH "3", ttyS3); } diff --git a/kernel/vfs/tty.c b/kernel/vfs/tty.c index 362c7d28..a9080185 100644 --- a/kernel/vfs/tty.c +++ b/kernel/vfs/tty.c @@ -702,7 +702,7 @@ pty_t * pty_new(struct winsize * size, int index) { pty->tios.c_iflag = ICRNL | BRKINT; pty->tios.c_oflag = ONLCR | OPOST; pty->tios.c_lflag = ECHO | ECHOE | ECHOK | ICANON | ISIG | IEXTEN; - pty->tios.c_cflag = CREAD | CS8; + pty->tios.c_cflag = CREAD | CS8 | B38400; pty->tios.c_cc[VEOF] = 4; /* ^D */ pty->tios.c_cc[VEOL] = 0; /* Not set */ pty->tios.c_cc[VERASE] = 0x7f; /* ^? */ diff --git a/libc/ioctl/ioctl.c b/libc/ioctl/ioctl.c index f56ed04d..91365c69 100644 --- a/libc/ioctl/ioctl.c +++ b/libc/ioctl/ioctl.c @@ -15,7 +15,7 @@ speed_t cfgetispeed(const struct termios * tio) { return 0; } speed_t cfgetospeed(const struct termios * tio) { - return 0; + return tio->c_cflag & CBAUD; } int cfsetispeed(struct termios * tio, speed_t speed) { @@ -24,6 +24,7 @@ int cfsetispeed(struct termios * tio, speed_t speed) { } int cfsetospeed(struct termios * tio, speed_t speed) { + tio->c_cflag = (tio->c_cflag & ~CBAUD) | (speed & CBAUD); return 0; }