Skip to content

console.c

TIP

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)-'@')

TIP

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){

TIP

if the user typed backspace, overwrite with a space.

    uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b');
  } else {
    uartputc_sync(c);
  }
}

struct {
  struct spinlock lock;
  

TIP

input

#define INPUT_BUF_SIZE 128
  char buf[INPUT_BUF_SIZE];
  uint r;
  uint w;
  uint e;
} cons;

TIP

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;
}

TIP

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){

TIP

wait until interrupt handler has put some input into cons.buffer.

    while(cons.r == cons.w){
      if(killed(myproc())){
        release(&cons.lock);
        return -1;
      }
      sleep(&cons.r, &cons.lock);
    }

    c = cons.buf[cons.r++ % INPUT_BUF_SIZE];

    if(c == C('D')){
      if(n < target){

TIP

Save ^D for next time, to make sure caller gets a 0-byte result.

        cons.r--;
      }
      break;
    }

TIP

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'){

TIP

a whole line has arrived, return to the user-level read().

      break;
    }
  }
  release(&cons.lock);

  return target - n;
}

TIP

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'):
    procdump();
    break;
  case C('U'):
    while(cons.e != cons.w &&
          cons.buf[(cons.e-1) % INPUT_BUF_SIZE] != '\n'){
      cons.e--;
      consputc(BACKSPACE);
    }
    break;
  case C('H'):
  case '\x7f':
    if(cons.e != cons.w){
      cons.e--;
      consputc(BACKSPACE);
    }
    break;
  default:
    if(c != 0 && cons.e-cons.r < INPUT_BUF_SIZE){
      c = (c == '\r') ? '\n' : c;

TIP

echo back to the user.

      consputc(c);

TIP

store for consumption by consoleread().

      cons.buf[cons.e++ % INPUT_BUF_SIZE] = c;

      if(c == '\n' || c == C('D') || cons.e-cons.r == INPUT_BUF_SIZE){

TIP

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();

TIP

connect read and write system calls to consoleread and consolewrite.

  devsw[CONSOLE].read = consoleread;
  devsw[CONSOLE].write = consolewrite;
}