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.
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 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.
See National Semiconductor 16550 UART for more details about the hardware.
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 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 (Upper Half)
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.
Interrupt Handler (Lower Half)
The interrupt handler is the lower half of the driver. A line status interrupt is merely noted in the UART's statistical counts. A receive interrupt 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. A transmit interrupt 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 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.
/* UART input flags */ #define UART_IFLAG_NOBLOCK 0x0001 /* do non-blocking input */ #define UART_IFLAG_ECHO 0x0002 /* echo input */ /* UART output flags */ #define UART_OFLAG_NOBLOCK 0x0001 /* do non-blocking output */