pty: initial support for setting line control

This commit is contained in:
K. Lange 2023-10-08 14:52:34 +09:00
parent 48dcf55a5d
commit bda8c8d67b
6 changed files with 172 additions and 38 deletions

View File

@ -14,6 +14,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pty.h>
@ -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};

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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; /* ^? */

View File

@ -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;
}