Transcript SCM-EM CPU

Interrupt Programming in Linux
경희대학교 컴퓨터공학과
조진성
Embedded System Lab. II
주요 내용

주요 내용




인터럽트 개념
PXA255 CPU에서의 인터럽트 하드웨어
리눅스에서의 인터럽트 처리
인터럽트 프로그래밍 예
Embedded System Lab. II
1
인터럽트 인터페이스
임베디드 시스템의 실시간성 요구에 필수적인 요소
CPU
PC
IR
intr request
status
reg
intr ack
data/address
Embedded System Lab. II
data
reg
2
mechanism

인터럽트

Suppose a peripheral intermittently receives data, which must be
serviced by the processor



The processor can poll the peripheral regularly to see if data has
arrived – wasteful
The peripheral can interrupt the processor when it has data
Requires an extra pin or pins: Int



If Int is 1, processor suspends current program, jumps to an Interrupt
Service Routine (ISR)
Known as interrupt-driven I/O
Essentially, “polling” of the interrupt pin is built-into the hardware, so
no extra time!
Embedded System Lab. II
3
인터럽트

What is the address (interrupt address vector) of the ISR?

Fixed interrupt



Vectored interrupt



Address built into microprocessor, cannot be changed
Either ISR stored at address or a jump to actual ISR stored if not enough
bytes available
Peripheral must provide the address
Common when microprocessor has multiple peripherals connected by a
system bus
Compromise: interrupt address table
Embedded System Lab. II
4
인터럽트 기반 I/O(fixed ISR location)
Time
1(a): μP is executing its main program.
1(b): P1 receives input data in a
register with address 0x8000.
2: P1 asserts Int to request
servicing by the
microprocessor.
3: After completing instruction at 100, μP
sees Int asserted, saves the PC’s value of
100, and sets PC to the ISR fixed location
of 16.
4(a): The ISR reads data from 0x8000,
modifies the data, and writes the resulting
data to 0x8001.
4(b): After being read, P1 deasserts Int.
5: The ISR returns, thus restoring PC to
100+1=101, where μP resumes executing.
Embedded System Lab. II
5
인터럽트 기반 I/O(fixed ISR location)
1(a): P is executing its main
program
1(b): P1 receives input data in a
register with address 0x8000.
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
6
μP
Data memory
System bus
Int
PC
P1
P2
0x8000
0x8001
인터럽트 기반 I/O(fixed ISR location)
2: P1 asserts Int to request servicing
by the microprocessor
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
7
μP
Data memory
System bus
Int
PC
P1
P2
0x8000
0x8001
1
인터럽트 기반 I/O(fixed ISR location)
3: After completing instruction at
100, P sees Int asserted, saves
the PC’s value of 100, and sets PC
to the ISR fixed location of 16.
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
8
μP
Data memory
System bus
Int
PC
100
P1
P2
0x8000
0x8001
인터럽트 기반 I/O(fixed ISR location)
4(a): The ISR reads data from
0x8000, modifies the data, and
writes the resulting data to 0x8001.
4(b): After being read, P1 deasserts
Int.
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
9
μP
Data memory
System bus
Int
PC
100
P1
P2
0x8000
0x8001
0
인터럽트 기반 I/O(fixed ISR location)
5: The ISR returns, thus restoring
PC to 100+1=101, where P
resumes executing.
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
10
μP
Data memory
System bus
Int
PC
100
+1
P1
P2
0x8000
0x8001
인터럽트 기반 I/O(vectored interrupt)
Time
1(a): μP is executing its main program.
1(b): P1 receives input data in a
register with address 0x8000.
3: After completing instruction at 100, μP sees Int
asserted, saves the PC’s value of 100, and asserts
Inta.
5(a): μP jumps to the address on the bus (16).
The ISR there reads data from 0x8000, modifies
the data, and writes the resulting data to 0x8001.
6: The ISR returns, thus restoring PC to
100+1=101, where μP resumes executing.
Embedded System Lab. II
11
2: P1 asserts Int to request servicing
by the microprocessor.
4: P1 detects Inta and puts interrupt
address vector 16 on the data bus.
5(b): After being read, P1 deasserts
Int.
인터럽트 기반 I/O(vectored interrupt)
1(a): P is executing its main program
1(b): P1 receives input data in a
register with address 0x8000.
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
12
μP
Data memory
System bus
Inta
Int
PC
100
P1
P2
16
0x8000
0x8001
인터럽트 기반 I/O(vectored interrupt)
2: P1 asserts Int to request servicing by
the microprocessor
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
13
μP
Data memory
System bus
Inta
Int
PC
100
P1
1
P2
16
0x8000
0x8001
인터럽트 기반 I/O(vectored interrupt)
3: After completing instruction at 100,
μP sees Int asserted, saves the PC’s
value of 100, and asserts Inta
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
14
μP
Data memory
System bus
Inta
Int
PC
100
1
P1
P2
16
0x8000
0x8001
인터럽트 기반 I/O(vectored interrupt)
4: P1 detects Inta and puts interrupt
address vector 16 on the data bus
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
15
μP
Data memory
System bus
16
Inta
Int
PC
100
P1
P2
16
0x8000
0x8001
인터럽트 기반 I/O(vectored interrupt)
5(a): PC jumps to the address on the
bus (16). The ISR there reads data
from 0x8000, modifies the data, and
writes the resulting data to 0x8001.
5(b): After being read, P1 deasserts Int.
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
16
μP
Data memory
System bus
Inta
Int
PC
100
P1
0
P2
16
0x8000
0x8001
인터럽트 기반 I/O(vectored interrupt)
6: The ISR returns, thus restoring the
PC to 100+1=101, where the μP
resumes
Embedded System Lab. II
Program memory
ISR
16: MOV R0, 0x8000
17: # modifies R0
18: MOV 0x8001, R0
19: RETI # ISR return
...
Main program
...
100: instruction
101: instruction
17
μP
Data memory
System bus
Int
PC
100
+1
P1
P2
0x8000
0x8001
Interrupt address table

