root/kernel/uart.c

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

DEFINITIONS

This source file includes following definitions.
  1. uartinit
  2. uartwrite
  3. uartputc_sync
  4. uartgetc
  5. 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 // for transmission.
  42 static struct spinlock tx_lock;
  43 static int tx_busy;           // is the UART busy sending?
  44 static int tx_chan;           // &tx_chan is the "wait channel"
  45 
  46 extern volatile int panicking; // from printf.c
  47 extern volatile int panicked; // from printf.c
  48 
  49 void
  50 uartinit(void)
  51 {
  52   // disable interrupts.
  53   WriteReg(IER, 0x00);
  54 
  55   // special mode to set baud rate.
  56   WriteReg(LCR, LCR_BAUD_LATCH);
  57 
  58   // LSB for baud rate of 38.4K.
  59   WriteReg(0, 0x03);
  60 
  61   // MSB for baud rate of 38.4K.
  62   WriteReg(1, 0x00);
  63 
  64   // leave set-baud mode,
  65   // and set word length to 8 bits, no parity.
  66   WriteReg(LCR, LCR_EIGHT_BITS);
  67 
  68   // reset and enable FIFOs.
  69   WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
  70 
  71   // enable transmit and receive interrupts.
  72   WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
  73 
  74   initlock(&tx_lock, "uart");
  75 }
  76 
  77 // transmit buf[] to the uart. it blocks if the
  78 // uart is busy, so it cannot be called from
  79 // interrupts, only from write() system calls.
  80 void
  81 uartwrite(char buf[], int n)
  82 {
  83   acquire(&tx_lock);
  84 
  85   int i = 0;
  86   while(i < n){ 
  87     while(tx_busy != 0){
  88       // wait for a UART transmit-complete interrupt
  89       // to set tx_busy to 0.
  90       sleep(&tx_chan, &tx_lock);
  91     }   
  92       
  93     WriteReg(THR, buf[i]);
  94     i += 1;
  95     tx_busy = 1;
  96   }
  97 
  98   release(&tx_lock);
  99 }
 100 
 101 
 102 // write a byte to the uart without using
 103 // interrupts, for use by kernel printf() and
 104 // to echo characters. it spins waiting for the uart's
 105 // output register to be empty.
 106 void
 107 uartputc_sync(int c)
 108 {
 109   if(panicking == 0)
 110     push_off();
 111 
 112   if(panicked){
 113     for(;;)
 114       ;
 115   }
 116 
 117   // wait for Transmit Holding Empty to be set in LSR.
 118   while((ReadReg(LSR) & LSR_TX_IDLE) == 0)
 119     ;
 120   WriteReg(THR, c);
 121 
 122   if(panicking == 0)
 123     pop_off();
 124 }
 125 
 126 // read one input character from the UART.
 127 // return -1 if none is waiting.
 128 int
 129 uartgetc(void)
 130 {
 131   if(ReadReg(LSR) & LSR_RX_READY){
 132     // input data is ready.
 133     return ReadReg(RHR);
 134   } else {
 135     return -1;
 136   }
 137 }
 138 
 139 // handle a uart interrupt, raised because input has
 140 // arrived, or the uart is ready for more output, or
 141 // both. called from devintr().
 142 void
 143 uartintr(void)
 144 {
 145   ReadReg(ISR); // acknowledge the interrupt
 146 
 147   acquire(&tx_lock);
 148   if(ReadReg(LSR) & LSR_TX_IDLE){
 149     // UART finished transmitting; wake up sending thread.
 150     tx_busy = 0;
 151     wakeup(&tx_chan);
 152   }
 153   release(&tx_lock);
 154 
 155   // read and process incoming characters.
 156   while(1){
 157     int c = uartgetc();
 158     if(c == -1)
 159       break;
 160     consoleintr(c);
 161   }
 162 }

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