Difference between revisions of "UART Driver"

From Embedded Xinu
Jump to navigation Jump to search
 
(24 intermediate revisions by 2 users not shown)
Line 1: Line 1:
The UART driver is designed to work with a [[National Semiconductor 16550 UART]].  The current driver is a char driver, responsible for reading and writing bytes synchronously to the hardware.  The new driver will be a char driver, responsible for receiving and sending bytes of data in both synchronous and asynchronous modes.  The UART driver does not utilize the FIFO capabiities of the 16550 UART, but this is a possible later enhancement.
+
[[Image:UartAsyncDriver.png|right|450px]]
== Driver structure ==
+
The UART driver is a char-oriented driver designed to work with a [[National Semiconductor 16550 UART]].  The driver is responsible for receiving and sending bytes of data asynchronously.   
The UART driver uses a structure to track all buffers, semaphores, and counts associated with the UARTCertain fields pertain only to asynchronous mode.
 
  
   struct uart                            /* Control block for the       */
+
The UART driver is divided into two sections: an upper half and a lower half.  The two halves communicate via semaphores and buffers.  The lower half is interrupt driven and interacts with the physical hardware.  The upper half of the driver interacts with user programs.   It does not interact directly with the hardware nor does it spinlock while waiting for the hardware to be ready. The upper half waits on semaphores which are signaled by the lower half to indicate bytes of data or free space are avaialable in the appropriate buffer.
  {                                      /* 16550 UART                  */
 
    struct uart_csreg *uart_csr;          /* control and status registers */
 
    struct dentry    *uart_dev;          /* dev structure                */
 
    struct tty        *uart_tty;          /* tty associated with the UART */
 
    /* Statistical Counts */
 
    int              uart_cout;          /* characters output            */
 
    int              uart_cin;          /* characters input            */
 
    int              uart_lserr;        /* receiver error count        */
 
    int              uart_ovrrn;        /* characters overrun          */
 
    int              uart_iirq;          /* input IRQ count              */
 
    int              uart_oirq;          /* output IRQ count            */
 
    /* Control flags */
 
    unsigned char    uart_iflags; /* Input flags         */
 
    unsigned char    uart_oflags; /* Output flags         */
 
    /* Asynchronous Input fields */
 
    SEMAPHORE        uart_isema;         /* I/0 semaphore for uart input */
 
    unsigned short    uart_istart; /* index of first byte          */
 
    unsigned short    uart_icount;        /* bytes in buffer              */
 
    unsigned char    uart_in[UART_IBLEN];  /* input buffer              */
 
    /* Asynchronous output fields */
 
    SEMAPHORE        uart_osema;         /* I/0 semaphore for uart output*/
 
    unsigned short    uart_ostart;        /* index of first byte          */
 
    unsigned short    uart_ocount;        /* bytes in buffer             */
 
    unsigned char    uart_out[UART_OBLEN]; /* output buffer              */
 
    int              uart_oidle;        /* UART transmitter idle        */
 
  };
 
  
The following structure represents the hardware registers:
+
== Physical UART ==
  struct uart_csreg                      /* Control and status registers */
