Difference between revisions of "UART Driver"

From Embedded Xinu
Jump to navigation Jump to search
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.
+
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 still needs work in the area of utilizing the FIFO capabiities of the 16550 UART.
== Driver structure ==
+
== Conceptual 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.
+
The UART driver is divided into two sections: an upper half and a lower half.  The two halves communicate via semaphores and buffers.
  
  struct uart                            /* Control block for the        */
+
[[Image:UartAsyncDriver.png]]
  {                                      /*  16550 UART                  */
+
 
    struct uart_csreg *uart_csr;          /* control and status registers */
+
The lower half is interrupt driven and interacts with the physical hardware.  The 16550 UART sends an interrupt (if enabled) when the transmitter is empty or the receiver has a byte.  The lower half of the driver handles these interrupts.  When data is received, the lower half places the received byte into the input buffer and signals on the input semaphore to make the upper half aware another byte is in the input buffer. When a transmitter empty interrupt occurs, the lower half moves bytes from the output buffer into the transmitter and signals the output semaphore to make the upper half aware another free byte is in the output buffer.
    struct dentry    *uart_dev;          /* dev structure                */
+
 
    struct tty        *uart_tty;          /* tty associated with the UART */
+
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.  Read removes data from the input buffer and places it in a user supplied buffer and returns.  Write places data into the output buffer from a user supplied buffer and returns.
    /* Statistical Counts */
+
 
    int              uart_cout;          /* characters output            */
+
== Hardware ==
    int              uart_cin;          /* characters input             */
+
The following structure represents the hardware control and status registers:
    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:
+
   /* Control and status registers for the 16550 UART. */
   struct uart_csreg                      /* Control and status registers */
+
  struct uart_csreg
  {                                      /*  for the 16550 UART.         */
+
  {
 
     volatile unsigned char uart_buffer;  /* receive buffer (read only) & */
 
     volatile unsigned char uart_buffer;  /* receive buffer (read only) & */
 
                                           /*  transmit hold (write only)  */
 
                                           /*  transmit hold (write only)  */
Line 43: Line 24:
 
     volatile unsigned char uart_lsr;      /* line status                  */
 
     volatile unsigned char uart_lsr;      /* line status                  */
 
     volatile unsigned char uart_msr;      /* modem status                */
 
     volatile unsigned char uart_msr;      /* modem status                */
    volatile unsigned char uart_scr;      /* scratch                      */
+
  volatile unsigned char uart_scr;      /* scratch                      */
 
   };
 
   };
 +
 
Some registers are also defined with additional or alternative names for coherent reference.
 
Some registers are also defined with additional or alternative names for coherent reference.
 +
 +
  /* Alternative names for control and status registers */
 
   #define uart_rbr uart_buffer            /* receive buffer (read only)  */
 
   #define uart_rbr uart_buffer            /* receive buffer (read only)  */
 
   #define uart_thr uart_buffer            /* transmit hold (write only)  */
 
   #define uart_thr uart_buffer            /* transmit hold (write only)  */
Line 52: Line 36:
 
   #define uart_dlm uart_ier              /* divisor latch high byte      */
 
   #define uart_dlm uart_ier              /* divisor latch high byte      */
  
== Modes ==
+
See [[National Semiconductor 16550 UART]] for more details about the hardware.
=== 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:
+
== Control block ==
  DEVCALL uartReadSync(struct dentry *dptr, unsigned char *buf, int len);
+
The UART driver uses a structure to track all buffers, semaphores, and counts associated with the UART.
  DEVCALL uartWriteSync(struct dentry *pdev, unsigned char *buf, int len);
 
  
=== Asynchronous ===
+
  /* UART 16550 control block */
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.
+
  struct uart
 +
  {
 +
    /* Pointers to associated structures */
 +
    struct uart_csreg *uart_csr;          /* control and status registers */
 +
    struct dentry    *uart_dev;          /* dev structure                */
 +
    /* 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            */
 +
    /* UART input fields */
 +
    unsigned char    uart_iflags; /* Input flags         */
 +
    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              */
 +
    /* UART output fields */
 +
    unsigned char    uart_oflags; /* Output flags         */
 +
    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        */
 +
  };
 +
 
 +
== Functionality ==
 +
=== Intialize ===
 +
  /* Initialize structure */
 +
  DEVCALL uartInit(struct dentry *pdev)
 +
 
 +
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 intialization process is setting values in the control and status registers:
 +
* A baud divisor of 0x000B (11d) is set; assuming x16 clock factor, that gives a base crystal frequency of about 20.275 MHz.
 +
* 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.
  
[[Image:UartAsyncDriver.png]]
+
=== Read (Upper Half) ===
 +
  /* Read into user buffer (Upper half)  */
 +
  DEVCALL uartRead(struct dentry *pdev, unsigned char *buf, int len)
 +
  /* Read a single character (calls uartRead; Upper Half) */
 +
  DEVCALL uartGetChar(struct dentry *pdev)
  
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.
+
=== Write (Upper Half) ===
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.
+
  /* Write from user buffer (Upper Half) */
 +
  DEVCALL uartWrite(struct dentry *pdev, unsigned char *buf, int len)
 +
  /* Write a signle character (calls uartWrite; Upper Half) */
 +
  DEVCALL uartPutChar(struct dentry *pdev, unsigned char ch)
  
The asynchronous functions are:
+
=== Interrupt Handler (Lower Half) ===
  void uartIntr(void);
+
   /* Interrupt handler (Lower Half) */
   DEVCALL uartReadAsync(struct dentry *dptr, unsigned char *buf, int len);
+
   void uartIntr(void)
   DEVCALL uartWriteAsync(struct dentry *pdev, unsigned char *buf, int len);
 
  
=== Switching modes ===
+
=== Control ===
The UART driver determines which mode to use by setting and reading input and output flags. 
+
  /* Control */
 +
  DEVCALL uartControl(struct dentry *pdev, int func, unsigned char arg1, unsigned char arg2)
  
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.
+
  /* uartControl() functions */
 +
  #define UART_IOC_SETIFLAG CHAR_IOC_SETIFLAG    /* set input flags */
 +
  #define UART_IOC_CLRIFLAG CHAR_IOC_CLRIFLAG    /* clear input flags */
 +
  #define UART_IOC_GETIFLAG CHAR_IOC_GETIFLAG    /* get input flags */
 +
  #define UART_IOC_SETOFLAG CHAR_IOC_SETOFLAG    /* set output flags    */
 +
  #define UART_IOC_CLROFLAG CHAR_IOC_CLROFLAG    /* clear output flags  */
 +
  #define UART_IOC_GETOFLAG CHAR_IOC_GETOFLAG    /* get output flags
  
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.
+
  /* UART input flags */
 +
  #define UART_IFLAG_NOBLOCK CHAR_IFLAG_NOBLOCK    /* do non-blocking input  */
 +
  #define UART_IFLAG_ECHO        CHAR_IFLAG_ECHO      /* echo input              */
  
== Control ==
+
  /* UART output flags */
''This section needs updating''
+
  #define UART_OFLAG_NOBLOCK CHAR_OFLAG_NOBLOCK    /* do non-blocking output  */
  
 
== See also ==
 
== See also ==

Revision as of 18:57, 18 June 2007

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 still needs work in the area of utilizing the FIFO capabiities of the 16550 UART.

Conceptual structure

The UART 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 physical hardware. The 16550 UART sends an interrupt (if enabled) when the transmitter is empty or the receiver has a byte. The lower half of the driver handles these interrupts. When data is received, the lower half places the received byte into the input buffer and signals on the input semaphore to make the upper half aware another byte is in the input buffer. When a transmitter empty interrupt occurs, the lower half moves bytes from the output buffer into the transmitter and signals the output semaphore to make the upper half aware another free byte is in the output buffer.

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. Read removes data from the input buffer and places it in a user supplied buffer and returns. Write places data into the output buffer from a user supplied buffer and returns.

Hardware

The following structure represents the hardware control and status registers:

 /* Control and status registers for the 16550 UART. */
 struct uart_csreg
 {
   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.

 /* Alternative names for control and status registers */
 #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      */

See National Semiconductor 16550 UART for more details about the hardware.

Control block

The UART driver uses a structure to track all buffers, semaphores, and counts associated with the UART.

 /* UART 16550 control block */
 struct uart
 {
   /* Pointers to associated structures */
   struct uart_csreg *uart_csr;          /* control and status registers */
   struct dentry     *uart_dev;          /* dev structure                */
   /* 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             */
   /* UART input fields */
   unsigned char     uart_iflags;	/* Input flags		        */
   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               */
   /* UART output fields */
   unsigned char     uart_oflags;	/* Output flags		        */
   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        */
 };

Functionality

Intialize

 /* Initialize structure */
 DEVCALL uartInit(struct dentry *pdev)

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 intialization process is setting values in the control and status registers:

  • A baud divisor of 0x000B (11d) is set; assuming x16 clock factor, that gives a base crystal frequency of about 20.275 MHz.
  • 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.

Read (Upper Half)

 /* Read into user buffer (Upper half)  */
 DEVCALL uartRead(struct dentry *pdev, unsigned char *buf, int len)
 /* Read a single character (calls uartRead; Upper Half) */
 DEVCALL uartGetChar(struct dentry *pdev)

Write (Upper Half)

 /* Write from user buffer (Upper Half) */
 DEVCALL uartWrite(struct dentry *pdev, unsigned char *buf, int len) 
 /* Write a signle character (calls uartWrite; Upper Half) */
 DEVCALL uartPutChar(struct dentry *pdev, unsigned char ch)

Interrupt Handler (Lower Half)

 /* Interrupt handler (Lower Half) */
 void uartIntr(void)

Control

 /* Control */
 DEVCALL uartControl(struct dentry *pdev, int func, unsigned char arg1, unsigned char arg2)
 /* uartControl() functions  */
 #define	UART_IOC_SETIFLAG	CHAR_IOC_SETIFLAG    /* set input flags	 */
 #define	UART_IOC_CLRIFLAG	CHAR_IOC_CLRIFLAG    /* clear input flags	 */
 #define	UART_IOC_GETIFLAG	CHAR_IOC_GETIFLAG    /* get input flags	 */
 #define	UART_IOC_SETOFLAG	CHAR_IOC_SETOFLAG    /* set output flags    */
 #define	UART_IOC_CLROFLAG	CHAR_IOC_CLROFLAG    /* clear output flags  */
 #define	UART_IOC_GETOFLAG	CHAR_IOC_GETOFLAG    /* get output flags
 /* UART input flags */
 #define	UART_IFLAG_NOBLOCK	CHAR_IFLAG_NOBLOCK    /* do non-blocking input   */
 #define UART_IFLAG_ECHO         CHAR_IFLAG_ECHO       /* echo input              */
 /* UART output flags */
 #define	UART_OFLAG_NOBLOCK	CHAR_OFLAG_NOBLOCK    /* do non-blocking output  */

See also