slkern/console.c

188 lines
3.7 KiB
C

// TODO: CHECK/REPLACE/UPDATE OLD CODE (this file is based on xv6)
//
// 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 "types.h"
#include "param.h"
#include "sched.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.
// called by printf(), and to echo input characters,
// but not from write().
//
void
consputc(int c)
{
if(c == BACKSPACE){
// if the user typed backspace, overwrite with a space.
uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b');
} else {
uartputc_sync(c);
}
}
sched_spinlock_t conslock;
// input
#define INPUT_BUF_SIZE 128
char consbuf[INPUT_BUF_SIZE];
uint consr; // Read index
uint consw; // Write index
uint conse; // Edit index
//
// user write()s to the console go here.
//
int
consolewrite(int user_src, uint64 src, int n)
{
int i;
for(i = 0; i < n; i++){
char c;
if(either_copyin(&c, user_src, src+i, 1) == -1)
break;
uartputc(c);
}
return i;
}
//
// 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(&conslock);
while(n > 0){
// wait until interrupt handler has put some
// input into cons.buffer.
while(consr == consw){
if(killed(myproc())){
release(&conslock);
return -1;
}
sleep(&consr, &conslock);
}
c = consbuf[consr++ % INPUT_BUF_SIZE];
if(c == C('D')){ // end-of-file
if(n < target){
// Save ^D for next time, to make sure
// caller gets a 0-byte result.
consr--;
}
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(&conslock);
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(&conslock);
switch(c){
case C('P'): // Print process list.
sched_dumpstatus();
break;
case C('U'): // Kill line.
while(conse != consw &&
consbuf[(conse-1) % INPUT_BUF_SIZE] != '\n'){
conse--;
consputc(BACKSPACE);
}
break;
case C('H'): // Backspace
case '\x7f': // Delete key
if(conse != consw){
conse--;
consputc(BACKSPACE);
}
break;
default:
if(c != 0 && conse-consr < INPUT_BUF_SIZE){
c = (c == '\r') ? '\n' : c;
// echo back to the user.
consputc(c);
// store for consumption by consoleread().
consbuf[conse++ % INPUT_BUF_SIZE] = c;
if(c == '\n' || c == C('D') || conse-consr == INPUT_BUF_SIZE){
// wake up consoleread() if a whole line (or end-of-file)
// has arrived.
consw = conse;
sched_wake(&consr);
}
}
break;
}
release(&conslock);
}
void
consoleinit(void)
{
initlock(&conslock, "cons");
uartinit();
// connect read and write system calls
// to consoleread and consolewrite.
devsw[CONSOLE].read = &consoleread;
devsw[CONSOLE].write = &consolewrite;
}