root/kernel/uart.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. uartinit
  2. uartputc
  3. uartputc_sync
  4. uartstart
  5. uartgetc
  6. uartintr

   1 //
   2 // low-level driver routines for 16550a UART.
   3 //
   4 
   5 #include "types.h"
   6 #include "param.h"
   7 #include "memlayout.h"
   8 #include "riscv.h"
   9 #include "spinlock.h"
  10 #include "proc.h"
  11 #include "defs.h"
  12 
  13 // the UART control registers are memory-mapped
  14 // at address UART0. this macro returns the
  15 // address of one of the registers.
  16 #define Reg(reg) ((volatile unsigned char *)(UART0 + (reg)))
  17 
  18 // the UART control registers.
  19 // some have different meanings for
  20 // read vs write.
  21 // see http://byterunner.com/16550.html
  22 #define RHR 0                 // receive holding register (for input bytes)
  23 #define THR 0                 // transmit holding register (for output bytes)
  24 #define IER 1                 // interrupt enable register
  25 #define IER_RX_ENABLE (1<<0)
  26 #define IER_TX_ENABLE (1<<1)
  27 #define FCR 2                 // FIFO control register
  28 #define FCR_FIFO_ENABLE (1<<0)
  29 #define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs
  30 #define ISR 2                 // interrupt status register
  31 #define LCR 3                 // line control register
  32 #define LCR_EIGHT_BITS (3<<0)
  33 #define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate
  34 #define LSR 5                 // line status register
  35 #define LSR_RX_READY (1<<0)   // input is waiting to be read from RHR
  36 #define LSR_TX_IDLE (1<<5)    // THR can accept another character to send
  37 
  38 #define ReadReg(reg) (*(Reg(reg)))
  39 #define WriteReg(reg, v) (*(Reg(reg)) = (v))
  40 
  41 // the transmit output buffer.
  42 struct spinlock uart_tx_lock;
  43 #define UART_TX_BUF_SIZE 32
  44 char uart_tx_buf[UART_TX_BUF_SIZE];
  45 uint64 uart_tx_w; // write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE]
  46 uint64 uart_tx_r; // read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]
  47 
  48 extern volatile int panicked; // from printf.c
  49 
  50 void uartstart();
  51 
  52 void
  53 uartinit(void)
  54 {
  55   // disable interrupts.
  56   WriteReg(IER, 0x00);
  57 
  58   // special mode to set baud rate.
  59   WriteReg(LCR, LCR_BAUD_LATCH);
  60 
  61   // LSB for baud rate of 38.4K.
  62   WriteReg(0, 0x03);
  63 
  64   // MSB for baud rate of 38.4K.
  65   WriteReg(1, 0x00);
  66 
  67   // leave set-baud mode,
  68   // and set word length to 8 bits, no parity.
  69   WriteReg(LCR, LCR_EIGHT_BITS);
  70 
  71   // reset and enable FIFOs.
  72   WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
  73 
  74   // enable transmit and receive interrupts.
  75   WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
  76 
  77   initlock(&uart_tx_lock, "uart");
  78 }
  79 
  80 // add a character to the output buffer and tell the
  81 // UART to start sending if it isn't already.
  82 // blocks if the output buffer is full.
  83 // because it may block, it can't be called
  84 // from interrupts; it's only suitable for use
  85 // by write().
  86 void
  87 uartputc(int c)
  88 {
  89   acquire(&uart_tx_lock);
  90 
  91   if(panicked){
  92     for(;;)
  93       ;
  94   }
  95   while(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){
  96     // buffer is full.
  97     // wait for uartstart() to open up space in the buffer.
  98     sleep(&uart_tx_r, &uart_tx_lock);
  99   }
 100   uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c;
 101   uart_tx_w += 1;
 102   uartstart();
 103   release(&uart_tx_lock);
 104 }
 105 
 106 
 107 // alternate version of uartputc() that doesn't 
 108 // use interrupts, for use by kernel printf() and
 109 // to echo characters. it spins waiting for the uart's
 110 // output register to be empty.
 111 void
 112 uartputc_sync(int c)
 113 {
 114   push_off();
 115 
 116   if(panicked){
 117     for(;;)
 118       ;
 119   }
 120 
 121   // wait for Transmit Holding Empty to be set in LSR.
 122   while((ReadReg(LSR) & LSR_TX_IDLE) == 0)
 123     ;
 124   WriteReg(THR, c);
 125 
 126   pop_off();
 127 }
 128 
 129 // if the UART is idle, and a character is waiting
 130 // in the transmit buffer, send it.
 131 // caller must hold uart_tx_lock.
 132 // called from both the top- and bottom-half.
 133 void
 134 uartstart()
 135 {
 136   while(1){
 137     if(uart_tx_w == uart_tx_r){
 138       // transmit buffer is empty.
 139       ReadReg(ISR);
 140       return;
 141     }
 142     
 143     if((ReadReg(LSR) & LSR_TX_IDLE) == 0){
 144       // the UART transmit holding register is full,
 145       // so we cannot give it another byte.
 146       // it will interrupt when it's ready for a new byte.
 147       return;
 148     }
 149     
 150     int c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE];
 151     uart_tx_r += 1;
 152     
 153     // maybe uartputc() is waiting for space in the buffer.
 154     wakeup(&uart_tx_r);
 155     
 156     WriteReg(THR, c);
 157   }
 158 }
 159 
 160 // read one input character from the UART.
 161 // return -1 if none is waiting.
 162 int
 163 uartgetc(void)
 164 {
 165   if(ReadReg(LSR) & 0x01){
 166     // input data is ready.
 167     return ReadReg(RHR);
 168   } else {
 169     return -1;
 170   }
 171 }
 172 
 173 // handle a uart interrupt, raised because input has
 174 // arrived, or the uart is ready for more output, or
 175 // both. called from devintr().
 176 void
 177 uartintr(void)
 178 {
 179   // read and process incoming characters.
 180   while(1){
 181     int c = uartgetc();
 182     if(c == -1)
 183       break;
 184     consoleintr(c);
 185   }
 186 
 187   // send buffered characters.
 188   acquire(&uart_tx_lock);
 189   uartstart();
 190   release(&uart_tx_lock);
 191 }

/* [<][>][^][v][top][bottom][index][help] */