Compromise between fixed and vectored interrupts



One interrupt pin
Table in memory holding ISR addresses (maybe 256 words)
Peripheral doesn’t provide ISR address, but rather index into table


Fewer bits are sent by the peripheral
Can move ISR location without changing peripheral
Embedded System Lab. II
18
Additional interrupt issues

Maskable vs. non-maskable interrupts

Maskable: programmer can set bit that causes processor to ignore interrupt


Non-maskable: a separate interrupt pin that can’t be masked


Important when in the middle of time-critical code
Typically reserved for drastic situations, like power failure requiring immediate backup of
data to non-volatile memory
Jump to ISR

Some microprocessors treat jump same as call of any subroutine


Complete state saved (PC, registers) – may take hundreds of cycles
Others only save partial state, like PC only


Thus, ISR must not modify registers, or else must save them first
Assembly-language programmer must be aware of which registers stored
Embedded System Lab. II
19
PXA255 General Purpose I/O Block Diagram
세부적인 내용은
Embedded SW I
참조
2
GPDR
1 : 출력
0 : 입력
0
1
2
3
Alternate Function
(Output)
3
0x40E0_000C/10/14
Alternate Function
Register(GAFR)
0x40E0_0054/58/5C
0x40E0_0060/64/68
Pin Set
Registers(GPSR)
0x40E0_0060/64/68
Pin Clear
Registers(GPCR)
0x40E0_0060/64/68
Alternate Function
(Input)
2
1
0
Edge
Detect
Power Manager
Sleep Wake-up
logic
Embedded System Lab. II
Pin Direction
Register(GPDR)
20
Base Address
0x40E0_0000
Edge Detect
Status Register(GEDR)
0x40E0_0048/4C/50
Rising Edge Detect
Enable Register(GRER)
0x40E0_0030/34/38
Falling Edge Detect
Enable Register(GFER)
0x40E0_003C/40/44
Pin-Level
Register(GPLR)
0x40E0_0000/04/08
PXA255 Interrupt controller
Interrupt Controller
40D0 0008
Level Register(ICLR)
All Other Qualified
interrupt Bits
0 : IRQ
1 : FIQ
23
CCR[DIM]=0 & IDLE mode=‘1’
40D0 0004
Mask Register (ICMR)
FIQ
CPSR.6(F)
Interrupt Controller
IRQ
Pending Register (ICPR)
Interrupt Controller
40D0 0000
40D0 000C
XScale CORE
Interrupt Controller
Interrupt Source Bit
40D0 0010
23
CPSR.7(I)
IRQ Pending Register (ICIP)
Interrupt Controller
FIQ Pending Register (ICFP)
40D0 0014 : Interrupt controller control register (ICCR)
ICCR.0 : disable idle mask(DIM)
Embedded System Lab. II
21
Interrupt Controller(2)





