Proper line discipline for TTYs and simple line discipline for chardevs
This commit is contained in:
+2
-1
@@ -81,7 +81,8 @@ OBJS+=$(O)/sys/debug.o \
|
||||
$(O)/sys/vfs/vfs_access.o \
|
||||
$(O)/sys/time.o \
|
||||
$(O)/sys/thread.o \
|
||||
$(O)/sys/ctype.o
|
||||
$(O)/sys/ctype.o \
|
||||
$(O)/sys/line.o
|
||||
|
||||
# \
|
||||
$(O)/sys/vfs/pty.o \
|
||||
|
||||
@@ -4,6 +4,9 @@ struct winsize;
|
||||
|
||||
void amd64_con_get_size(struct winsize *ws);
|
||||
|
||||
// For tty
|
||||
int pc_con_putc(void *dev, char c);
|
||||
|
||||
void con_blink(void);
|
||||
void amd64_con_putc(int c);
|
||||
void amd64_con_init(void);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
struct chrdev;
|
||||
|
||||
void ps2_init(void);
|
||||
void ps2_register_device(void);
|
||||
void ps2_kbd_set_tty(struct chrdev *dev);
|
||||
|
||||
+9
-3
@@ -1,19 +1,25 @@
|
||||
#pragma once
|
||||
#include "sys/termios.h"
|
||||
#include "sys/types.h"
|
||||
#include "sys/ring.h"
|
||||
|
||||
struct chrdev {
|
||||
void *dev_data;
|
||||
|
||||
struct ring buffer;
|
||||
enum {
|
||||
CHRDEV_GENERIC = 0,
|
||||
CHRDEV_TTY
|
||||
} type;
|
||||
|
||||
// TODO: move tty info somewhere else?
|
||||
struct termios tc;
|
||||
|
||||
int (*ioctl) (struct chrdev *chr, unsigned int cmd, void *arg);
|
||||
ssize_t (*write) (struct chrdev *chr, const void *buf, size_t pos, size_t lim);
|
||||
ssize_t (*read) (struct chrdev *chr, void *buf, size_t pos, size_t lim);
|
||||
};
|
||||
|
||||
// Generic ring-buffer reader
|
||||
ssize_t chr_read_ring(struct chrdev *chr, void *buf, size_t pos, size_t lim);
|
||||
|
||||
int chr_ioctl(struct chrdev *chr, unsigned int cmd, void *arg);
|
||||
ssize_t chr_write(struct chrdev *chr, const void *buf, size_t pos, size_t lim);
|
||||
ssize_t chr_read(struct chrdev *chr, void *buf, size_t pos, size_t lim);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
struct chrdev;
|
||||
|
||||
// Common TTY line discipline
|
||||
ssize_t line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim);
|
||||
|
||||
// Simple line discipline - no control, disregard termios, spit out data right after
|
||||
// a single byte is available
|
||||
ssize_t simple_line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim);
|
||||
|
||||
// TODO: invent some magic for devices like /dev/urandom and /dev/zero etc.
|
||||
// they're implemented as block devices right now
|
||||
+16
-11
@@ -2,19 +2,24 @@
|
||||
#include "sys/types.h"
|
||||
#include "sys/spin.h"
|
||||
|
||||
struct thread;
|
||||
|
||||
#define RING_SIGNAL_BRK (1 << 0)
|
||||
#define RING_SIGNAL_EOF (1 << 1)
|
||||
|
||||
// Ring buffer
|
||||
struct ring {
|
||||
char *data;
|
||||
size_t wrptr;
|
||||
size_t rdptr;
|
||||
size_t rd, wr;
|
||||
size_t cap;
|
||||
spin_t lock;
|
||||
int post;
|
||||
struct thread *listener;
|
||||
char *base;
|
||||
int flags;
|
||||
};
|
||||
|
||||
void ring_post(struct ring *b);
|
||||
size_t ring_avail(struct ring *b);
|
||||
ssize_t ring_read(struct thread *ctx, struct ring *b, void *buf, size_t lim);
|
||||
int ring_putc(struct thread *ctx, struct ring *b, char c);
|
||||
void ring_init(struct ring *b, size_t cap);
|
||||
int ring_readable(struct ring *b);
|
||||
int ring_getc(struct thread *ctx, struct ring *b, char *c, int err);
|
||||
|
||||
void ring_signal(struct ring *b, int type);
|
||||
int ring_putc(struct thread *ctx, struct ring *b, char c, int wait);
|
||||
int ring_write(struct thread *ctx, struct ring *b, const void *data, size_t count, int wait);
|
||||
|
||||
int ring_init(struct ring *b, size_t cap);
|
||||
|
||||
@@ -1,11 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#define TCGETS 0x5401
|
||||
#define TCSETS 0x5402
|
||||
#define TIOCSPGRP 0x5410
|
||||
#define TIOCGWINSZ 0x5413
|
||||
|
||||
// Output flags
|
||||
#define OPOST (1 << 0)
|
||||
#define ONLCR (1 << 2)
|
||||
#define OCRNL (1 << 4)
|
||||
#define ONLRET (1 << 6)
|
||||
|
||||
// Control flags
|
||||
#define ECHO (1 << 4) // Echo input characters
|
||||
#define ECHOE (1 << 5) // if ICANON, ERASE erases prec. character,
|
||||
// WERASE erases prec. word
|
||||
#define ECHOK (1 << 6) // if ICANON, KILL character erases the line
|
||||
#define ECHONL (1 << 8) // if ICANON, echo newline (even if no ECHO)
|
||||
|
||||
#define TERMIOS_DEFAULT \
|
||||
{ \
|
||||
.c_iflag = 0, \
|
||||
.c_oflag = OPOST, \
|
||||
.c_cflag = 0, \
|
||||
.c_lflag = ECHO | ECHONL | ECHOE | ECHOK, \
|
||||
}
|
||||
|
||||
typedef unsigned int tcflag_t;
|
||||
|
||||
struct winsize {
|
||||
unsigned short ws_row;
|
||||
unsigned short ws_col;
|
||||
unsigned short ws_xpixel;
|
||||
unsigned short ws_ypixel;
|
||||
};
|
||||
|
||||
struct termios {
|
||||
tcflag_t c_iflag;
|
||||
tcflag_t c_oflag;
|
||||
tcflag_t c_cflag;
|
||||
tcflag_t c_lflag;
|
||||
};
|
||||
|
||||
#define TCSANOW 0
|
||||
|
||||
+9
-3
@@ -1,6 +1,12 @@
|
||||
#pragma once
|
||||
#include "sys/fs/node.h"
|
||||
struct chrdev;
|
||||
|
||||
// From keyboard handlers and stuff
|
||||
void tty_control_write(struct chrdev *n, char c);
|
||||
void tty_data_write(struct chrdev *n, char c);
|
||||
|
||||
// From code directly to the screen
|
||||
void tty_puts(struct chrdev *n, const char *s);
|
||||
void tty_putc(struct chrdev *n, char c);
|
||||
|
||||
void tty_control_write(int n, char c);
|
||||
void tty_buffer_write(int n, char c);
|
||||
void tty_init(void);
|
||||
|
||||
@@ -361,6 +361,11 @@ void amd64_con_putc(int c) {
|
||||
}
|
||||
}
|
||||
|
||||
int pc_con_putc(void *dev, char c) {
|
||||
amd64_con_putc(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void amd64_con_get_size(struct winsize *ws) {
|
||||
ws->ws_row = con_height;
|
||||
ws->ws_col = con_width;
|
||||
|
||||
+46
-48
@@ -5,6 +5,7 @@
|
||||
#include "sys/ctype.h"
|
||||
#include "sys/signum.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/line.h"
|
||||
#include "sys/tty.h"
|
||||
#include "sys/chr.h"
|
||||
#include "sys/dev.h"
|
||||
@@ -31,13 +32,15 @@
|
||||
|
||||
// Controller stuff
|
||||
|
||||
// TTY owning the keyboard
|
||||
static struct chrdev *_keyboard_tty = NULL;
|
||||
static uint32_t ps2_kbd_mods = 0;
|
||||
static uint32_t ps2_aux_state = 0;
|
||||
|
||||
static struct chrdev _ps2_mouse = {
|
||||
.dev_data = NULL,
|
||||
.write = NULL,
|
||||
.read = chr_read_ring,
|
||||
.read = simple_line_read,
|
||||
.ioctl = NULL
|
||||
};
|
||||
|
||||
@@ -73,6 +76,10 @@ static const char ps2_key_table_1[128] = {
|
||||
[0x7F] = 0
|
||||
};
|
||||
|
||||
void ps2_kbd_set_tty(struct chrdev *dev) {
|
||||
_keyboard_tty = dev;
|
||||
}
|
||||
|
||||
uint32_t ps2_irq_keyboard(void *ctx) {
|
||||
uint8_t st = inb(0x64);
|
||||
|
||||
@@ -99,43 +106,48 @@ uint32_t ps2_irq_keyboard(void *ctx) {
|
||||
ps2_kbd_mods ^= PS2_MOD_CAPS;
|
||||
}
|
||||
|
||||
if (!_keyboard_tty) {
|
||||
// No TTY to send strokes to
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (!(key & 0x80) && !(ps2_kbd_mods & PS2_MOD_CTRL)) {
|
||||
// Special keys
|
||||
switch (key) {
|
||||
case 0x01:
|
||||
tty_buffer_write(0, '\033');
|
||||
tty_data_write(_keyboard_tty, '\033');
|
||||
return IRQ_HANDLED;
|
||||
case 0x47:
|
||||
tty_buffer_write(0, '\033');
|
||||
tty_buffer_write(0, '[');
|
||||
tty_buffer_write(0, '[');
|
||||
tty_buffer_write(0, 'H');
|
||||
tty_data_write(_keyboard_tty, '\033');
|
||||
tty_data_write(_keyboard_tty, '[');
|
||||
tty_data_write(_keyboard_tty, '[');
|
||||
tty_data_write(_keyboard_tty, 'H');
|
||||
return IRQ_HANDLED;
|
||||
case 0x48:
|
||||
tty_buffer_write(0, '\033');
|
||||
tty_buffer_write(0, '[');
|
||||
tty_buffer_write(0, 'A');
|
||||
tty_data_write(_keyboard_tty, '\033');
|
||||
tty_data_write(_keyboard_tty, '[');
|
||||
tty_data_write(_keyboard_tty, 'A');
|
||||
return IRQ_HANDLED;
|
||||
case 0x50:
|
||||
tty_buffer_write(0, '\033');
|
||||
tty_buffer_write(0, '[');
|
||||
tty_buffer_write(0, 'B');
|
||||
tty_data_write(_keyboard_tty, '\033');
|
||||
tty_data_write(_keyboard_tty, '[');
|
||||
tty_data_write(_keyboard_tty, 'B');
|
||||
return IRQ_HANDLED;
|
||||
case 0x4D:
|
||||
tty_buffer_write(0, '\033');
|
||||
tty_buffer_write(0, '[');
|
||||
tty_buffer_write(0, 'C');
|
||||
tty_data_write(_keyboard_tty, '\033');
|
||||
tty_data_write(_keyboard_tty, '[');
|
||||
tty_data_write(_keyboard_tty, 'C');
|
||||
return IRQ_HANDLED;
|
||||
case 0x4F:
|
||||
tty_buffer_write(0, '\033');
|
||||
tty_buffer_write(0, '[');
|
||||
tty_buffer_write(0, '[');
|
||||
tty_buffer_write(0, 'F');
|
||||
tty_data_write(_keyboard_tty, '\033');
|
||||
tty_data_write(_keyboard_tty, '[');
|
||||
tty_data_write(_keyboard_tty, '[');
|
||||
tty_data_write(_keyboard_tty, 'F');
|
||||
return IRQ_HANDLED;
|
||||
case 0x4B:
|
||||
tty_buffer_write(0, '\033');
|
||||
tty_buffer_write(0, '[');
|
||||
tty_buffer_write(0, 'D');
|
||||
tty_data_write(_keyboard_tty, '\033');
|
||||
tty_data_write(_keyboard_tty, '[');
|
||||
tty_data_write(_keyboard_tty, 'D');
|
||||
return IRQ_HANDLED;
|
||||
default:
|
||||
break;
|
||||
@@ -152,15 +164,15 @@ uint32_t ps2_irq_keyboard(void *ctx) {
|
||||
}
|
||||
|
||||
if (key_char != 0) {
|
||||
tty_buffer_write(0, key_char);
|
||||
tty_data_write(_keyboard_tty, key_char);
|
||||
}
|
||||
} else {
|
||||
switch (key) {
|
||||
case 0x20: // ^D
|
||||
tty_control_write(0, 'd');
|
||||
tty_control_write(_keyboard_tty, 'd');
|
||||
break;
|
||||
case 0x2E: // ^C
|
||||
tty_control_write(0, 'c');
|
||||
tty_control_write(_keyboard_tty, 'c');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -179,6 +191,9 @@ uint32_t ps2_irq_mouse(void *arg) {
|
||||
packet[i] = inb(0x60);
|
||||
}
|
||||
|
||||
// TODO: ability to "hold" and "release" the buffer
|
||||
// so objects are written at least in one piece
|
||||
|
||||
int16_t dx, dy;
|
||||
dx = packet[1];
|
||||
dx -= (packet[0] << 4) & 0x100;
|
||||
@@ -186,30 +201,13 @@ uint32_t ps2_irq_mouse(void *arg) {
|
||||
dy = packet[2];
|
||||
dy -= (packet[0] << 3) & 0x100;
|
||||
|
||||
if ((packet[0] & PS2_AUX_BM) != (ps2_aux_state & PS2_AUX_BM)) {
|
||||
if (packet[0] & PS2_AUX_BM) {
|
||||
ring_putc(NULL, &_ps2_mouse.buffer, 'M');
|
||||
} else {
|
||||
ring_putc(NULL, &_ps2_mouse.buffer, 'm');
|
||||
}
|
||||
}
|
||||
if ((packet[0] & PS2_AUX_BR) != (ps2_aux_state & PS2_AUX_BR)) {
|
||||
if (packet[0] & PS2_AUX_BR) {
|
||||
ring_putc(NULL, &_ps2_mouse.buffer, 'R');
|
||||
} else {
|
||||
ring_putc(NULL, &_ps2_mouse.buffer, 'r');
|
||||
}
|
||||
}
|
||||
if ((packet[0] & PS2_AUX_BL) != (ps2_aux_state & PS2_AUX_BL)) {
|
||||
if (packet[0] & PS2_AUX_BL) {
|
||||
ring_putc(NULL, &_ps2_mouse.buffer, 'L');
|
||||
} else {
|
||||
ring_putc(NULL, &_ps2_mouse.buffer, 'l');
|
||||
}
|
||||
}
|
||||
if ((packet[0] & 7) != ps2_aux_state) {
|
||||
ring_post(&_ps2_mouse.buffer);
|
||||
if (dx || dy) {
|
||||
// Write "delta" event
|
||||
ring_putc(NULL, &_ps2_mouse.buffer, 'd', 0);
|
||||
ring_write(NULL, &_ps2_mouse.buffer, &dx, 2, 0);
|
||||
ring_write(NULL, &_ps2_mouse.buffer, &dy, 2, 0);
|
||||
}
|
||||
|
||||
ps2_aux_state = packet[0] & 7;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
||||
@@ -48,7 +48,7 @@ static uint32_t rs232_irq(void *ctx) {
|
||||
c = '\n';
|
||||
}
|
||||
|
||||
tty_buffer_write(0, c);
|
||||
// TODO: send data to bound TTY
|
||||
}
|
||||
|
||||
return has_data ? IRQ_HANDLED : IRQ_UNHANDLED;
|
||||
|
||||
@@ -269,7 +269,7 @@ int sys_select(int nfds, fd_set *rd, fd_set *wr, fd_set *exc, struct timeval *tv
|
||||
struct chrdev *chr = fd->vnode->dev;
|
||||
_assert(chr);
|
||||
|
||||
if (ring_avail(&chr->buffer) > 0) {
|
||||
if (ring_readable(&chr->buffer) > 0) {
|
||||
FD_SET(i, rd);
|
||||
res = 0;
|
||||
goto done;
|
||||
|
||||
@@ -34,40 +34,3 @@ ssize_t chr_read(struct chrdev *chr, void *buf, size_t off, size_t count) {
|
||||
}
|
||||
return chr->read(chr, buf, off, count);
|
||||
}
|
||||
|
||||
ssize_t chr_read_ring(struct chrdev *chr, void *buf, size_t off, size_t lim) {
|
||||
struct thread *thr = get_cpu()->thread;
|
||||
_assert(thr);
|
||||
char ibuf[16];
|
||||
|
||||
if (lim == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t rem = lim;
|
||||
size_t p = 0;
|
||||
|
||||
while (rem) {
|
||||
if (chr->buffer.post) {
|
||||
break;
|
||||
}
|
||||
ssize_t rd = ring_read(thr, &chr->buffer, ibuf, MIN(16, rem));
|
||||
if (rd <= 0) {
|
||||
// Interrupt or end of stream
|
||||
break;
|
||||
}
|
||||
memcpy((char *) buf + p, ibuf, rd);
|
||||
|
||||
rem -= rd;
|
||||
p += rd;
|
||||
|
||||
if (thr->flags & THREAD_INTERRUPTED) {
|
||||
thr->flags &= ~THREAD_INTERRUPTED;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
// Reset post bit
|
||||
chr->buffer.post = 0;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
#include "sys/ring.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/assert.h"
|
||||
#include "sys/chr.h"
|
||||
#include "sys/tty.h"
|
||||
#include "sys/amd64/cpu.h"
|
||||
|
||||
ssize_t line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim) {
|
||||
struct thread *thr = get_cpu()->thread;
|
||||
_assert(chr->type == CHRDEV_TTY);
|
||||
struct ring *r = &chr->buffer;
|
||||
|
||||
size_t rd = 0;
|
||||
size_t rem = lim;
|
||||
char *wr = buf;
|
||||
char c;
|
||||
|
||||
if (r->flags & RING_SIGNAL_EOF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NO ICANON YET
|
||||
// TODO: pass this through for non-canonical mode
|
||||
while (rem) {
|
||||
if (ring_getc(thr, r, &c, 0) < 0) {
|
||||
// TODO: make this optional via termios
|
||||
if (r->flags & RING_SIGNAL_EOF) {
|
||||
// ^D
|
||||
return rd;
|
||||
} else {
|
||||
// ^C
|
||||
// Reset the state
|
||||
rem = lim;
|
||||
wr = buf;
|
||||
rd = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ICANON
|
||||
if (c == '\033') {
|
||||
if (rem >= 2) {
|
||||
*wr++ = '^';
|
||||
*wr++ = '[';
|
||||
}
|
||||
rem -= 2;
|
||||
rd += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\b' && (chr->tc.c_lflag & ECHOE)) {
|
||||
if (rd) {
|
||||
tty_puts(chr, "\033[D \033[D");
|
||||
|
||||
--wr;
|
||||
++rem;
|
||||
--rd;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
*wr++ = c;
|
||||
++rd;
|
||||
--rem;
|
||||
|
||||
if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rd;
|
||||
}
|
||||
|
||||
ssize_t simple_line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim) {
|
||||
struct thread *thr = get_cpu()->thread;
|
||||
struct ring *r = &chr->buffer;
|
||||
|
||||
size_t rd = 0;
|
||||
size_t rem = lim;
|
||||
char *wr = buf;
|
||||
|
||||
// Non-TTYs don't support breaks/signals
|
||||
do {
|
||||
if (!ring_readable(r)) {
|
||||
asm volatile ("sti; hlt; cli");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
// TODO: properly handle interrupts
|
||||
while (ring_readable(r)) {
|
||||
if (ring_getc(thr, r, wr, 1) < 0) {
|
||||
break;
|
||||
}
|
||||
--rem;
|
||||
++wr;
|
||||
++rd;
|
||||
}
|
||||
|
||||
if (rd == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rd;
|
||||
}
|
||||
+76
-104
@@ -4,136 +4,108 @@
|
||||
#include "sys/ring.h"
|
||||
#include "sys/heap.h"
|
||||
|
||||
size_t ring_avail(struct ring *ring) {
|
||||
if (ring->rdptr == ring->wrptr) {
|
||||
int ring_readable(struct ring *ring) {
|
||||
// | . . . . R X X X W . . . |
|
||||
if (ring->rd <= ring->wr) {
|
||||
return ring->wr - ring->rd;
|
||||
// | X X W . . . . . . R X X |
|
||||
} else {
|
||||
return ring->wr + (ring->cap - ring->rd);
|
||||
}
|
||||
}
|
||||
|
||||
static int ring_writable(struct ring *ring) {
|
||||
if (ring->wr == ring->cap - 1 && ring->rd == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ring->rdptr > ring->wrptr) {
|
||||
return (ring->cap - ring->rdptr) + ring->wrptr;
|
||||
// | X X X X R . . . W X X X |
|
||||
if (ring->rd <= ring->wr) {
|
||||
return ring->wr + (ring->cap - ring->rd);
|
||||
// | . . W X X X X R . . . . |
|
||||
} else {
|
||||
return (ring->wrptr - ring->rdptr);
|
||||
if (ring->rd == ring->wr + 1) {
|
||||
return 0;
|
||||
}
|
||||
return ring->rd - ring->wr;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ring_writable(struct ring *ring) {
|
||||
if (ring->rdptr == ring->wrptr) {
|
||||
return ring->cap - 1;
|
||||
}
|
||||
|
||||
if (ring->rdptr > ring->wrptr) {
|
||||
return ring->rdptr - ring->wrptr - 1;
|
||||
static void ring_advance_read(struct ring *ring) {
|
||||
if (ring->rd == ring->cap - 1) {
|
||||
ring->rd = 0;
|
||||
} else {
|
||||
return (ring->cap - ring->wrptr) + ring->rdptr - 1;
|
||||
++ring->rd;
|
||||
}
|
||||
}
|
||||
|
||||
void ring_post(struct ring *ring) {
|
||||
ring->post = 1;
|
||||
}
|
||||
|
||||
static inline void ring_advance_read(struct ring *ring) {
|
||||
ring->rdptr++;
|
||||
if (ring->rdptr == ring->cap) {
|
||||
ring->rdptr = 0;
|
||||
static void ring_advance_write(struct ring *ring) {
|
||||
if (ring->wr == ring->cap - 1) {
|
||||
ring->wr = 0;
|
||||
} else {
|
||||
++ring->wr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ring_advance_write(struct ring *ring) {
|
||||
ring->wrptr++;
|
||||
if (ring->wrptr == ring->cap) {
|
||||
ring->wrptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ring_getc(struct thread *ctx, struct ring *ring, char *out) {
|
||||
uintptr_t flags;
|
||||
|
||||
//spin_lock_irqsave(&ring->lock, &flags);
|
||||
|
||||
do {
|
||||
if (ring_avail(ring)) {
|
||||
break;
|
||||
}
|
||||
//spin_release(&ring->lock);
|
||||
ctx->flags |= THREAD_WAITING;
|
||||
ctx->flags &= ~THREAD_INTERRUPTED;
|
||||
asm volatile ("sti; hlt");
|
||||
if (ring->post) {
|
||||
break;
|
||||
}
|
||||
if (ctx->flags & (THREAD_INTERRUPTED | THREAD_EOF)) {
|
||||
int ring_getc(struct thread *ctx, struct ring *ring, char *c, int err) {
|
||||
if (err) {
|
||||
if (!ring_readable(ring)) {
|
||||
return -1;
|
||||
}
|
||||
ctx->flags &= ~THREAD_WAITING;
|
||||
//spin_lock(&ring->lock);
|
||||
} while (1);
|
||||
} else {
|
||||
do {
|
||||
if (ring->flags & (RING_SIGNAL_BRK | RING_SIGNAL_EOF)) {
|
||||
ring->flags &= ~RING_SIGNAL_BRK;
|
||||
// TODO: send SIGINT on break
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ring_readable(ring)) {
|
||||
asm volatile ("sti; hlt; cli");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
*out = ring->data[ring->rdptr];
|
||||
*c = ring->base[ring->rd];
|
||||
ring_advance_read(ring);
|
||||
|
||||
//spin_release_irqrestore(&ring->lock, &flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t ring_read(struct thread *ctx, struct ring *ring, void *buf, size_t count) {
|
||||
size_t rem = count;
|
||||
size_t pos = 0;
|
||||
|
||||
while (rem) {
|
||||
if (ring->post) {
|
||||
break;
|
||||
int ring_putc(struct thread *ctx, struct ring *ring, char c, int wait) {
|
||||
if (wait) {
|
||||
while (!ring_writable(ring)) {
|
||||
asm volatile ("sti; hlt; cli");
|
||||
}
|
||||
|
||||
if (ring_getc(ctx, ring, (char *) buf + pos) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
--rem;
|
||||
++pos;
|
||||
}
|
||||
|
||||
if (pos == 0) {
|
||||
ring->base[ring->wr] = c;
|
||||
ring_advance_write(ring);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ring_write(struct thread *ctx, struct ring *ring, const void *buf, size_t len, int wait) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (ring_putc(ctx, ring, ((const char *) buf)[i], wait) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ring_signal(struct ring *r, int s) {
|
||||
r->flags |= s;
|
||||
}
|
||||
|
||||
int ring_init(struct ring *r, size_t cap) {
|
||||
r->cap = cap;
|
||||
r->rd = 0;
|
||||
r->wr = 0;
|
||||
r->flags = 0;
|
||||
if (!(r->base = kmalloc(cap))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ring_putc(struct thread *ctx, struct ring *ring, char c) {
|
||||
uintptr_t flags;
|
||||
|
||||
//spin_lock_irqsave(&ring->lock, &flags);
|
||||
|
||||
do {
|
||||
if (ring_writable(ring)) {
|
||||
break;
|
||||
}
|
||||
//spin_release(&ring->lock);
|
||||
if (ctx) {
|
||||
asm volatile ("sti; hlt");
|
||||
} else {
|
||||
asm volatile ("sti; hlt");
|
||||
}
|
||||
//spin_lock(&ring->lock);
|
||||
} while (1);
|
||||
|
||||
|
||||
ring->data[ring->wrptr] = c;
|
||||
ring_advance_write(ring);
|
||||
|
||||
//spin_release_irqrestore(&ring->lock, &flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ring_init(struct ring *ring, size_t cap) {
|
||||
ring->lock = 0;
|
||||
ring->wrptr = 0;
|
||||
ring->rdptr = 0;
|
||||
ring->data = kmalloc(cap);
|
||||
_assert(ring->data);
|
||||
ring->cap = cap;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "sys/tty.h"
|
||||
#include "sys/chr.h"
|
||||
#include "sys/ctype.h"
|
||||
#include "sys/line.h"
|
||||
#include "sys/errno.h"
|
||||
#include "sys/debug.h"
|
||||
#include "sys/sched.h"
|
||||
@@ -12,43 +14,47 @@
|
||||
#include "sys/mm.h"
|
||||
#include "sys/amd64/cpu.h"
|
||||
#include "sys/amd64/hw/con.h"
|
||||
#include "sys/amd64/hw/ps2.h"
|
||||
#include "sys/dev.h"
|
||||
|
||||
#define DEV_TTY(n) (n ## ULL)
|
||||
|
||||
static ssize_t tty_write(struct chrdev *tty, const void *buf, size_t pos, size_t lim);
|
||||
static int tty_ioctl(struct chrdev *tty, unsigned int cmd, void *arg);
|
||||
|
||||
struct tty_data {
|
||||
// Process group ID of the foreground group (session leader)
|
||||
pid_t fg_pgid;
|
||||
// Number
|
||||
int tty_n;
|
||||
// TODO: make console screen and keyboard character devices
|
||||
// Output device info
|
||||
void *out_dev;
|
||||
int (*out_putc) (void *dev, char c);
|
||||
};
|
||||
|
||||
// Keyboard + display
|
||||
static struct tty_data _dev_tty0_data = {
|
||||
.fg_pgid = 1,
|
||||
.tty_n = DEV_TTY(0)
|
||||
.out_dev = 0,
|
||||
.out_putc = pc_con_putc
|
||||
};
|
||||
|
||||
static struct chrdev _dev_tty0 = {
|
||||
.type = CHRDEV_TTY,
|
||||
.dev_data = &_dev_tty0_data,
|
||||
.tc = TERMIOS_DEFAULT,
|
||||
.write = tty_write,
|
||||
.read = chr_read_ring,
|
||||
// Line discipline
|
||||
.read = line_read,
|
||||
.ioctl = tty_ioctl
|
||||
};
|
||||
|
||||
static void tty_control(struct chrdev *tty, char c) {
|
||||
void tty_control_write(struct chrdev *tty, char c) {
|
||||
struct tty_data *data = tty->dev_data;
|
||||
_assert(data);
|
||||
|
||||
switch (c) {
|
||||
case 'd':
|
||||
// Close stdin for process group
|
||||
sched_close_stdin_group(data->fg_pgid);
|
||||
ring_signal(&tty->buffer, RING_SIGNAL_EOF);
|
||||
break;
|
||||
case 'c':
|
||||
ring_signal(&tty->buffer, RING_SIGNAL_BRK);
|
||||
sched_signal_group(data->fg_pgid, SIGINT);
|
||||
break;
|
||||
default:
|
||||
@@ -56,41 +62,60 @@ static void tty_control(struct chrdev *tty, char c) {
|
||||
}
|
||||
}
|
||||
|
||||
void tty_control_write(int tty_no, char c) {
|
||||
kdebug("^%c on tty%d\n", c, tty_no);
|
||||
switch (tty_no) {
|
||||
case 0:
|
||||
tty_control(&_dev_tty0, c);
|
||||
void tty_data_write(struct chrdev *tty, char c) {
|
||||
_assert(tty && tty->type == CHRDEV_TTY);
|
||||
ring_putc(NULL, &tty->buffer, c, 0);
|
||||
|
||||
switch (c) {
|
||||
case '\n':
|
||||
// TODO: this should also check ICANON
|
||||
if (tty->tc.c_lflag & ECHONL) {
|
||||
tty_putc(tty, c);
|
||||
}
|
||||
break;
|
||||
case '\b':
|
||||
break;
|
||||
case '\033':
|
||||
// TODO: ICANON
|
||||
tty_puts(tty, "^[");
|
||||
break;
|
||||
default:
|
||||
// This ignores ICANON
|
||||
if (tty->tc.c_lflag & ECHO) {
|
||||
tty_putc(tty, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function receives keystrokes from keyboard drivers
|
||||
void tty_buffer_write(int tty_no, char c) {
|
||||
switch (tty_no) {
|
||||
case 0:
|
||||
ring_putc(NULL, &_dev_tty0.buffer, c);
|
||||
break;
|
||||
void tty_puts(struct chrdev *tty, const char *s) {
|
||||
for (; *s; ++s) {
|
||||
tty_putc(tty, *s);
|
||||
}
|
||||
}
|
||||
|
||||
void tty_putc(struct chrdev *tty, char c) {
|
||||
struct tty_data *data = tty->dev_data;
|
||||
_assert(data);
|
||||
_assert(data->out_putc);
|
||||
data->out_putc(data->out_dev, c);
|
||||
}
|
||||
|
||||
void tty_init(void) {
|
||||
ring_init(&_dev_tty0.buffer, 16);
|
||||
|
||||
// Bind tty0 to keyboard
|
||||
ps2_kbd_set_tty(&_dev_tty0);
|
||||
|
||||
dev_add(DEV_CLASS_CHAR, DEV_CHAR_TTY, &_dev_tty0, "tty0");
|
||||
}
|
||||
|
||||
// TODO: multiple ttys
|
||||
static ssize_t tty_write(struct chrdev *tty, const void *buf, size_t pos, size_t lim) {
|
||||
struct tty_data *data = tty->dev_data;
|
||||
_assert(data);
|
||||
if (data->tty_n != DEV_TTY(0)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// TODO: buffer sanity checks
|
||||
_assert(data->out_putc);
|
||||
for (size_t i = 0; i < lim; ++i) {
|
||||
amd64_con_putc(((const char *) buf)[i]);
|
||||
// This is printed as-is without tty_putc wrapper
|
||||
data->out_putc(data->out_dev, ((const char *) buf)[i]);
|
||||
}
|
||||
return lim;
|
||||
}
|
||||
@@ -100,11 +125,19 @@ static int tty_ioctl(struct chrdev *tty, unsigned int cmd, void *arg) {
|
||||
_assert(data);
|
||||
|
||||
switch (cmd) {
|
||||
case TCGETS:
|
||||
memcpy(arg, &tty->tc, sizeof(struct termios));
|
||||
return 0;
|
||||
case TCSETS:
|
||||
memcpy(&tty->tc, arg, sizeof(struct termios));
|
||||
return 0;
|
||||
case TIOCGWINSZ:
|
||||
// TODO: See comment on tty data struct
|
||||
amd64_con_get_size(arg);
|
||||
return 0;
|
||||
case TIOCSPGRP:
|
||||
// Clear interrupts and stuff
|
||||
tty->buffer.flags = 0;
|
||||
data->fg_pgid = *(pid_t *) arg;
|
||||
return 0;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user