0% found this document useful (0 votes)
33 views

The UART Project: Applying What We've Learned About Linux Device-Drivers To The PC's Serial-Port Controller

The document describes a project to create a Linux device driver for a UART serial port controller. The project aims to give experience with hardware device drivers by having students implement driver methods to allow programs to communicate between PCs over a null-modem serial cable through the UART device file. Details are provided on the UART hardware, registers, baud rate configuration, and the driver methods needed like write and read.

Uploaded by

Pradeep Kumar
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views

The UART Project: Applying What We've Learned About Linux Device-Drivers To The PC's Serial-Port Controller

The document describes a project to create a Linux device driver for a UART serial port controller. The project aims to give experience with hardware device drivers by having students implement driver methods to allow programs to communicate between PCs over a null-modem serial cable through the UART device file. Details are provided on the UART hardware, registers, baud rate configuration, and the driver methods needed like write and read.

Uploaded by

Pradeep Kumar
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 43

The UART project

Applying what we’ve learned


about Linux device-drivers to the
PC’s serial-port controller
Project’s purpose
• Allow you to gain experience encountering
the kinds of issues that commonly arise in
crafting software to operate real hardware:
– Learning the hardware device’s capabilities
– Deciding which driver-methods to implement
– Accommodating your platform’s interfaces
– Exploiting the OS kernel’s support-functions
– Devising a strategy for testing and debugging
Universal Asynchronous
Receiver-Transmitter
(UART)
See our CS635 course website at:

<http://cs.usfca.edu/~cruse/cs635f07>

for links to the UART manufacturer’s documentation


and to an in-depth online programming tutorial
Tx and Rx
• The UART has a transmission-engine, and
also a reception-engine, which are able to
operate simultaneously (i.e., “full-duplex”)
• Software controls the UART’s operations
by accessing several registers, using the
x86 processor’s ‘in’ and ‘out’ instructions
• Linux provides some convenient ‘macros’
that ‘hide’ the x86 machine-code details
PC-to-PC communications

student student
workstation workstation

KVM cable KVM cable

rackmount rackmount
PC system ‘null-modem’ serial cable PC system

ethernet cables
Using ‘echo’ and ‘cat’
• Your device-driver module (named ‘uart.c’)
is intended to allow unprivileged programs
that are running on a pair of adjacent PCs
to communicate via a “null-modem” cable
Transmitting… Receiving…

$ echo Hello > /dev/uart


$ cat /dev/uart
$_
Hello _
Kudlick Classroom

08 09 10 15 16 17 18 19 20 28 29 30

04 05 06 07 11 12 13 14 24 25 26 27

01 02 03 21 22 23

lectern

Indicates a “null-modem” PC-to-PC serial cable connection


Linux char-driver components
Device-driver LKM layout

module’s ‘payload’
function
is a collection of
function callback-functions
having prescribed
prototypes

function AND

...
a ‘package’ of
fops
function-pointers

the usual pair of init registers the ‘fops’


module-administration
functions exit unregisters the ‘fops’
Requires a device-file node
• Our System Administrator has created the
device-file needed for your driver-module:
root# mknod /dev/uart c 84 0
root# chmod a+w /dev/uart
• Your driver-module needs to ‘register’ your
package of driver-methods (i.e., functions)
in its initialization routine (and ‘unregister’
them later in its cleanup routine)
‘write()’ and ‘read()’
• Obviously your driver-module’s ‘payload’
will have to include ‘methods’ (functions)
which perform the ‘write()’ and ‘read()’
operations that applications will invoke
• You may decide your driver needs also to
implement certain additional ‘methods’
• A little history is helpful for understanding
some of the UART device’s terminology
DCE and DTE
• Original purpose of the UART was for PCs
to communicate via the telephone network
• Telephones were for voice communication
(analog signals) whereas computers need
so exchange discrete data (digital signals)
• Special ‘communication equipment’ was
needed for doing the signal conversions
(i.e. a modulator/demodulator, or modem)
PC with a modem
computer
terminal
modem