All interrupts routed to FIQ or IRQ
Two level interrupt structure
1. What module caused interrupt

Serial channel, DMA, Power Management, etc
2. Why did an interrupt occur there?

RX, TX, over-run, under-run, Data Done, Battery Fault, etc
Template for servicing interrupts provided with firmware
Peripheral/PCMCIA interrupt mask in each module
GPIO masks determined per pin or group of pins
Embedded System Lab. II
22
ICMR(Interrupt Controller Mask
Register)
31 30 29 28 27 26 25 24 23 22 21 20
19 18 17 16 15 14 13 12 11 10 9 8 7
6
5
4 3
2 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ? ? 0 0 0 0 0 0 0 ? ? ? ? ? ? ? ?
IM31 – IM28 IM27 – IM24 IM23 – IM20 IM19 – IM17 IM14 – IM12 IM11 – I M8
Reserved
IM[x] Interrupt Mask ‘x’ (where x= 8 through 14 and 17 through 31).
0 – Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager).
1 – Pending interrupt is allowed to become active (interrupts are sent to CPU and Power
Manager).
NOTE: In idle mode, the IM bits are ignored if ICCR[DIM] is cleared.
Reserved[0-7, 15, 16]
Physical Address : 0x40D0/0004
Embedded System Lab. II
23
ICLR(Interrupt Controller Level
Register)
31 30 29 28 27 26 25 24 23 22 21 20
19 18 17 16 15 14 13 12 11 10 9 8 7
6
5
4 3
2 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # # 0 0 0 0 0 0 0 # # # # # # # #
IL31 – IL28 IL27 – IL24 IL23 – IL20 IL19
– IL17
IL14 – IL12 I L 11
– IL8
Reserved
IL[x] Interrupt Level ‘x’ (where n = 8 through 14 and 17 through 31).
0 – Interrupt routed to IRQ interrupt input.
1 – Interrupt routed to FIQ interrupt input.
Reserved[0-7, 15, 16]
Physical Address : 0x40D0/0008
Embedded System Lab. II
24
ICCR(Interrupt Controller Control
Register)
31 30 29 28 27 26 25 24 23 22 21 20
19 18 17 16 15 14 13 12 11 10 9 8 7
6
5
4 3
2 1 0
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???X
Reserved
DIM
DIM[0] Disable Idle mask.
0 – All enabled interrupts bring the processor out of idle mode.
1 – Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle mode.
This bit is cleared during all resets.
Reserved[31:1]
Physical Address : 0x40D0/0014
Embedded System Lab. II
25
ICIP(Interrupt Controller IRQ Pending
Register)
31 30 29 28 27 26 25 24 23 22 21 20
19 18 17 16 15 14 13 12 11 10 9 8 7
6
5
4 3
2 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ? ? 0 0 0 0 0 0 0 ? ? ? ? ? ? ? ?
I P3 1 – I P2 8 I P27 – I P24 I P23 – I P20 I P19 – I P17 I P14 – I P12 I P 1 1 – I P 8
IP[x] : IRQ Pending x (where x = 8 through 14 and 17 through 31).
0 – IRQ NOT requested by any enabled source.
1 – IRQ requested by an enabled source.
Reserved[0-7, 15, 16]
Physical Address : 0x40D0/0000
Embedded System Lab. II
26
Reserved
ICFP(Interrupt Controller FIQ Pending
Register)
31 30 29 28 27 26 25 24 23 22 21 20
19 18 17 16 15 14 13 12 11 10 9 8 7
6
5
4 3
2 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ? ? 0 0 0 0 0 0 0 ? ? ? ? ? ? ? ?
FP31 – FP28 FP27 – FP24 FP23 – FP20 FP19 – FP17
F P 11 – F P 8
FP14 – FP12
FP[x] : FIQ Pending x (where x = 8 through 14 and 17 through 31).
0 – FIQ NOT requested by any enabled source.
1 – FIQ requested by an enabled source.
Reserved[0-7, 15, 16]
Physical Address : 0x40D0/000C
Embedded System Lab. II
27
Reserved
ICPR(Interrupt Controller Pending
Register)
31 30 29 28 27 26 25 24 23 22 21 20
19 18 17 16 15 14 13 12 11 10 9 8 7
6
5
4 3
2 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ? ? 0 0 0 0 0 0 0 ? ? ? ? ? ? ? ?
IS31 – IS28 IS27 – IS24 IS23 – IS20IS19 – IS17



