pty: initial support for setting line control
This commit is contained in:
parent
48dcf55a5d
commit
bda8c8d67b
18
apps/getty.c
18
apps/getty.c
@ -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};
|
||||
|
66
apps/stty.c
66
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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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; /* ^? */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user