serial
cable
Data Data
phone Communications Terminal
wire
Equipment Equipment
(DCE) (DTE)
Serial data-transmission
The Transmitter Holding Register (8-bits) Software outputs a byte
of data to the THR
0 1 1 0 0 0 0 1
The bits are immediately
copied into an internal
‘shift’-register

The bits are shifted out,


one-at-a-time, in sync
with a clock-pulse

0 1 1 0 0 0 0 1 1-0-1-1-0-0-0-0-1-0

The transmitter’s internal ‘shift’ register data-bits

stop start
clock clock-pulses bit bit
trigger bit-shifts
Serial data reception
input voltage

clock
clock-pulses trigger
voltage-sampling
and bit-shifts
at regular intervals The receiver’s internal ‘shift’ register

1-0-1-1-0-0-0-0-1-0 0 1 1 0 0 0 0 1
data-bits

stop start
bit bit

Software can input


the received byte
0 1 1 0 0 0 0 1
from the RBR
The Receiver Buffer Register (8-bits)
Normal 9-wire serial cable

1 Carrier Detect 1
6 6
Data Set Ready
Rx data

Request To Send
Tx data

Clear To Send
Data Terminal Ready

Ring Indicator
9 9
Signal Ground
5 5
Signal functions
• CD: Carrier Detect The modem asserts
this signal to indicate that it successfully
made its connection to a remote device
• RI: Ring Indicator The modem asserts
this signal to indicate that the phone is
ringing at the other end of its connection
• DSR: Data Set Ready Modem to PC
• DTR: Data Terminal Ready PC to Modem
Signal functions (continued)
• RTS: Request To Send PC is ready for
the modem to relay some received data
• CLS: Clear To Send Modem is ready for
the PC to begin transmitting some data
9-wire null-modem cable
CD CD
RxD RxD
TxD TxD
GND GND
DSR DSR
DTR DTR
RTS RTS
CTS CTS
RI RI
Data Data
Terminal Terminal
Equipment Equipment
no modems
The 16550 UART registers
Base+0 Divisor Latch Register 16-bits (R/W)

Base+0 Transmit Data Register 8-bits (Write-only)

Base+0 Received Data Register 8-bits (Read-only)

Base+1 Interrupt Enable Register 8-bits (Read/Write)

Base+2 Interrupt Identification Register 8-bits (Read-only)

Base+2 FIFO Control Register 8-bits (Write-only)

Base+3 Line Control Register 8-bits (Read/Write)

Base+4 Modem Control Register 8-bits (Read/Write)

Base+5 Line Status Register 8-bits (Read-only)

Base+6 Modem Status Register 8-bits (Read-only)

Base+7 Scratch Pad Register 8-bits (Read/Write)


Rate of data-transfer
• The standard UART clock-frequency for
PCs equals 1,843,200 cycles-per-second
• Each data-bit consumes 16 clock-cycles
• So the fastest serial bit-rate in PCs would
be 1843200/16 = 115200 bits-per-second
• With one ‘start’ bit and one ‘stop’ bit, ten
bits are required for each ‘byte’ of data
• Rate is too fast for ‘teletype’ terminals
Divisor Latch
• The ‘Divisor Latch’ may be used to slow
down the UART’s rate of data-transfer
• Clock-frequency gets divided by the value
programmed in the ‘Divisor Latch’ register
• Older terminals often were operated at a
‘baud rate’ of 300 bits-per-second (which
translates into 30 characters-per-second)
• So Divisor-Latch was set to 0x0180
How timing works
Transmitter clock (bit-rate times 16)

DATA
OUT
start-bit data-bit 0 data-bit 1 …

24 clock-cycles 16 clock-cycles 16 clock-cycles

sample sample

Receiver clock (bit-rate times 16)

receiver detects this high-to-low transition,


so it waits 24 clock-cycles,
then samples the data-line’s voltage
every 16 clock-cycles afterward
Programming interface
The PC uses eight consecutive I/O-ports to access the UART’s registers

0x03F8 0x03F9 0x03FA 0x03FB 0x03FC 0s03FD 0x03FE 0x03FF

RxD/TxD IER IIR/FCR LCR MCR LSR MSR SCR

interrupt line modem


