summaryrefslogtreecommitdiff
path: root/kernel/tty.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/tty.c')
-rw-r--r--kernel/tty.c86
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();
+ }
+}