Reserved
a 32-bit read-only register that shows all active interrupts in the system.
These bits are not affected by the state of the mask register (ICMR).
Clearing the interrupt status bit at the source, automatically clears the
corresponding ICPR flag, provided there are no other interrupt status bits
set within the source unit.


I S 11 – I S 8
IS14 – IS12
Table 4-36 참조
Physical Address : 0x40D0/0010
Embedded System Lab. II
28
Why Bottom Halves ?

Because interrupt handlers ….

run asynchronously and thus interrupt other potentially important code(even
other interrupt handlers)

run with current interrupt level disabled at best, and at worst (if
SA_INTERRUPT set) with all interrupts disabled

are often timing-critical because they deal with hardware

cannot block

Consequently, managing interrupts is divided into two parts (or halves)

Some useful tips about how to divide work between top and bottom half




if the work is time-sensitive
if the work is related to the hardware itself
if the work needs to ensure that another interrupt (particularly the
same interrupt) does not interrupt it
Embedded System Lab. II
29
Bottom Halves in Linux 2.4


the future when the system is less busy
and interrupts are enabled again
Three types of bottom halves in 2.4


softirq, tasklet, BH
operations on deferrable functions





initialization
activation
masking
execution
(* activation and execution on the same
CPU ? )
Embedded System Lab. II
30
Bottom Halves in Linux 2.4

History of bottom half status in Linux





BH removed in 2.5
Task queues removed in 2.5
Softirq available since 2.3
Tasklet available since 2.3
Work queues available since 2.5
Embedded System Lab. II
31
Softirqs

Each registered softirq in one entry of softirq_vec[32]

(32 softirqs possible)
/* structure representing a softirq entry */
/* (linux/interrupt.h) */
/* 32-entry array of softirq_action (kernel/softirq.c) */struct softirq_action {
void (*action) (struct softirq_action *);
static struct softirq_action softirq_vec[32];
void *data; /
};

irq_stat
irq_cpustat_t irq_stat[NR_CPUS];
Embedded System Lab. II
typedef struct {
unsigned int __softirq_pending;
unsigned int __local_irq_count;
unsigned int __local_bh_count;
unsigned int __syscall_count;
struct task_struct *__ksoftirqd_task;
unsigned int __nmi_count;
} irq_cpustat_t;
32
Softirqs

Softirq handler

with entire softirq_action as argument (why ?)


(ex) void net_tx_action(struct softirq_action *h) { }
void net_rx_action(struct softirq_action *h) { }
Remember that

A softirq handler run with interrupts enabled and cannot sleep.




softirqs on current CPU are disabled
an interrupt handler can preempt a softirq
Another softirq can run on another CPU.
Four softirqs used in 2.4 (six in 2.6)


HI_SOFTIRQ / TIMER_SOFTIRQ / NET_TX_SOFTIRQ /
NET_RX_SOFTIRQ / SCSI_SOFTIRQ /(…)/ TASKLET_SOFTIRQ
Embedded System Lab. II
33
Softirqs

Registering softirq handler at run-time by open_softirq( )



open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
Raising softirq by raise_softirq( )

(usually, an interrupt handler raises its softirq before returning)


raise_softirq(NET_TX_SOFTIRQ);
Checking for pending softirqs (kernel version / supported hardware)