enable status status
register register register
line modem
receive buffer register and scratchpad
control control
transmitter holding register register
register register
(also Divisor Latch register)
interrupt identification register
and FIFO control register
Modem Control Register

7 6 5 4 3 2 1 0

LOOP
0 0 0 OUT2 OUT1 RTS DTR
BACK

Legend:
DTR = Data Terminal Ready (1=yes, 0=no)
RTS = Request To Send (1=yes, 0=no)
OUT1 = not used (except in loopback mode)
OUT2 = enables the UART to issue interrupts
LOOPBACK-mode (1=enabled, 0=disabled)
Modem Status Register

7 6 5 4 3 2 1 0

delta delta delta delta


DCD RI DSR CTS
DCD RI DSR CTS

set if the corresponding bit


has changed since the last
time this register was read

Legend: [---- loopback-mode ----]


CTS = Clear To Send (1=yes, 0=no) [bit 0 in Modem Control]
DSR = Data Set Ready (1=yes, 0=no) [bit 1 in Modem Control]
RI = Ring Indicator (1=yes,0=no) [bit 2 in Modem Control]
DCD = Data Carrier Detected (1=yes,0=no) [bit 3 in Modem Control]
Line Status Register

7 6 5 4 3 2 1 0
Received
Error in Transmitter THR Break Framing Parity Overrun
idle
Data
Rx FIFO empty interrupt error error error
Ready

These status-bits indicate errors in the received data

This status-bit
indicates that the This status-bit indicates that
data-transmission the Transmitter Holding Register
has been completed is ready to accept a new data byte

This status-bit indicates that a new byte of data has arrived


(or, in FIFO-mode, that the receiver-FIFO has reached its threshold)
Line Control Register

7 6 5 4 3 2 1 0
Divisor even number
set stick parity word length
Latch parity of stop
break parity enable selection
access select bits
00 = 5 bits
01 = 6 bits
10 = 7 bits
11 = 8 bits

0 = 1 stop bit
0 = normal 1 = 2 stop bits
1 = ‘break’ 0 = no parity bits
1 = one parity bit
0 = not accessible
1 = even parity
1 = assessible
0 = ‘odd’ parity
Interrupt Enable Register

7 6 5 4 3 2 1 0
Modem Rx Line THR Received
0 0 0 0 Status Status is data is
change change empty available

If enabled (by setting the bit to 1),


the UART will generate an interrupt:
(bit 3) whenever modem status changes
(bit 2) whenever a receive-error is detected
(bit 1) whenever the transmit-buffer is empty
(bit 0) whenever the receive-buffer is nonempty

Also, in FIFO mode, a ‘timeout’ interrupt will be generated if neither


FIFO has been ‘serviced’ for at least four character-clock times
FIFO Control Register

7 6 5 4 3 2 1 0
DMA XMIT RCVR
RCVR FIFO FIFO
reserved reserved Mode FIFO FIFO
trigger-level select reset reset enable

00 = 1 byte
NOTE: DMA is unsupported
01 = 4 bytes
for the UART on our systems
10 = 8 bytes
11 = 14 bytes

Writing 1 empties the FIFO, writing 0 has no effect

Writing 0 will disable the UART’s FIFO-mode, writing 1 will enable FIFO-mode
Interrupt Identification Register

7 6 5 4 3 2 1 0

0 0

‘highest priority’
00 = FIFO-mode has not been enabled
11 = FIFO-mode is currently enabled UART interrupt
highest
still pending
011 = receiver line-status
010 = received data ready
100 = character timeout
001 = Tx Holding Reg empty
000 = modem-status change
lowest

1 = No UART interrupts are pending