+
The XINU backends have been equipped with serial ports that are representative of the National Semiconductor 16550 UART. Documentation on the 16550 UART can be found at [http://www.national.com/ds.cgi/NS/NS16C552.pdf http://www.national.com/ds.cgi/NS/NS16C552.pdf].
  {                                      /* for the 16550 UART.        */
 
    volatile unsigned char uart_buffer;  /* receive buffer (read only) & */
 
                                          /*  transmit hold (write only)  */
 
    volatile unsigned char uart_ier;      /* interrupt enable            */
 
    volatile unsigned char uart_iir;      /* interrupt ident (read only)  */
 
                                          /*  or FIFO control (write only)*/
 
    volatile unsigned char uart_lcr;      /* line control                */
 
    volatile unsigned char uart_mcr;      /* modem control                */
 
    volatile unsigned char uart_lsr;      /* line status                  */
 
    volatile unsigned char uart_msr;      /* modem status                */
 
    volatile unsigned char uart_scr;      /* scratch                      */
 
  };
 
Some registers are also defined with additional or alternative names for coherent reference.
 
  #define uart_rbr uart_buffer            /* receive buffer (read only)  */
 
  #define uart_thr uart_buffer            /* transmit hold (write only)  */
 
  #define uart_fcr uart_iir              /* FIFO control (write only)    */
 
  #define uart_dll uart_buffer            /* divisor latch low byte      */
 
  #define uart_dlm uart_ier              /* divisor latch high byte      */
 
  
== Modes ==
+
== Initialization ==
=== Synchronous ===
+
Intialize defines the starting values for all members of the control block: statistical counts are zeroed, buffers are defined, and semaphores are allocatedAlso part of the initialization process is setting values in the control and status registers:
In synchronous mode, the driver spinlocks to wait for the hardwareThe driver continuously checks the Line Status register to determine when the hardware has received a byte or is ready to transmit a byteSynchronous mode does not rely on interrupts or buffers.
+
* Line control is set to 8 bit, no parity, 1 stop.
 +
* Receiver FIFO full, transmitt buffer empty, and receiver line status interrupts are enabled.
 +
* Hardware FIFOs are enabled.
 +
* Divisor Latch bits (high and low)
 +
** The divisor can be calculated by using the formula:
 +
:<math>divisor=\frac{baud\_base+\frac{baud\_rate}{2}}{baud\_rate}.</math>
 +
Where <tt>baud_rate</tt> is the speed you wish to connect at (typically 115,200) and
 +
:<math>baud\_base=\frac{clockrate}{16}</math>.
 +
The clockrate should be measured in hertz and may not be equivalent to the clockspeed(The [[WRT54GL]], for example, has a hard-coded clockrate of 20,000,000 or 20MHz, while the [[WRT54G]] has a clockrate of about 25MHz.)
  
The synchronous functions are:
+
== Upper Half (Read and Write) ==
  DEVCALL uartReadSync(struct dentry *dptr, unsigned char *buf, int len);
+
Read is part of the upper half of the driver that fills a user supplied buffer with bytes from the input buffer filled by the lower half of the driver.  If the input buffer is empty, read waits for the lower half to signal on the input semaphore and indicate bytes are avaiable in the input buffer.
  DEVCALL uartWriteSync(struct dentry *pdev, unsigned char *buf, int len);
 
  
=== Asynchronous ===
+
Write is part of the upper half of the driver and places bytes from a user supplied buffer into the output buffer read by the lower half of the driverIf there is no free space in the output buffer, write waits for the lower half to signal on the output semaphore and indicate free space is available in the output buffer.
In asynchronous mode, the driver is divided into two sections: an upper half and a lower half.  The two halves communicate via semaphores and buffers.
 
  
[[Image:UartAsyncDriver.png]]
+
== Lower Half (Interrupt Handler) ==
 +
The interrupt handler is the lower half of the driver.  The 16550 UART sends an interrupt (if enabled) when the transmitter FIFO is empty or the receiver FIFO has reached its available bytes tigger level.
 +
Three different types of interrupts are handled by the lower half:
 +
* Line or modem status: The interrupt is merely noted in the UART's statistical counts. 
 +
* Receiver hardware FIFO trigger level: The driver moves bytes from the UART's receive hardware FIFO into the input buffer.  Received bytes are read from the UART until the Data Ready bit in the Line Status Register is no longer set.  The input semaphore is signaled to let the upper half know bytes of data are in the input buffer. 
 +
* Transmitter hardware FIFO empty: The lower half fills the UART's transmit hardware FIFO from the output buffer.  The interrupt handler fills the transmit hardware FIFO until the FIFO is full or the output buffer is empty.  The output semaphore is signaled to let the upper half know bytes of space are available in the output buffer.
  
The lower half is interrupt driven and interacts with the hardware.  The 16550 UART sends an interrupt (if enabled) when the transmitter is empty or the receiver has a byte.  The driver places received data into the input buffer and signals the input semaphore to let the upper half know that another byte is in the input bufferThe lower half also moves data from the output buffer into the transmitter and signals the output semaphore to let the upper half know that another free byte is in the output buffer.
+
== Control ==
The upper half of the driver interacts with user programs.  A read or write call adds or removes bytes from the appropriate buffer and returns.  The upper half does not wait for the lower half or the hardware to be ready, it only waits for the appropriate buffer.
+
The control functions are used to set, clear, and get the input and output flags for the UART driverNon-blocking flags indicate the upper half read and write functions should perform as much of the requested read or write length as possible, but should not block to wait for the lower half to fill or empty the input or output buffers.  When the echo input flag is set, the UART outputs every byte as it is received in addition to placing the byte in the input buffer.
  
The asynchronous functions are:
+
=== Loopback ===
  void uartIntr(void);
+
<code>UART_ENABLE_LOOPBACK</code> and <code>UART_DISABLE_LOOPBACK</code> control functions enable and disable hardware loopback.  Be aware that loopback is precarious and must be used carefully.  It is recommended that you turn off interrupts prior to enabling loopback and after disabling loopback to avoid interleaving output while in loopback mode.
  DEVCALL uartReadAsync(struct dentry *dptr, unsigned char *buf, int len);
 
  DEVCALL uartWriteAsync(struct dentry *pdev, unsigned char *buf, int len);
 
  
=== Switching modes ===
+
Prior to enabling and disabling hardware loopback, the control function ensures the transmitter is completely empty and has completed all previous transmissionWhen in loopback mode the hardware does not throw interrupts, so the control functions call the UART interrupt handler explicitly.
The UART driver determines which mode to use by setting and reading input and output flags. 
 
 
 
Switching from synchronous to asynchronous mode is the simpler switch.  The proper flags need to be set and the proper interrupts enabled on the hardware.
 
 
 
Switching from asynchronous to synchronous is more difficult because there may be bytes in the buffers waiting for the appropriate driver half to respond.  The control function needs to flush the appropriate buffers before making the mode switchIt also needs to set the proper flags and disable the proper interrupts on the hardware.
 
  
 
== See also ==
 
== See also ==
* [[National Semiconductor 16550 UART]]
 
 
* [[TTY Driver]]
 
* [[TTY Driver]]
* [[Why we add serial ports]]
 

Latest revision as of 23:54, 11 September 2008

UartAsyncDriver.png

The UART driver is a char-oriented driver designed to work with a National Semiconductor 16550 UART. The driver is responsible for receiving and sending bytes of data asynchronously.

The UART driver is divided into two sections: an upper half and a lower half. The two halves communicate via semaphores and buffers. The lower half is interrupt driven and interacts with the physical hardware. The upper half of the driver interacts with user programs. It does not interact directly with the hardware nor does it spinlock while waiting for the hardware to be ready. The upper half waits on semaphores which are signaled by the lower half to indicate bytes of data or free space are avaialable in the appropriate buffer.

Physical UART

The XINU backends have been equipped with serial ports that are representative of the National Semiconductor 16550 UART. Documentation on the 16550 UART can be found at http://www.national.com/ds.cgi/NS/NS16C552.pdf.

Initialization

Intialize defines the starting values for all members of the control block: statistical counts are zeroed, buffers are defined, and semaphores are allocated. Also part of the initialization process is setting values in the control and status registers:

  • Line control is set to 8 bit, no parity, 1 stop.
  • Receiver FIFO full, transmitt buffer empty, and receiver line status interrupts are enabled.
  • Hardware FIFOs are enabled.
  • Divisor Latch bits (high and low)
    • The divisor can be calculated by using the formula:
<math>divisor=\frac{baud\_base+\frac{baud\_rate}{2}}{baud\_rate}.</math>

Where baud_rate is the speed you wish to connect at (typically 115,200) and

<math>baud\_base=\frac{clockrate}{16}</math>.

The clockrate should be measured in hertz and may not be equivalent to the clockspeed. (The WRT54GL, for example, has a hard-coded clockrate of 20,000,000 or 20MHz, while the WRT54G has a clockrate of about 25MHz.)

Upper Half (Read and Write)

Read is part of the upper half of the driver that fills a user supplied buffer with bytes from the input buffer filled by the lower half of the driver. If the input buffer is empty, read waits for the lower half to signal on the input semaphore and indicate bytes are avaiable in the input buffer.

Write is part of the upper half of the driver and places bytes from a user supplied buffer into the output buffer read by the lower half of the driver. If there is no free space in the output buffer, write waits for the lower half to signal on the output semaphore and indicate free space is available in the output buffer.

Lower Half (Interrupt Handler)

The interrupt handler is the lower half of the driver. The 16550 UART sends an interrupt (if enabled) when the transmitter FIFO is empty or the receiver FIFO has reached its available bytes tigger level. Three different types of interrupts are handled by the lower half:

  • Line or modem status: The interrupt is merely noted in the UART's statistical counts.
  • Receiver hardware FIFO trigger level: The driver moves bytes from the UART's receive hardware FIFO into the input buffer. Received bytes are read from the UART until the Data Ready bit in the Line Status Register is no longer set. The input semaphore is signaled to let the upper half know bytes of data are in the input buffer.
  • Transmitter hardware FIFO empty: The lower half fills the UART's transmit hardware FIFO from the output buffer. The interrupt handler fills the transmit hardware FIFO until the FIFO is full or the output buffer is empty. The output semaphore is signaled to let the upper half know bytes of space are available in the output buffer.

Control

The control functions are used to set, clear, and get the input and output flags for the UART driver. Non-blocking flags indicate the upper half read and write functions should perform as much of the requested read or write length as possible, but should not block to wait for the lower half to fill or empty the input or output buffers. When the echo input flag is set, the UART outputs every byte as it is received in addition to placing the byte in the input buffer.

Loopback

UART_ENABLE_LOOPBACK and UART_DISABLE_LOOPBACK control functions enable and disable hardware loopback. Be aware that loopback is precarious and must be used carefully. It is recommended that you turn off interrupts prior to enabling loopback and after disabling loopback to avoid interleaving output while in loopback mode.

Prior to enabling and disabling hardware loopback, the control function ensures the transmitter is completely empty and has completed all previous transmission. When in loopback mode the hardware does not throw interrupts, so the control functions call the UART interrupt handler explicitly.

See also