Handling a UART interrupt A look at some recent changes in the Linux kernel’s programming interface for device-interrupts.

Download Report

Transcript Handling a UART interrupt A look at some recent changes in the Linux kernel’s programming interface for device-interrupts.

Handling a UART interrupt

A look at some recent changes in the Linux kernel’s programming interface for device-interrupts

The new ‘anchor’ cluster

• To reduce contention for CS workstations that have null-modem cables attached, we are bringing online an additional cluster of eight servers – you access them remotely ‘anchor00’ ‘anchor01’ ‘anchor02’ ‘anchor04’ ‘anchor03’ ‘anchor05’ ‘anchor06’ ‘anchor07’

Thanks to overnight efforts by Alex Fedosov and our CS support-team!

LDD3: kernel 2.6.10

• Our text “

Linux Device Drivers (3 rd Ed)”

is published by O’Reilly in February 2005 • The kernel version it covers is 2.6.10

• But in our classroom we are using a more recent version of the kernel (i.e., 2.6.22.5) which was released in mid-August 2007 • Various changes (improvements) are now implemented (and differ from LDD3 book)

Example 1

• Our textbook shows the prototype for a device driver’s interrupt service routine: irqreturn_t

isr

( int irq, void *dev_id, struct pt_regs *regs ); • But this has changed in kernel 2.6.22.5 to: Irqreturn_t

isr

( int irq, void *dev_id ); • What prompted the kernel developers to remove that third function-argument? • Just a guess, but probably it was because programmers were not actually using it

Example 2

• The kernel’s header-files provided symbolic names for important interrupt-related constants in 2.6.10: #define SA_SHIRQ 0x04000000 • Such definitions, formerly replicated for each supported CPU architecture, have now been consolidated (and renamed) in the kernel header-file : #define IRQF_SHARED 0x00000080

Consequences

• If you try to apply the textbook discussion about interrupt-handlers to your LKMs for this class, the compiler will generate error messages and/or warning messages • So you will need to use the “new” kernel’s interfaces, not documented in our textbook • Maybe you can locate an online tutorial, or look at other device drivers’ source-code

Our ‘uartintr.c’ module

• We have written a kernel module that you can study (and experiment with) showing an interrupt-handler for the UART device that we created purely for demonstration purposes using kernel version 2.6.22.5 • Obviously you would need to modify it if you wanted to use an interrupt-handler in your solution for our course’s Project #2

Module’s components

The LKM layout the usual pair of module-administration functions The isr function init exit module’s ‘payload’ is just a single callback-function that will ‘handle’ a UART interrupt

registers the ‘isr’ and then enables the UART device to generate interrupt-signals disables the UART’s interrupt-signals and then unregisters this module’s ‘isr’

Interrupt Identification Register

7 6 5 4 3 2 1 0 0 0 00 = FIFO-mode has not been enabled 11 = FIFO-mode is currently enabled ‘highest priority’ UART interrupt still pending highest 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

An interrupt service routine

• Whenever the UART receives a new byte of data, it will transmit it back to the sender #include #include #define UART_BASE 0x03F8 } { irqreturn_t

my_uart_isr

( int irq, void *dev_id ) int intr_identify = inb( UART_BASE + 2 ) & 0x0F; if ( intr_identify == 0x01 ) return IRQ_NONE; if ( intr_identify == 0x04 ) // a new character has arrived outb( inb( UART_BASE ), UART_BASE ); return IRQ_HANDLED;

Installing the ‘isr()’

• Here is how your module asks the kernel to execute your UART interrupt-handler: #define UART_IRQ 4 // signal line’s number to the IO-APIC char modname[] = “uartintr”; // kernel displays this in ‘/proc/interrupts’ } { static int __init

my_init

( void ) … if (

request_irq

( UART_IRQ, &my_uart_isr, IRQF_SHARED, modname, &modname ) < 0 ) return –EBUSY; … // your code to enable the UART’s interrupts goes here … return 0; // SUCCESS

Interrupt Enable Register

7 6 5 4 3 2 1 0 0 0 0 0

Modem Status change Rx Line Status change THR is empty Received data is 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 RCVR FIFO trigger-level

reserved reserved DMA Mode select

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

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

Modem Control Register

7 6 5 4 3 2 1 0 0 0 0 LOOP BACK OUT2 OUT1 RTS DTR 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)

UART initialization

• Here is code that initializes the UART (its baud-rate and data-format) and enables it to generate ‘character received’ interrupts: // initialize the UART device for the desired demo operations outb( 0x01, UART_BASE + 1 ); // issue RDR interrupts outb( 0x00, UART_BASE + 2 ); outb( 0x80, UART_BASE + 3 ); // turn off FIFO-mode // SET DLAB=1 outw( 0x0001, UART_BASE ); outb( 0x03, UART_BASE + 3 ); outb( 0x0B, UART_BASE + 4 ); // DIVISOR_LATCH = 1 // data-format is 8-N-1 // DSR=1, RTS=1, OUT2=1

Disabling UART interrupts

• Here is code that disables any more UART interrupts, so that your module’s ‘cleanup’ can safely remove your interrupt-handler: { static __exit

my_exit

( void ) … // disable any further UART interrupt-requests outb( 0x00, UART_BASE + 1 ); outb( 0x00, UART_BASE + 4 ); // INTERRUPT_ENABLE // MODEM_CONTROL // remove your UART interrupt-service routine

free_irq

( UART_IRQ, modname ); … }

In-class exercise

• Try running our ‘trycable.cpp’ application on an adjacent workstation after you have downloaded, compiled, and installed our ‘uartintr.c’ demo-module • What do you see on the two screens?

• Tonight’s class ends early -- so that you can attend the ACM Chapter’s Pizza Night