0 = At least one UART interrupt is pending
Responding to interrupts
• You need to ‘clear’ a reported interrupt by
taking some action -- depending on which
condition was the cause of the interrupt:
– Line-Status: read the Line Status Register
– Rx Data Ready: read Receiver Data Register
– Timeout: read from Receiver Data Register
– THRE: read Interrupt Identification Register or
write to Transmitter Data Register (or both)
– Modem-Status: read Modem Status Register
Usage flexibility
• A UART can be programmed to operate in
“polled” mode or in “interrupt-driven” mode
• While “Polled Mode” is simple to program
(as we shall show on the following slides),
it does not make efficient use of the CPU
in situations that require ‘multitasking’ (as
the CPU is kept busy doing “polling” of the
UART’s status instead of useful work
How to transmit a byte

Read the Line Status Register

Transmit Holding Register


NO is Empty?

YES

Write byte to the Transmitter Data Register

DONE
How to receive a byte

Read the Line Status Register

Received Data
NO is Ready?

YES

Read byte from the Receiver Data Register

DONE
How to implement in C/C++

// declare the program’s variables and constants


char inch, outch = ‘A’;

// --------------------- Transmitting a byte -------------------


// wait until the Transmitter Holding Register is empty,
// then output the byte to the Transmit Data Register
do { } while ( (inb( LINE_STATUS) & 0x20) == 0 );
outb( outch, TRANSMIT_DATA_REGISTER );

// ---------------------- Receiving a byte ------------------------


// wait until the Received Data Ready bit becomes true,
// then input a byte from the Received Data Register
do { } while ( (inb( LINE_STATUS ) & 0x01 ) == 0 );
inch = inb( RECEIVED_DATA_REGISTER );
How to initialize ‘loopback’ mode

Set the Divisor Latch Access Bit


in the Line Control Register

Write a nonzero value to the Divisor Latch Register

Clear the Divisor Latch Access Bit


and specify the desired data-format
in the Line Control Register

Set the Loopback bit


in the Modem Control Register

DONE
How to adjust the cpu’s IOPL
• Linux provides a system-call to privileged
programs which need to access I/O ports
• The <sys/io.h> header-file prototypes it,
and the ‘iopl()’ library-function invokes it
• The kernel will modify the CPU’s current
I/O Permission Level in cpu’s EFLAGS (if
the program’s owner has ‘root’ privileges)
• So you first execute our ‘iopl3’ command
In-class experiments
• For learning purposes, we can write user-
programs that are able to execute ‘in’ and
‘out’ instructions, and so control the UART
• But to avoid the CPU’s normal ‘segfaults’
we will have to acquire I/O privileges
• The ‘iopl3’ command (created by our CS
System Administrator Alex Fedosov) will
establish the permissions which we need
Experiment #1
• Download and run our ‘testuart.cpp’ demo
• It uses the UART’s ‘loopback’ test mode to
‘receive’ each character that it ‘transmits’

UART ‘loopback’ mode

TxShiftReg TxData

RxShiftReg RxData

Output loops back to become input The external signal-lines are bypased
Experiment #2
• Download and run our ‘uartecho.cpp’ app
(it does nothing untill you do the next step)
• Modify the ‘testuart.cpp’ demo-program by
commenting out the instruction that places
the UART into ‘loopback’ mode
• Execute those two programs on a pair of
PCs that are connected by a null-modem
Experiment #3
• Add a pair of counters to ‘testuart.cpp’:
– Declare two integer variables (initialized to 0)
int txwait = 0, rxwait = 0;
– Increment these in the body of your do-loops
do { ++txwait; } while ( /* Transmitter is busy */ );
do { ++rxwait; } while ( /* Receiver not ready */ );
– Display their totals at the demo’s conclusion
printf( “txwait=%d rxwait=%d \n”, txwait, rxwait );
Experiment #4
• Modify the ‘testuart.cpp’ demo-program to
experiment with using a different baud rate
and a different data-format
• For example, use 300 baud and 7-N-2:
– output 0x0180 to the Divisor Latch register
– output 0x06 to the Line Control register
• Then, to better observe the effect, add the
statement ‘fflush( stdout );’ in the program
loop immediately after ‘printf( “%c”, data);’
Is your cable working?
• We created a diagnostic program (called
‘trycable.cpp’) that you can use to check
whether two PCs are properly connected
with a working “null-modem” cable
• You run ‘uartecho.cpp’ on one of the PCs
and then execute ‘testcable.cpp’ on the
adjacent PC – you will see a message on
both screens if your cable is working OK

You might also like