After processing a hardware interrupt
When one of ksoftirqd_CPUn kernel threads is awoken
When a packet is received on a NIC
When local_bh_enable macro re-enables the softirqs
Embedded System Lab. II
34
Softirqs

Executing softirqs : do_softirq( )
local_irq_count(cpu) = = 0 ? // nested ?
local_bh_count(cpu) = = 0 ? // enabled ?
serializing execution on a CPU
softirq not processed in this invocation ?
// goto restart only if different types
// goto wakeup_softirqd(cpu) if same softirq
re-activated
Embedded System Lab. II
if( in_interrupt( ) ) return;
local_irq_save( flags ); // disable local INT
pending = softirq_pending(cpu);
if( pending ){
mask = ~pending;
local_bh_disable(cpu); // disable softirq
// local_bh_count++
restart:
softirq_pending(cpu) = 0;
local_irq_enable();
struct softirq_action* h = softirq_vec;
do{
if( pending & 1 ) h->action(h);
h++; pending >> 1;
}while(pending);
local_irq_disable();
pending = softirq_pending(cpu);
if( pending & mask ){
mask &= ~pending;
goto restart;
}
local_bh_enable();
if( pending ) wakeup_softirqd(cpu);
}
local_irq_restore( flags );
35
Kernel Thread : ksoftirqd_CPUn


Softirq kernel thread for each CPU
Q: what if softirqs raised or re-activated at very high frequency ?

either ignore new softirqs that occur while do_softirq( ) is running


or continuous re-checking for pending softirqs


long softirq latency (even on idle machine)
user starving (long response time)
softirq kernel threads

ksoftirqd_CPUn at low priority


give user a priority
but run immediately on idle
Embedded System Lab. II
36
for (; ;) {
set_current_state(TASK_INTERRPTIBLE);
schedule( );
// now in TASK_RUNNING state
while (softirq_pending(cpu)) {
do_doftirq( );
if (current->need_resched)
schedule( );
}
}
Tasklets


Implemented on top of softirqs: HI_SOFTIRQ, TASKLET_SOFTIRQ
Tasklet structure (linux/interrupt.h)
struct tasklet_struct {
struct tasklet_struct *next; // pointer to next tasklet in the list
unsigned long state; // state of the tasklet : 0, SCHED, RUN
atomic_t count; // enable (0) /disable (nonzero) tasklet
void (*func)(unsigned long); // tasklet handler function
unsigned long data; // argument to tasklet function
}

tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS]


to store scheduled tasklets (like raised softirqs)
for regular tasklets and high-priority tasklets
Embedded System Lab. II
37
Tasklets

Declaring your tasklets

statically (linux/interrupt.h)
DECLARE_TASKLET(name, func, data) / DECLARE_TASKLET_DISABLED( )
(ex) DECLARE_TASKLET(my_tasklet, my_tasklet_handler, dev);
struct tasklet_struct my_tasklet = { NULL, 0, ATOMIC_INIT(0),
my_tasklet_handler, dev};
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);

dynamically
void tasklist_init(struct tasklet_struct *t, tasklet_handler, dev);
Embedded System Lab. II
38
Tasklets

Writing your tasklet handler
void tasklet_handler(unsigned long data) {
….;
}

Remember that



Tasklets cannot sleep
if a tasklet share any data with an interrupt handler ?

(tasklets run with all interrupts enabled)
if a tasklet shares data with another tasklet or softirq ?
Embedded System Lab. II
39
Tasklets

Activating your tasklet tasklet_schedule( ) / tasklet_hi_schedule( )
tasklet_schedule(&my_tasklet); /* mark my_tasklet as pending */
static void tasklet_schedule(struct tasklet_struct *t)
{
1. if TASKLET_STATE_SCHED is set, returns; /* already been scheduled ? */
otherwise set TASKLET_STATE_SCHED flag;
2. local_irq_save( flags); // save the state of interrupt system
and disable local interrupts
3.insert the tasklet to the head of tasklet_vec or tasklet_hi_vec;
4. raise TASKLET_SOFTIRQ or HI_SOFTIRQ softirq;
5. local_irq_restore(flags);
}
* what if the same tasklet is scheduled again ?
* what if it is already running on another CPU ?
Embedded System Lab. II
40
Tasklets

Handling tasklets via tasklet_action( ) : TASKLET_SOFTIRQ
static void tasklet_action(struct softirq_action *a)
{
1. disable local interrupts;
2. list = the address of the list pointed to by tasklet_vec[cpu];
3. put NULL in tasklet_vec[cpu]; // list of scheduled tasklets is emptied //
4. enable local interrupts;
5. for each tasklet decriptor in the list
a. if TASKLET_STATE_RUN set, // tasklet of the same type on another CPU
reinsert the tasklet descriptor in the list of tasklet_vec[cpu]
and activate TASKLET_SOFTIRQ again;
otherwise, set TASKLET_STATE_RUN (in SMP)
b. if the tasklet is disabled (count <> 0 ?)
reinsert and activate TASKLET_SOFTIRQ; // defer tasklet
otherwise, clear TASKLET_STATE_SCHED and execute the tasklet function;
}
Embedded System Lab. II
41
Tasklets

Controlling tasklets

Enable / Disable tasklets

tasklet_enable( ) / tasklet_disable( )
tasklet_disable(&my_tasklet);
/* we can now do stuff knowing that the tasklet cannot run .. */
tasklet_enable(&my_tasklet);

Remove a tasklet from the pending queue

tasklet_kill( )
Embedded System Lab. II
42
Tasklets

In summary,


In most cases, tasklets are preferred way to implement a bottom half for a
normal hardware device
Tasklets are dynamically created, easy to use, and relatively quick.
Embedded System Lab. II
43
BH

BH interface : a high-priority tasklet with no concurrency



at most one BH is running (global_bh_lock spin lock)
only for backward compatibility (Linux 2.2 or older)
Note that



Each BH must be statically defined and there are max 32 BHs.
Modules could not directly use BH interface because BH handlers must
be defined at compile-time.
All BH handlers are strictly serialized.



No two BH handlers (even of different types) can run concurrenlty
Easy synchronization, but not good for SMP performance
a driver using BH interface does not scale well to SMP
Embedded System Lab. II
44
BH

Task queues


a group of kernel functions as a BH ? (task queues)
Three particular task queues





Immediate queue (run by IMMEDIATE_BH)
Timer queue (run by TQUEUE_BH)
Scheduler queue (run by keventd kernel thread) (* Linux 2.4 *)
Can dynamically create new task queues
Later…


various queues 􀃆 tasklets
scheduler queue + keventd --> “work queue” in 2.6
Embedded System Lab. II
45
Disabling Bottom Halves


Data structures accessed by bottom halves must be protected
against race conditions because bottom halves can be executed at
any time.
A trivial way to disable bottom halves on a CPU is to disable
interrupts on that CPU.


(no interrupts --> no softirqs)
How to disable bottom halves without disabling interrupts ?


void local_bh_disable( ) --> __local_bh_count++ (of irq_stat[cpu])
void local_bh_enable( )
Embedded System Lab. II
46
GPIO 디바이스 드라이버

GPIO 입력

