The UART driver module

Introduction - - UART drivers are modules which contain all the hardware level driver code for hardware which performs Universal Asynchronous Receiver Transmitter functions - or in lay-man's terms, serial input and output.

Common UARTs include the 16550 and antique 8250. Both at a minimal level allow the input and output of characters across a serial line. However, debuggers may require the use of the only serial port on some hardware, and so hence a UART driver module may indirect console i/o through the debugger.

Also, hardware like an ethernet controller or a parallel port can function as a UART, so these driver modules are not restricted to merely serial hardware. You could even drive an LCD panel or VGA display as a UART.

Furthermore, as there may be more than one UART controller of the same type in your hardware (often there are two serial controllers in development hardware), the driver keeps its internal data within a context block. This allows the same code to drive multiple controllers of the same type easily.

Callability
C:  #include "UARTs/<name>/driver.h"
HALError
Module name:  "????UART" (0x????????, 0x54524155)
Codes:
0:  (0x00):  Failed to initialise hardware
1:  (0x01):  Failed to finalise hardware
2:  (0x02):  Data overrun
3:  (0x03):  Framing error
4:  (0x04):  Parity error
5:  (0x05):  Timeout

The first four letters of the module name should be some four letter mnemonic representing the chosen name of the driver.

Portability API is portable
Code is portable
Constituents Usually written in ANSI C for portability
Porting notes All non-portable information will be in the common.h file held in the specific port directory. Some problems may occur with the packing of items within structures or bit orientation of the host processor
Other Notes None
General use Because each UART driver is different, there are different prefixes before each call provided by the module. However, the postfixes and function of each call are the same across every UART driver.

Prefixes can be anything you want, but it is good form to maintain some sort of convention. We suggest <acronym>UARTDriver_<API postfix> eg; 16550UARTDriver_Initialise.

It is expected that most UART driver modules will be written entirely in C to aid portability (hence one can change the processor but retain the remainder of the hardware). However, if the module is written in C, calls may only be made when the C run-time system is operational (ie; main memory and C library working and initialised). Hence, one may wish to write the driver partly or wholly in assembler to prevent these limitations - this can be especially relevent when debugging early boot code.

Interrupt driven i/o is not expected to be implemented within the NedHAL API framework, but DMA driven i/o is permitted and encouraged. This is because interrupt driven i/o requires substantial support from other areas of NedHAL which are non-portable, and hence the UART code would become unportable. You can by all means add your own API's to do interrupt driven i/o to the same module source, or else have an internal switch to interrupt driven i/o after the operating system is ready to handle it - but the driver module must start up in a fashion which does not require interrupt support. Remember that debug i/o is best done in a polled fashion in case the operating system is too screwed up to do it via interrupts.

To initialise and finalise the operation of this module, the usual calls <name>_Initialise and <name>_Finalise are provided. The API <name>_Set sets the future operation of the UART. The API's <name>_WriteN and <name>_ReadN perform output and input to the UART. The optional API <name>_GetIOStatus returns the current buffer status of the UART.

 

<name>_Initialise

Purpose - - Initialises the UART driver module for subsequent use
Prototype HALError *<name>_Initialise(HALUARTBlk *contextblk, void *hardwareaddr, int uartid)
Exit Null if no error occurred, pointer to valid HALError structure otherwise
Interrupts IRQ is unchanged
FIQ is unchanged
Processor Mode Unchanged
Staticity Not static
Use This call initialises the HALUARTBlk pointed to by contextblk with appropriate data to drive the UART hardware at base address hardwareaddr. You may assign a driver-dependent identification number in uartid - this may be necessary for internal driver purposes.

You will need to keep the context block set up by this call valid at all times until you have finalised its operation as DMA driven i/o may need to use the block to perform its task.

Notes None

 

<name>_Finalise

Purpose     Deinitialises the UART driver module
Prototype HALError *<name>_Finalise(HALUARTBlk *contextblk)
Exit Null if no error occurred, pointer to valid HALError structure otherwise
Interrupts IRQ is unchanged
FIQ is unchanged
Processor Mode Unchanged
Staticity Not static
Use This call deinitialises the hardware specified by contextblk. You may dispose of contextblk after this call returns.
Notes None

 

<name>_Set

Purpose     Reconfigures the UART driver
Prototype HALError *<name>_Set(HALUARTBlk *contextblk, u32 baud, u8 bits, u8 parity, u8 stopbits)
Exit Null if no error occurred, pointer to valid HALError structure otherwise
Interrupts IRQ is unchanged
FIQ is unchanged
Processor Mode Unchanged
Staticity Not static
Use This call changes the settings of the hardware specified by contextblk to run at baud bits per second, to use bits bits per byte and to use stopbits stop bits per byte sent.

The only parameter which should need further explanation is parity. This can take one of the following values:

  • 0: use no parity
  • 1: use odd parity
  • 2: use even parity
Notes None

 

<name>_WriteN

Purpose     Writes a a certain number of characters to the UART driver
Prototype HALError *<name>_WriteN(HALUARTBlk *contextblk, char *buffer, int length)
Exit Null if no error occurred, pointer to valid HALError structure otherwise
Interrupts IRQ is unchanged
FIQ is unchanged
Processor Mode Unchanged
Staticity Not static
Use This call writes length characters from the character array pointed to by buffer to the hardware specified by contextblk.

Note that this call may or may not return before all or part of the actual write takes place. Use <name>_GetIOStatus to determine current input and output status. If the write does take place asynchronously, the buffer passed to this call is copied to an internal buffer and hence you may dispose of the local buffer upon return of this call.

Notes None

 

<name>_ReadN

Purpose     Reads a a certain number of characters from the UART driver
Prototype HALError *<name>_ReadN(HALUARTBlk *contextblk, char *buffer, int length)
Exit Null if no error occurred, pointer to valid HALError structure otherwise
Interrupts IRQ is unchanged
FIQ is unchanged
Processor Mode Unchanged
Staticity Not static
Use This call reads length characters into the character array pointed to by buffer from the hardware specified by contextblk.

This call will always wait until all the characters have been read. Use <name>_GetIOStatus to determine how many characters are awaiting to be read.

Notes None

 

<name>_GetIOStatus

Purpose     Returns the current input and output status' of the UART driver
Prototype HALError *<name>_GetIOStatus(HALUARTBlk *contextblk, s32 *txsize, s32 *rxsize)
Exit Null if no error occurred, pointer to valid HALError structure otherwise
Interrupts IRQ is unchanged
FIQ is unchanged
Processor Mode Unchanged
Staticity Not static
Use This call sets the s32's pointed to by txsize and rxsize to the characters remaining in output buffer and the input buffer respectively of the hardware specified by contextblk.

If either the pointer's txsize or rxsize are null on entry, those values are not set. If the values returned are -1, this indicates a value which is not zero (ie; the driver does not know how many bytes there are but it does know that it isn't none).

Notes This call may not be provided by some UART driver modules