Proper line discipline for TTYs and simple line discipline for chardevs

This commit is contained in:
Mark
2020-01-21 17:08:31 +02:00
parent beecf5e2b3
commit 0105aa8002
16 changed files with 383 additions and 237 deletions
+2 -1
View File
@@ -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 \
+3
View File
@@ -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);
+2
View File
@@ -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
View File
@@ -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);
+12
View File
@@ -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
View File
@@ -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);
+34
View File
@@ -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
View File
@@ -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);
+5
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
-37
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+61 -28
View File
@@ -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: