UART Driver

From Embedded Xinu
Jump to navigation Jump to search

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.

Driver structure

The UART driver uses a structure to track all buffers, semaphores, and counts associated with the UART. Certain fields pertain only to asynchronous mode.

 struct uart                             /* Control block for the        */
 {                                       /*  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 character	  */
   unsigned char     uart_in[IBLEN];     /* input buffer                 */
   /* Asynchronous output fields */
   SEMAPHORE         uart_osema;	  /* I/0 semaphore for uart output*/
   unsigned short    uart_ostart;	  /* index of first character	  */
   unsigned char     uart_out[IBLEN];    /* output buffer                */
   int               uart_oidle;         /* UART transmitter idle        */
 };

The following structure represents the hardware registers:

 struct uart_csreg                       /* Control and status registers */
 {                                       /*  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

Synchronous

In synchronous mode, the driver spinlocks to wait for the hardware. The driver continuously checks the Line Status register to determine when the hardware has received a byte or is ready to transmit a byte. Synchronous mode does not rely on interrupts or buffers.

The synchronous functions are:

 DEVCALL uartReadSync(struct dentry *dptr, unsigned char *buf, int len);
 DEVCALL uartWriteSync(struct dentry *pdev, unsigned char *buf, int len);

Asynchronous

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. UartAsyncDriver.png 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 buffer. The 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. 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 asynchronous functions are:

 void uartIntr(void);
 DEVCALL uartReadAsync(struct dentry *dptr, unsigned char *buf, int len);
 DEVCALL uartWriteAsync(struct dentry *pdev, unsigned char *buf, int len);

Switching modes

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 switch. It also needs to set the proper flags and disable the proper interrupts on the hardware.

See also