toaruos/kernel/fs/tty.c

384 lines
9.3 KiB
C
Raw Normal View History

2013-03-18 03:34:23 +04:00
#include <system.h>
#include <fs.h>
#include <pipe.h>
#include <logging.h>
2014-04-06 03:36:17 +04:00
#include <printf.h>
2013-03-18 03:34:23 +04:00
#include <ioctl.h>
2013-03-18 11:52:12 +04:00
#include <termios.h>
2013-04-03 10:02:43 +04:00
#include <ringbuffer.h>
2013-03-18 11:52:12 +04:00
2013-03-18 03:34:23 +04:00
#define TTY_BUFFER_SIZE 512
typedef struct pty {
2013-03-18 11:52:12 +04:00
/* the PTY number */
2013-03-18 03:34:23 +04:00
int name;
2013-03-18 11:52:12 +04:00
/* Master and slave endpoints */
2013-03-18 03:34:23 +04:00
fs_node_t * master;
fs_node_t * slave;
2013-03-18 11:52:12 +04:00
/* term io "window size" struct (width/height) */
2013-03-18 03:34:23 +04:00
struct winsize size;
2013-03-18 11:52:12 +04:00
/* termios data structure */
struct termios tios;
/* directional pipes */
2013-04-03 10:02:43 +04:00
ring_buffer_t * in;
ring_buffer_t * out;
2013-03-18 11:52:12 +04:00
char * canon_buffer;
size_t canon_bufsize;
size_t canon_buflen;
2013-03-18 11:52:12 +04:00
pid_t ct_proc; /* Controlling process (shell) */
pid_t fg_proc; /* Foreground process (might also be shell) */
2013-03-18 03:34:23 +04:00
} pty_t;
list_t * pty_list = NULL;
#define IN(character) ring_buffer_write(pty->in, 1, (uint8_t *)&(character))
#define OUT(character) ring_buffer_write(pty->out, 1, (uint8_t *)&(character))
static void dump_input_buffer(pty_t * pty) {
char * c = pty->canon_buffer;
while (pty->canon_buflen > 0) {
IN(*c);
pty->canon_buflen--;
c++;
}
}
static void clear_input_buffer(pty_t * pty) {
pty->canon_buflen = 0;
pty->canon_buffer[0] = '\0';
}
static void output_process(pty_t * pty, uint8_t c) {
if (c == '\n' && (pty->tios.c_oflag & ONLCR)) {
uint8_t d = '\r';
OUT(d);
}
OUT(c);
}
static void input_process(pty_t * pty, uint8_t c) {
if (pty->tios.c_lflag & ICANON) {
debug_print(INFO, "Processing for character %d in canon mode", c);
if (c == pty->tios.c_cc[VKILL]) {
while (pty->canon_buflen > 0) {
pty->canon_buflen--;
pty->canon_buffer[pty->canon_buflen] = '\0';
if (pty->tios.c_lflag & ECHO) {
output_process(pty, '\010');
output_process(pty, ' ');
output_process(pty, '\010');
}
}
return;
}
if (c == pty->tios.c_cc[VERASE]) {
/* Backspace */
if (pty->canon_buflen > 0) {
pty->canon_buflen--;
pty->canon_buffer[pty->canon_buflen] = '\0';
if (pty->tios.c_lflag & ECHO) {
output_process(pty, '\010');
output_process(pty, ' ');
output_process(pty, '\010');
}
}
return;
}
if (c == pty->tios.c_cc[VINTR]) {
if (pty->tios.c_lflag & ECHO) {
output_process(pty, '^');
output_process(pty, '@' + c);
output_process(pty, '\n');
}
clear_input_buffer(pty);
if (pty->fg_proc) {
send_signal(pty->fg_proc, SIGINT);
}
return;
}
if (c == pty->tios.c_cc[VQUIT]) {
if (pty->tios.c_lflag & ECHO) {
output_process(pty, '^');
output_process(pty, '@' + c);
output_process(pty, '\n');
}
clear_input_buffer(pty);
if (pty->fg_proc) {
send_signal(pty->fg_proc, SIGQUIT);
}
return;
}
pty->canon_buffer[pty->canon_buflen] = c;
if (pty->tios.c_lflag & ECHO) {
output_process(pty, c);
}
if (pty->canon_buffer[pty->canon_buflen] == '\n') {
pty->canon_buflen++;
dump_input_buffer(pty);
return;
}
if (pty->canon_buflen == pty->canon_bufsize) {
dump_input_buffer(pty);
return;
}
pty->canon_buflen++;
return;
} else if (pty->tios.c_lflag & ECHO) {
output_process(pty, c);
}
IN(c);
}
2013-03-19 10:57:40 +04:00
int pty_ioctl(pty_t * pty, int request, void * argp) {
2014-03-16 06:48:51 +04:00
debug_print(INFO, "Incoming IOCTL request %d", request);
2013-03-19 10:57:40 +04:00
switch (request) {
case IOCTLDTYPE:
/*
* This is a special toaru-specific call to get a simple
* integer that describes the kind of device this is.
* It's more specific than just "character device" or "file",
* but for here we just need to say we're a TTY.
*/
return IOCTL_DTYPE_TTY;
2013-03-19 10:57:40 +04:00
case TIOCSWINSZ:
2013-04-03 05:06:06 +04:00
if (!argp) return -1;
validate(argp);
2013-03-19 10:57:40 +04:00
memcpy(&pty->size, argp, sizeof(struct winsize));
/* TODO send sigwinch to fg_prog */
return 0;
case TIOCGWINSZ:
2013-04-03 05:06:06 +04:00
if (!argp) return -1;
validate(argp);
2013-03-19 10:57:40 +04:00
memcpy(argp, &pty->size, sizeof(struct winsize));
return 0;
case TCGETS:
if (!argp) return -1;
validate(argp);
memcpy(argp, &pty->tios, sizeof(struct termios));
return 0;
case TIOCSPGRP:
if (!argp) return -1;
validate(argp);
pty->fg_proc = *(pid_t *)argp;
debug_print(NOTICE, "Setting PTY group to %d", pty->fg_proc);
return 0;
case TIOCGPGRP:
if (!argp) return -1;
validate(argp);
*(pid_t *)argp = pty->fg_proc;
return 0;
case TCSETS:
case TCSETSW:
case TCSETSF:
if (!argp) return -1;
validate(argp);
if (!(((struct termios *)argp)->c_lflag & ICANON) && (pty->tios.c_lflag & ICANON)) {
/* Switch out of canonical mode, the dump the input buffer */
dump_input_buffer(pty);
}
memcpy(&pty->tios, argp, sizeof(struct termios));
return 0;
2013-03-19 10:57:40 +04:00
default:
return -1; /* TODO EINV... something or other */
}
return -1;
}
#define MIN(a,b) ((a) < (b) ? (a) : (b))
2013-03-18 03:34:23 +04:00
uint32_t read_pty_master(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
2013-03-26 08:48:16 +04:00
pty_t * pty = (pty_t *)node->device;
2013-03-18 03:34:23 +04:00
/* Standard pipe read */
2013-04-03 10:02:43 +04:00
return ring_buffer_read(pty->out, size, buffer);
2013-03-18 03:34:23 +04:00
}
uint32_t write_pty_master(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
2013-03-26 08:48:16 +04:00
pty_t * pty = (pty_t *)node->device;
2013-03-18 03:34:23 +04:00
size_t l = 0;
for (uint8_t * c = buffer; l < size; ++c, ++l) {
input_process(pty, *c);
}
return l;
2013-03-18 03:34:23 +04:00
}
void open_pty_master(fs_node_t * node, unsigned int flags) {
2013-03-18 03:34:23 +04:00
return;
}
void close_pty_master(fs_node_t * node) {
return;
}
uint32_t read_pty_slave(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
2013-03-26 08:48:16 +04:00
pty_t * pty = (pty_t *)node->device;
2013-03-18 03:34:23 +04:00
if (pty->tios.c_lflag & ICANON) {
return ring_buffer_read(pty->in, size, buffer);
} else {
if (pty->tios.c_cc[VMIN] == 0) {
return ring_buffer_read(pty->in, MIN(size, ring_buffer_unread(pty->in)), buffer);
} else {
return ring_buffer_read(pty->in, MIN(pty->tios.c_cc[VMIN], size), buffer);
}
}
2013-03-18 03:34:23 +04:00
}
2013-03-18 03:34:23 +04:00
uint32_t write_pty_slave(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
2013-03-26 08:48:16 +04:00
pty_t * pty = (pty_t *)node->device;
2013-03-18 03:34:23 +04:00
size_t l = 0;
for (uint8_t * c = buffer; l < size; ++c, ++l) {
output_process(pty, *c);
}
return l;
2013-03-18 03:34:23 +04:00
}
void open_pty_slave(fs_node_t * node, unsigned int flags) {
2013-03-18 03:34:23 +04:00
return;
}
void close_pty_slave(fs_node_t * node) {
return;
}
2013-03-19 10:57:40 +04:00
/*
* These are separate functions just in case I ever feel the need to do
* things differently in the slave or master.
*/
int ioctl_pty_master(fs_node_t * node, int request, void * argp) {
2013-03-26 08:48:16 +04:00
pty_t * pty = (pty_t *)node->device;
2013-03-19 10:57:40 +04:00
return pty_ioctl(pty, request, argp);
}
int ioctl_pty_slave(fs_node_t * node, int request, void * argp) {
2013-03-26 08:48:16 +04:00
pty_t * pty = (pty_t *)node->device;
2013-03-19 10:57:40 +04:00
return pty_ioctl(pty, request, argp);
}
2013-03-22 22:58:22 +04:00
int pty_available_input(fs_node_t * node) {
2013-03-26 08:48:16 +04:00
pty_t * pty = (pty_t *)node->device;
2013-04-03 10:02:43 +04:00
return ring_buffer_unread(pty->in);
2013-03-22 22:58:22 +04:00
}
int pty_available_output(fs_node_t * node) {
2013-03-26 08:48:16 +04:00
pty_t * pty = (pty_t *)node->device;
2013-04-03 10:02:43 +04:00
return ring_buffer_unread(pty->out);
2013-03-22 22:58:22 +04:00
}
2013-03-18 03:34:23 +04:00
fs_node_t * pty_master_create(pty_t * pty) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
memset(fnode, 0x00, sizeof(fs_node_t));
fnode->name[0] = '\0';
sprintf(fnode->name, "pty master");
fnode->uid = 0;
fnode->gid = 0;
fnode->flags = FS_PIPE;
fnode->read = read_pty_master;
fnode->write = write_pty_master;
fnode->open = open_pty_master;
fnode->close = close_pty_master;
fnode->readdir = NULL;
fnode->finddir = NULL;
2013-03-19 10:57:40 +04:00
fnode->ioctl = ioctl_pty_master;
2013-03-22 22:58:22 +04:00
fnode->get_size = pty_available_output;
2013-03-18 03:34:23 +04:00
2013-03-26 08:48:16 +04:00
fnode->device = pty;
2013-03-18 03:34:23 +04:00
return fnode;
}
fs_node_t * pty_slave_create(pty_t * pty) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
memset(fnode, 0x00, sizeof(fs_node_t));
fnode->name[0] = '\0';
sprintf(fnode->name, "pty slave");
fnode->uid = 0;
fnode->gid = 0;
fnode->flags = FS_PIPE;
fnode->read = read_pty_slave;
fnode->write = write_pty_slave;
fnode->open = open_pty_slave;
fnode->close = close_pty_slave;
fnode->readdir = NULL;
fnode->finddir = NULL;
2013-03-19 10:57:40 +04:00
fnode->ioctl = ioctl_pty_slave;
2013-03-22 22:58:22 +04:00
fnode->get_size = pty_available_input;
2013-03-18 03:34:23 +04:00
2013-03-26 08:48:16 +04:00
fnode->device = pty;
2013-03-18 03:34:23 +04:00
return fnode;
}
void pty_install(void) {
pty_list = list_create();
}
pty_t * pty_new(struct winsize * size) {
pty_t * pty = malloc(sizeof(pty_t));
2013-03-18 11:52:12 +04:00
/* stdin linkage; characters from terminal → PTY slave */
2013-04-03 10:02:43 +04:00
pty->in = ring_buffer_create(TTY_BUFFER_SIZE);
pty->out = ring_buffer_create(TTY_BUFFER_SIZE);
2013-03-18 03:34:23 +04:00
2013-03-18 11:52:12 +04:00
/* Master endpoint - writes go to stdin, reads come from stdout */
2013-03-18 03:34:23 +04:00
pty->master = pty_master_create(pty);
2013-03-18 11:52:12 +04:00
/* Slave endpoint, reads come from stdin, writes go to stdout */
2013-03-18 03:34:23 +04:00
pty->slave = pty_slave_create(pty);
2013-03-18 11:52:12 +04:00
/* TODO PTY name */
pty->name = 0;
2013-03-18 03:34:23 +04:00
if (size) {
memcpy(&pty->size, size, sizeof(struct winsize));
} else {
/* Sane defaults */
2013-03-18 11:52:12 +04:00
pty->size.ws_row = 25;
pty->size.ws_col = 80;
2013-03-18 03:34:23 +04:00
}
2013-03-18 11:52:12 +04:00
/* Controlling and foreground processes are set to 0 by default */
pty->ct_proc = 0;
pty->fg_proc = 0;
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;
pty->tios.c_cc[VEOF] = 4; /* ^D */
pty->tios.c_cc[VEOL] = 0; /* Not set */
pty->tios.c_cc[VERASE] = '\b';
pty->tios.c_cc[VINTR] = 3; /* ^C */
pty->tios.c_cc[VKILL] = 21; /* ^U */
pty->tios.c_cc[VMIN] = 1;
pty->tios.c_cc[VQUIT] = 28; /* ^\ */
pty->tios.c_cc[VSTART] = 17; /* ^Q */
pty->tios.c_cc[VSTOP] = 19; /* ^S */
pty->tios.c_cc[VSUSP] = 26; /* ^Z */
pty->tios.c_cc[VTIME] = 0;
pty->canon_buffer = malloc(TTY_BUFFER_SIZE);
pty->canon_bufsize = TTY_BUFFER_SIZE;
pty->canon_buflen = 0;
2013-03-18 03:34:23 +04:00
return pty;
}
2014-05-03 05:47:41 +04:00
int pty_create(void *size, fs_node_t ** fs_master, fs_node_t ** fs_slave) {
2013-03-18 03:34:23 +04:00
pty_t * pty = pty_new(size);
2014-05-03 05:47:41 +04:00
*fs_master = pty->master;
*fs_slave = pty->slave;
2013-03-18 03:34:23 +04:00
return 0;
}