summaryrefslogtreecommitdiff
path: root/kernel/console.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/console.c')
-rw-r--r--kernel/console.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/kernel/console.c b/kernel/console.c
new file mode 100644
index 0000000..87a83ff
--- /dev/null
+++ b/kernel/console.c
@@ -0,0 +1,199 @@
+//
+// Console input and output, to the uart.
+// Reads are line at a time.
+// Implements special input characters:
+// newline -- end of line
+// control-h -- backspace
+// control-u -- kill line
+// control-d -- end of file
+// control-p -- print process list
+//
+
+#include <stdarg.h>
+
+#include "types.h"
+#include "param.h"
+#include "spinlock.h"
+#include "sleeplock.h"
+#include "fs.h"
+#include "file.h"
+#include "memlayout.h"
+#include "riscv.h"
+#include "defs.h"
+#include "proc.h"
+
+#define BACKSPACE 0x100
+#define C(x) ((x)-'@') // Control-x
+
+//
+// send one character to the uart.
+//
+void
+consputc(int c)
+{
+ extern volatile int panicked; // from printf.c
+
+ if(panicked){
+ for(;;)
+ ;
+ }
+
+ if(c == BACKSPACE){
+ // if the user typed backspace, overwrite with a space.
+ uartputc('\b'); uartputc(' '); uartputc('\b');
+ } else {
+ uartputc(c);
+ }
+}
+
+struct {
+ struct spinlock lock;
+
+ // input
+#define INPUT_BUF 128
+ char buf[INPUT_BUF];
+ uint r; // Read index
+ uint w; // Write index
+ uint e; // Edit index
+} cons;
+
+//
+// user write()s to the console go here.
+//
+int
+consolewrite(int user_src, uint64 src, int n)
+{
+ int i;
+
+ acquire(&cons.lock);
+ for(i = 0; i < n; i++){
+ char c;
+ if(either_copyin(&c, user_src, src+i, 1) == -1)
+ break;
+ consputc(c);
+ }
+ release(&cons.lock);
+
+ return n;
+}
+
+//
+// user read()s from the console go here.
+// copy (up to) a whole input line to dst.
+// user_dist indicates whether dst is a user
+// or kernel address.
+//
+int
+consoleread(int user_dst, uint64 dst, int n)
+{
+ uint target;
+ int c;
+ char cbuf;
+
+ target = n;
+ acquire(&cons.lock);
+ while(n > 0){
+ // wait until interrupt handler has put some
+ // input into cons.buffer.
+ while(cons.r == cons.w){
+ if(myproc()->killed){
+ release(&cons.lock);
+ return -1;
+ }
+ sleep(&cons.r, &cons.lock);
+ }
+
+ c = cons.buf[cons.r++ % INPUT_BUF];
+
+ if(c == C('D')){ // end-of-file
+ if(n < target){
+ // Save ^D for next time, to make sure
+ // caller gets a 0-byte result.
+ cons.r--;
+ }
+ break;
+ }
+
+ // copy the input byte to the user-space buffer.
+ cbuf = c;
+ if(either_copyout(user_dst, dst, &cbuf, 1) == -1)
+ break;
+
+ dst++;
+ --n;
+
+ if(c == '\n'){
+ // a whole line has arrived, return to
+ // the user-level read().
+ break;
+ }
+ }
+ release(&cons.lock);
+
+ return target - n;
+}
+
+//
+// the console input interrupt handler.
+// uartintr() calls this for input character.
+// do erase/kill processing, append to cons.buf,
+// wake up consoleread() if a whole line has arrived.
+//
+void
+consoleintr(int c)
+{
+ acquire(&cons.lock);
+
+ switch(c){
+ case C('P'): // Print process list.
+ procdump();
+ break;
+ case C('U'): // Kill line.
+ while(cons.e != cons.w &&
+ cons.buf[(cons.e-1) % INPUT_BUF] != '\n'){
+ cons.e--;
+ consputc(BACKSPACE);
+ }
+ break;
+ case C('H'): // Backspace
+ case '\x7f':
+ if(cons.e != cons.w){
+ cons.e--;
+ consputc(BACKSPACE);
+ }
+ break;
+ default:
+ if(c != 0 && cons.e-cons.r < INPUT_BUF){
+ c = (c == '\r') ? '\n' : c;
+
+ // echo back to the user.
+ consputc(c);
+
+ // store for consumption by consoleread().
+ cons.buf[cons.e++ % INPUT_BUF] = c;
+
+ if(c == '\n' || c == C('D') || cons.e == cons.r+INPUT_BUF){
+ // wake up consoleread() if a whole line (or end-of-file)
+ // has arrived.
+ cons.w = cons.e;
+ wakeup(&cons.r);
+ }
+ }
+ break;
+ }
+
+ release(&cons.lock);
+}
+
+void
+consoleinit(void)
+{
+ initlock(&cons.lock, "cons");
+
+ uartinit();
+
+ // connect read and write system calls
+ // to consoleread and consolewrite.
+ devsw[CONSOLE].read = consoleread;
+ devsw[CONSOLE].write = consolewrite;
+}