diff options
Diffstat (limited to 'kernel/tty.c')
| -rw-r--r-- | kernel/tty.c | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/kernel/tty.c b/kernel/tty.c new file mode 100644 index 0000000..1946110 --- /dev/null +++ b/kernel/tty.c @@ -0,0 +1,86 @@ +#include <asm/interrupt.h> +#include <errno.h> +#include <kernel/sched.h> +#include <kernel/serial.h> +#include <kernel/tty.h> +#include <limits.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#define NRTTY \ + (sizeof(ttys) / sizeof(struct tty_struct*)) + +static struct tty_struct *ttys[] = { + NULL, + &tty_rs +}; + +/* check if the buffer's empty and if so, go to sleep */ +static int sleep_while_empty(struct tty_queue *q) { + if(q->pread != q->pwrite) + return 0; + + cli(); + while(1) { + if(q->pread == q->pwrite) + interruptible_sleep_on(&q->waiting); + + /* check again to see if anything was read, or we were interrupted */ + if(q->pread == q->pwrite) { + sti(); + return -1; + } + + sti(); + return 0; + } +} + +ssize_t tty_read(unsigned tty, void *buf, size_t len) { + size_t i; + char *b = buf; + struct tty_queue *q = &ttys[tty]->rqueue; + + if(tty > NRTTY || ttys[tty] == NULL) + return -1; + + if(len == 0) + return 0; + + if(len > SSIZE_MAX) + len = SSIZE_MAX; + + for(i = 0; i < len; i++) { + /* check if the buffer's empty and if so, go to sleep */ + if(sleep_while_empty(q) < 0) { + if(i == 0) + return -EINTR; + + return i; + } + + *b++ = q->buf[q->pread]; + q->pread = q->pread + 1 % TTY_BUF_SIZE; + } + + return len; +} + +void tty_init(void) { + int i; + + for(i = 0; i < NRTTY; i++) { + if(!ttys[i]) + continue; + + memset(&ttys[i]->rqueue, 0, sizeof(struct tty_queue)); + memset(&ttys[i]->wqueue, 0, sizeof(struct tty_queue)); + + ttys[i]->rqueue.pwrite = 1; + ttys[i]->wqueue.pwrite = 1; + + if(ttys[i]->init) + ttys[i]->init(); + } +} |
