summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/rs.s34
-rw-r--r--kernel/serial.c39
-rw-r--r--kernel/sys.c9
-rw-r--r--kernel/tty.c64
4 files changed, 132 insertions, 14 deletions
diff --git a/kernel/rs.s b/kernel/rs.s
index 9f3d4a6..69d31ae 100644
--- a/kernel/rs.s
+++ b/kernel/rs.s
@@ -1,7 +1,20 @@
global rs_isr
-extern rs_handler
+extern rsint_rx
+extern rsint_tx
extern rsputs
+%define RSBASE 0x3F8
+
+%define RSDATA RSBASE
+%define RSINTEN (RSBASE + 1)
+%define RSFIFOC (RSBASE + 2)
+%define RSINTID (RSBASE + 2)
+%define RSLINEC (RSBASE + 3)
+%define RSMODEMC (RSBASE + 4)
+%define RSLINES (RSBASE + 5)
+%define RSMODEMS (RSBASE + 6)
+%define RSSCRATCH (RSBASE + 7)
+
rs_isr:
pusha
mov ax, ds
@@ -12,7 +25,24 @@ rs_isr:
mov fs, ax
mov gs, ax
- call rs_handler
+ mov dx, RSINTID
+ in al, dx
+ test al, 1
+ jnz .skip
+ and al, 0x0E
+ cmp al, 0x04
+ je .rx
+ cmp al, 0x0C
+ je .rx
+ cmp al, 0x02
+ je .tx
+ jmp .skip
+.tx:
+ call rsint_tx
+ jmp .skip
+.rx:
+ call rsint_rx
+.skip:
mov al, 0x20
out 0x20, al
diff --git a/kernel/serial.c b/kernel/serial.c
index 39c5ee0..a133865 100644
--- a/kernel/serial.c
+++ b/kernel/serial.c
@@ -16,6 +16,7 @@
#define RSDATA RSBASE
#define RSINTEN (RSBASE + 1)
#define RSFIFOC (RSBASE + 2)
+#define RSINTID (RSBASE + 2)
#define RSLINEC (RSBASE + 3)
#define RSMODEMC (RSBASE + 4)
#define RSLINES (RSBASE + 5)
@@ -27,8 +28,12 @@
extern void rs_isr(void);
+/* internal function prototypes */
+void rsint_tx(void);
+
struct tty_struct tty_rs = {
- .init = &serial_init
+ .init = &serial_init,
+ .write = &rsint_tx
};
static char present;
@@ -70,11 +75,18 @@ void serial_init(void) {
/* restore access to the divisor */
outb(RSLINEC, inb(RSLINEC) & 0x7F);
/* configure port parameters */
- outb(RSFIFOC, 0x06);
+ outb(RSFIFOC, 0x07);
outb(RSLINEC, 0x03);
outb(RSMODEMC, 0x0B);
- outb(RSINTEN, 0x01);
+ outb(RSINTEN, 0x03);
inb(RSDATA);
+
+ /* test FIFO functionality */
+ if(inb(RSINTID) & 0xC0 != 0xC0) {
+ printk("[rs] Failed to enable FIFO on COM0!\n");
+ return;
+ }
+
/* register the interrupt handler */
register_isr(0x24, 0, &rs_isr);
irq_enable(0x10);
@@ -85,7 +97,7 @@ void serial_init(void) {
present = 1;
}
-void rs_handler(void) {
+void rsint_rx(void) {
struct tty_queue *q = &tty_rs.rqueue;
if((inb(RSLINES) & 1) == 0)
@@ -98,3 +110,22 @@ void rs_handler(void) {
wake_up(&q->waiting);
}
+
+void rsint_tx(void) {
+ int copied = 0;
+ struct tty_queue *q = &tty_rs.wqueue;
+
+ while(q->pread != q->pwrite) {
+ /* we're done if the TX FIFO is full */
+ if((inb(RSLINES) & 0x20) == 0)
+ break;
+
+ outb(RSDATA, q->buf[q->pread]);
+ q->pread = (q->pread + 1) % TTY_BUF_SIZE;
+
+ copied = 1;
+ }
+
+ if(copied)
+ wake_up(&q->waiting);
+}
diff --git a/kernel/sys.c b/kernel/sys.c
index 6127f58..8e28edd 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1,5 +1,6 @@
#include <errno.h>
#include <kernel/con.h>
+#include <kernel/kernel.h>
#include <kernel/sched.h>
#include <kernel/serial.h>
#include <kernel/tty.h>
@@ -84,6 +85,14 @@ ssize_t sys_read(void *buf, size_t len) {
return tty_read(ctask->ctty, buf, len);
}
+ssize_t sys_write(void *buf, size_t len) {
+ return tty_write(ctask->ctty, buf, len);
+}
+
+void sys_panic(void) {
+ panic();
+}
+
int sys_dummy(void) {
return -ENOSYS;
}
diff --git a/kernel/tty.c b/kernel/tty.c
index 1946110..75c3701 100644
--- a/kernel/tty.c
+++ b/kernel/tty.c
@@ -16,11 +16,8 @@ static struct tty_struct *ttys[] = {
&tty_rs
};
-/* check if the buffer's empty and if so, go to sleep */
+/* check if a 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)
@@ -37,6 +34,24 @@ static int sleep_while_empty(struct tty_queue *q) {
}
}
+/* check if a buffer's full and if so, go to sleep */
+static int sleep_while_full(struct tty_queue *q) {
+ cli();
+ while(1) {
+ if(q->pwrite == (q->pread - 1 % sizeof(q->buf)))
+ interruptible_sleep_on(&q->waiting);
+
+ /* check again to see if anything was removed, or we were interrupted */
+ if(q->pwrite == (q->pread - 1 % sizeof(q->buf))) {
+ sti();
+ return -1;
+ }
+
+ sti();
+ return 0;
+ }
+}
+
ssize_t tty_read(unsigned tty, void *buf, size_t len) {
size_t i;
char *b = buf;
@@ -61,9 +76,45 @@ ssize_t tty_read(unsigned tty, void *buf, size_t len) {
}
*b++ = q->buf[q->pread];
- q->pread = q->pread + 1 % TTY_BUF_SIZE;
+ q->pread = (q->pread + 1) % TTY_BUF_SIZE;
+ }
+
+ return len;
+}
+
+ssize_t tty_write(unsigned tty, void *buf, size_t len) {
+ size_t i;
+ char *b = buf;
+ struct tty_queue *q = &ttys[tty]->wqueue;
+
+ 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 full and if so, go to sleep */
+ if(sleep_while_full(q) < 0) {
+ if(i == 0)
+ return -EINTR;
+
+ return i;
+ }
+
+ q->buf[q->pwrite] = *b++;
+ q->pwrite = (q->pwrite + 1) % TTY_BUF_SIZE;
}
+ /* tell the driver that new data is available */
+ cli();
+ if(ttys[tty]->write)
+ ttys[tty]->write();
+ sti();
+
return len;
}
@@ -77,9 +128,6 @@ void tty_init(void) {
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();
}