button를 제어하는 gpio 드라이버 프로그램 소스 예제(gpio.c)
#include <linux/kernel.h> /* gpio.c */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <asm/hardware.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/param.h>
#define IRQ_BUTTON
IRQ_GPIO(16)
static void button_interrupt(int irq, void *dev_id, struct pt_regs *regs);
GPIO에 할당된 IRQ값과
인터럽트 핸들러
static int GPIO_MAJOR = 0;
static DECLARE_WAIT_QUEUE_HEAD(wait_queue);
블럭킹 I/O를 위한 대기 큐
Embedded System Lab. II
47
GPIO 디바이스 드라이버(2)
static void button_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
wake_up_interruptible(&wait_queue);
}
대기큐에 있는 프로세스를 깨움
(버튼이 눌러졌을 때)
static ssize_t gpio_read(struct file *filp, char *buf, size_t count, loff_t *l)
{
char hello[] = "GPIO_16 port was pushed!";
interruptible_sleep_on(&wait_queue);
copy_to_user(buf,&hello,sizeof(hello));
return 0;
}
대기큐로 진입 – 블록킹 상태
(버튼이 눌러지길 기다림)
블록킹이 해제 후 메시지 출력
static int gpio_open(struct inode *inode, struct file *filp)
{
int res;
unsigned int gafr;
gafr = 0x3;
GAFR0_U &= ~gafr;
16번포트의 GAFR설정 해제
printk("IRQ_BUTTON = %d\n",IRQ_BUTTON);
printk("IRQ_TO_GPIO =%d\n",IRQ_TO_GPIO_2_80(IRQ_BUTTON));
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) &=
~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON));
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON),
GPIO_FALLING_EDGE);
Embedded System Lab. II
48
16번 포트를 출력으로 설정과
및 에지 신호 설정
GPIO 디바이스 드라이버(3)
res = request_irq(IRQ_BUTTON,&button_interrupt,SA_INTERRUPT,
"Button",NULL);
if(res < 0)
printk(KERN_ERR "%s: Request for IRQ %d failed\n",
__FUNCTION__,IRQ_BUTTON);
else {
enable_irq(IRQ_BUTTON);
}
인터럽트 핸들러 설치
인터럽트를 활성화
MOD_INC_USE_COUNT;
return 0;
}
static int gpio_release(struct inode *inode, struct file *filp)
{
free_irq(IRQ_BUTTON,NULL);
disable_irq(IRQ_BUTTON);
설정된 irq를 설정 해제한다
MOD_DEC_USE_COUNT;
return 0;
}
static struct file_operations gpio_fops = {
read:
gpio_read,
open: gpio_open,
release:
gpio_release
};
Embedded System Lab. II
49
GPIO 디바이스 드라이버(4)
int init_module(void)
{
int result;
result = register_chrdev(GPIO_MAJOR,"GPIOINTERRUPT",&gpio_fops);
if(result < 0) {
printk(KERN_WARNING"Can't get major %d\n",GPIO_MAJOR);
return result;
}
if(GPIO_MAJOR == 0) GPIO_MAJOR = result;
printk("init module, GPIO major number : %d\n",result);
return 0;
}
void cleanup_module(void)
{
unregister_chrdev(GPIO_MAJOR,"GPIO INTERRUPT");
return;
}
Embedded System Lab. II
50
GPIO 디바이스 드라이버 응용 프로
그램
#include <stdio.h> /* test.c */
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
static int dev;
int main(void)
{
char buff[40];
dev = open("/dev/gpio",O_RDWR);
if(dev < 0) {
printf( "Device Open ERROR!\n");
exit(1);
}
gpio장치를 open
printf("Please push the GPIO_16 port!\n");
read(dev,buff,40);
printf("%s\n",buff);
버튼이 눌러지길 기다림
(블록킹 상태가 됨)
close(dev);
return 0;
}
Embedded System Lab. II
51
Makefile 작성 예
# led Device Driver Makefile /* Makefile */
CC = arm-linux-gcc
KERNELDIR = /usr/src/linux-2.4.19
INCLUDEDIR = -I$(KERNELDIR)/include
CFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpio.o
MODULE_SRCS = gpio.c
TEST_TARGET = test
TEST_OBJS = test.o
TEST_SRCS = test.c
all: $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) : $(TEST_OBJS)
$(CC) $(TEST_OBJS) -o $@
clean:
rm -f *.o
rm -f $(TEST_TARGET)
Embedded System Lab. II
52
GPIO 디바이스 드라이버 실행

GPIO 디바이스 드라이버 컴파일 방법
$ make

GPIO 디바이스 드라이버 실행 방법



생성된 gpio.o와 test파일을 타겟 보드로 전송
insmod로 드라이버를 커널에 loading
mknod를 통해 특수 장치 파일을 만듦
$ insmod gpio.o
Using gpio.o
init module, GPIO major number : 252
$ mknod /dev/gpio c 252 0

응용프로그램을 실행
$ ./test
Embedded System Lab. II
53
GPIO 디바이스 드라이버 실행 예

디바이스 드라이버 컴파일

디바이스 드라이버 실행
Embedded System Lab. II
54