Upload
aolani
View
72
Download
2
Embed Size (px)
DESCRIPTION
Interrupt Programming in Linux. 경희대학교 컴퓨터공학과 조 진 성. 주요 내용. 주요 내용 인터럽트 개념 PXA255 CPU 에서의 인터럽트 하드웨어 리눅스에서의 인터럽트 처리 인터럽트 프로그래밍 예. intr request. status reg. CPU. intr ack. mechanism. IR. PC. data/address. data reg. 인터럽트 인터페이스. 임베디드 시스템의 실시간성 요구에 필수적인 요소. 인터럽트. - PowerPoint PPT Presentation
Citation preview
Embedded System Lab II
Interrupt Programming in Interrupt Programming in LinuxLinux
경희대학교 컴퓨터공학과
조 진 성
Embedded System Lab II 2
주요 내용
주요 내용 인터럽트 개념 PXA255 CPU 에서의 인터럽트 하드웨어 리눅스에서의 인터럽트 처리 인터럽트 프로그래밍 예
Embedded System Lab II 3
인터럽트 인터페이스
임베디드 시스템의 실시간성 요구에 필수적인 요소
CPU
statusreg
datareg m
echa
nism
PCintr request
intr ack
dataaddress
IR
Embedded System Lab II 4
인터럽트
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 ndash 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 IO Essentially ldquopollingrdquo of the interrupt pin is built-into the hardware so
no extra time
Embedded System Lab II 5
인터럽트 What is the address (interrupt address vector) of the ISR
Fixed 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 Vectored interrupt
Peripheral must provide the address Common when microprocessor has multiple peripherals connected by a
system bus Compromise interrupt address table
Embedded System Lab II 6
인터럽트 기반 IO(fixed ISR location)
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 microprocessor3 After completing instruction at 100 μP
sees Int asserted saves the PCrsquos 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
5 The ISR returns thus restoring PC to 100+1=101 where μP resumes executing
4(b) After being read P1 de-asserts Int
Time
Embedded System Lab II 7
인터럽트 기반 IO(fixed ISR location)
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
μP
P1 P2
System bus
Int
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Embedded System Lab II 8
인터럽트 기반 IO(fixed ISR location)
2 P1 asserts Int to request servicing by the microprocessor
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
IntInt1
Embedded System Lab II 9
인터럽트 기반 IO(fixed ISR location)
3 After completing instruction at 100 P sees Int asserted saves the PCrsquos value of 100 and sets PC to the ISR fixed location of 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 2
주요 내용
주요 내용 인터럽트 개념 PXA255 CPU 에서의 인터럽트 하드웨어 리눅스에서의 인터럽트 처리 인터럽트 프로그래밍 예
Embedded System Lab II 3
인터럽트 인터페이스
임베디드 시스템의 실시간성 요구에 필수적인 요소
CPU
statusreg
datareg m
echa
nism
PCintr request
intr ack
dataaddress
IR
Embedded System Lab II 4
인터럽트
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 ndash 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 IO Essentially ldquopollingrdquo of the interrupt pin is built-into the hardware so
no extra time
Embedded System Lab II 5
인터럽트 What is the address (interrupt address vector) of the ISR
Fixed 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 Vectored interrupt
Peripheral must provide the address Common when microprocessor has multiple peripherals connected by a
system bus Compromise interrupt address table
Embedded System Lab II 6
인터럽트 기반 IO(fixed ISR location)
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 microprocessor3 After completing instruction at 100 μP
sees Int asserted saves the PCrsquos 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
5 The ISR returns thus restoring PC to 100+1=101 where μP resumes executing
4(b) After being read P1 de-asserts Int
Time
Embedded System Lab II 7
인터럽트 기반 IO(fixed ISR location)
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
μP
P1 P2
System bus
Int
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Embedded System Lab II 8
인터럽트 기반 IO(fixed ISR location)
2 P1 asserts Int to request servicing by the microprocessor
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
IntInt1
Embedded System Lab II 9
인터럽트 기반 IO(fixed ISR location)
3 After completing instruction at 100 P sees Int asserted saves the PCrsquos value of 100 and sets PC to the ISR fixed location of 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 3
인터럽트 인터페이스
임베디드 시스템의 실시간성 요구에 필수적인 요소
CPU
statusreg
datareg m
echa
nism
PCintr request
intr ack
dataaddress
IR
Embedded System Lab II 4
인터럽트
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 ndash 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 IO Essentially ldquopollingrdquo of the interrupt pin is built-into the hardware so
no extra time
Embedded System Lab II 5
인터럽트 What is the address (interrupt address vector) of the ISR
Fixed 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 Vectored interrupt
Peripheral must provide the address Common when microprocessor has multiple peripherals connected by a
system bus Compromise interrupt address table
Embedded System Lab II 6
인터럽트 기반 IO(fixed ISR location)
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 microprocessor3 After completing instruction at 100 μP
sees Int asserted saves the PCrsquos 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
5 The ISR returns thus restoring PC to 100+1=101 where μP resumes executing
4(b) After being read P1 de-asserts Int
Time
Embedded System Lab II 7
인터럽트 기반 IO(fixed ISR location)
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
μP
P1 P2
System bus
Int
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Embedded System Lab II 8
인터럽트 기반 IO(fixed ISR location)
2 P1 asserts Int to request servicing by the microprocessor
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
IntInt1
Embedded System Lab II 9
인터럽트 기반 IO(fixed ISR location)
3 After completing instruction at 100 P sees Int asserted saves the PCrsquos value of 100 and sets PC to the ISR fixed location of 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 4
인터럽트
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 ndash 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 IO Essentially ldquopollingrdquo of the interrupt pin is built-into the hardware so
no extra time
Embedded System Lab II 5
인터럽트 What is the address (interrupt address vector) of the ISR
Fixed 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 Vectored interrupt
Peripheral must provide the address Common when microprocessor has multiple peripherals connected by a
system bus Compromise interrupt address table
Embedded System Lab II 6
인터럽트 기반 IO(fixed ISR location)
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 microprocessor3 After completing instruction at 100 μP
sees Int asserted saves the PCrsquos 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
5 The ISR returns thus restoring PC to 100+1=101 where μP resumes executing
4(b) After being read P1 de-asserts Int
Time
Embedded System Lab II 7
인터럽트 기반 IO(fixed ISR location)
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
μP
P1 P2
System bus
Int
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Embedded System Lab II 8
인터럽트 기반 IO(fixed ISR location)
2 P1 asserts Int to request servicing by the microprocessor
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
IntInt1
Embedded System Lab II 9
인터럽트 기반 IO(fixed ISR location)
3 After completing instruction at 100 P sees Int asserted saves the PCrsquos value of 100 and sets PC to the ISR fixed location of 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 5
인터럽트 What is the address (interrupt address vector) of the ISR
Fixed 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 Vectored interrupt
Peripheral must provide the address Common when microprocessor has multiple peripherals connected by a
system bus Compromise interrupt address table
Embedded System Lab II 6
인터럽트 기반 IO(fixed ISR location)
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 microprocessor3 After completing instruction at 100 μP
sees Int asserted saves the PCrsquos 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
5 The ISR returns thus restoring PC to 100+1=101 where μP resumes executing
4(b) After being read P1 de-asserts Int
Time
Embedded System Lab II 7
인터럽트 기반 IO(fixed ISR location)
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
μP
P1 P2
System bus
Int
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Embedded System Lab II 8
인터럽트 기반 IO(fixed ISR location)
2 P1 asserts Int to request servicing by the microprocessor
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
IntInt1
Embedded System Lab II 9
인터럽트 기반 IO(fixed ISR location)
3 After completing instruction at 100 P sees Int asserted saves the PCrsquos value of 100 and sets PC to the ISR fixed location of 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 6
인터럽트 기반 IO(fixed ISR location)
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 microprocessor3 After completing instruction at 100 μP
sees Int asserted saves the PCrsquos 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
5 The ISR returns thus restoring PC to 100+1=101 where μP resumes executing
4(b) After being read P1 de-asserts Int
Time
Embedded System Lab II 7
인터럽트 기반 IO(fixed ISR location)
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
μP
P1 P2
System bus
Int
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Embedded System Lab II 8
인터럽트 기반 IO(fixed ISR location)
2 P1 asserts Int to request servicing by the microprocessor
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
IntInt1
Embedded System Lab II 9
인터럽트 기반 IO(fixed ISR location)
3 After completing instruction at 100 P sees Int asserted saves the PCrsquos value of 100 and sets PC to the ISR fixed location of 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 7
인터럽트 기반 IO(fixed ISR location)
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
μP
P1 P2
System bus
Int
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Embedded System Lab II 8
인터럽트 기반 IO(fixed ISR location)
2 P1 asserts Int to request servicing by the microprocessor
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
IntInt1
Embedded System Lab II 9
인터럽트 기반 IO(fixed ISR location)
3 After completing instruction at 100 P sees Int asserted saves the PCrsquos value of 100 and sets PC to the ISR fixed location of 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 8
인터럽트 기반 IO(fixed ISR location)
2 P1 asserts Int to request servicing by the microprocessor
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
IntInt1
Embedded System Lab II 9
인터럽트 기반 IO(fixed ISR location)
3 After completing instruction at 100 P sees Int asserted saves the PCrsquos value of 100 and sets PC to the ISR fixed location of 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 9
인터럽트 기반 IO(fixed ISR location)
3 After completing instruction at 100 P sees Int asserted saves the PCrsquos value of 100 and sets PC to the ISR fixed location of 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 10
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
인터럽트 기반 IO(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
100
Int0
P1
System bus
P1
0x8000
P2
0x8001
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 11
인터럽트 기반 IO(fixed ISR location)
5 The ISR returns thus restoring PC to 100+1=101 where P resumes executing
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 12
인터럽트 기반 IO(vectored interrupt)
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 microprocessor3 After completing instruction at 100 μP sees Int
asserted saves the PCrsquos 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
5(b) After being read P1 deasserts Int
Time
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 13
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
IntInta
16
1(a) P is executing its main program
1(b) P1 receives input data in a register with address 0x8000
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 14
인터럽트 기반 IO(vectored interrupt)
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
100
Inta
16
2 P1 asserts Int to request servicing by the microprocessor
Int1
Int
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 15
인터럽트 기반 IO(vectored interrupt)
3 After completing instruction at 100 μP sees Int asserted saves the PCrsquos value of 100 and asserts Inta
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100100
1Inta
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 16
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
인터럽트 기반 IO(vectored interrupt)
100
4 P1 detects Inta and puts interrupt address vector 16 on the data bus
16
16
System bus
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 17
인터럽트 기반 IO(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
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PCInt
Inta
16
100
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
P1 P2
0x8000 0x8001
System bus
0Int
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 18
인터럽트 기반 IO(vectored interrupt)
6 The ISR returns thus restoring the PC to 100+1=101 where the μP resumes
μP
P1 P2
System bus
Data memory
0x8000 0x8001
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
Program memory
PC
Int
100100+1
16 MOV R0 0x8000 17 modifies R0 18 MOV 0x8001 R0 19 RETI ISR return
ISR
100101
instruction instruction
Main program
100
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 19
Interrupt address table
Compromise between fixed and vectored interrupts One interrupt pin Table in memory holding ISR addresses (maybe 256 words) Peripheral doesnrsquot 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 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 20
Additional interrupt issues
Maskable vs non-maskable interrupts Maskable programmer can set bit that causes processor to ignore interrupt
Important when in the middle of time-critical code Non-maskable a separate interrupt pin that canrsquot be masked
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) ndash 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 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 21
PXA255 General Purpose IO Block DiagramPin Direction
Register(GPDR) Alternate Function
Register(GAFR) Pin Set
Registers(GPSR)
Edge DetectStatus Register(GEDR)
Rising Edge DetectEnable Register(GRER)
Falling Edge DetectEnable Register(GFER)
EdgeDetect
Pin-LevelRegister(GPLR)
0
1Alternate Function(Output)
Alternate Function(Input)
Pin Clear Registers(GPCR)2
3
3210
Power Manager Sleep Wake-up logic
2
0x40E0_000C1014
GPDR1 출력0 입력
0x40E0_0054585C0x40E0_00606468
0x40E0_00606468
0x40E0_00606468
Base Address0x40E0_0000
0x40E0_00484C50
0x40E0_00303438
0x40E0_003C4044
0x40E0_00000408
세부적인 내용은 Embedded SW I
참조
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 22
PXA255 Interrupt controller Interrupt Controller
Level Register(ICLR)
Interrupt ControllerMask Register (ICMR)
Interrupt Source Bit
Interrupt ControllerPending Register
(ICPR)Interrupt Controller
IRQ Pending Register (ICIP)
Interrupt ControllerFIQ Pending Register (ICFP)
All Other Qualified interrupt Bits
FIQ
IRQ
23 23 XScale CORE
CPSR6(F)
CPSR7(I)40D0 0000
40D0 0004
40D0 0008
40D0 000C
40D0 0010
40D0 0014 Interrupt controller control register (ICCR) ICCR0 disable idle mask(DIM)
CCR[DIM]=0 amp IDLE mode=lsquo1rsquo
0 IRQ1 FIQ
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 23
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 PeripheralPCMCIA interrupt mask in each module GPIO masks determined per pin or group of pins
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 24
ICMR(Interrupt Controller Mask Register)
IM[x] Interrupt Mask lsquoxrsquo (where x= 8 through 14 and 17 through 31)0 ndash Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or
Power Manager)1 ndash 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 0x40D00004
0000 0000 0000 000 000 0000
IM31 ndash IM28 IM27 ndash IM24 IM23 ndash IM20 IM11 ndash IM8IM19 ndash IM17 ReservedIM14 ndash IM12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 25
ICLR(Interrupt Controller Level Register)
IL[x] Interrupt Level lsquoxrsquo (where n = 8 through 14 and 17 through 31)0 ndash Interrupt routed to IRQ interrupt input1 ndash Interrupt routed to FIQ interrupt input
Reserved[0-7 15 16]Physical Address 0x40D00008
0000 0000 0000 000 000 0000
IL31 ndash IL28 IL27 ndash IL24 IL23 ndash IL20 IL11 ndash IL8IL19 ndash IL17 ReservedIL14 ndash IL12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 26
ICCR(Interrupt Controller Control Register)
DIM[0] Disable Idle mask0 ndash All enabled interrupts bring the processor out of idle mode1 ndash Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle
modeThis bit is cleared during all resets
Reserved[311]Physical Address 0x40D00014
X
Reserved DIM
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 27
ICIP(Interrupt Controller IRQ Pending Register)
IP[x] IRQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash IRQ NOT requested by any enabled source1 ndash IRQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D00000
0000 0000 0000 000 000 0000
IP31 ndash IP28 IP27 ndash IP24 IP23 ndash IP20 IP11 ndash IP8IP19 ndash IP17 ReservedIP14 ndash IP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 28
ICFP(Interrupt Controller FIQ Pending Register)
FP[x] FIQ Pending x (where x = 8 through 14 and 17 through 31)0 ndash FIQ NOT requested by any enabled source1 ndash FIQ requested by an enabled source
Reserved[0-7 15 16]Physical Address 0x40D0000C
0000 0000 0000 000 000 0000
FP31 ndash FP28 FP27 ndash FP24 FP23 ndash FP20 FP11 ndash FP8FP19 ndash FP17 ReservedFP14 ndash FP12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 29
ICPR(Interrupt Controller Pending Register)
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 Table 4-36 참조
Physical Address 0x40D00010
0000 0000 0000 000 000 0000
IS31 ndash IS28 IS27 ndash IS24 IS23 ndash IS20 IS11 ndash IS8IS19 ndash IS17 ReservedIS14 ndash IS12
3 2 1 07 6 5 411 10 9 815 14 13 1219 18 17 1623 22 21 2027 26 25 2431 30 29 28
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 30
Why Bottom Halves
Because interrupt handlers hellip 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 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 31
Bottom Halves in Linux 24
the future when the system is less busy and interrupts are enabled again
Three types of bottom halves in 24 softirq tasklet BH operations on deferrable functions
initialization activation masking execution ( activation and execution on the same
CPU )
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 32
Bottom Halves in Linux 24
History of bottom half status in Linux BH removed in 25 Task queues removed in 25 Softirq available since 23 Tasklet available since 23 Work queues available since 25
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 33
Softirqs
Each registered softirq in one entry of softirq_vec[32] (32 softirqs possible)
irq_stat
32-entry array of softirq_action (kernelsoftirqc) static struct softirq_action softirq_vec[32]
structure representing a softirq entry (linuxinterrupth) struct softirq_action void (action) (struct softirq_action ) void data
irq_cpustat_t irq_stat[NR_CPUS]
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
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 34
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 24 (six in 26)
HI_SOFTIRQ TIMER_SOFTIRQ NET_TX_SOFTIRQ NET_RX_SOFTIRQ SCSI_SOFTIRQ (hellip) TASKLET_SOFTIRQ
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 35
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 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 36
Softirqs
Executing softirqs do_softirq( )if( in_interrupt( ) ) returnlocal_irq_save( flags ) disable local INTpending = 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 amp 1 ) h-gtaction(h) h++ pending gtgt 1 while(pending) local_irq_disable() pending = softirq_pending(cpu) if( pending amp mask ) mask amp= ~pending goto restart local_bh_enable() if( pending ) wakeup_softirqd(cpu)local_irq_restore( flags )
softirq not processed in this invocation goto restart only if different types goto wakeup_softirqd(cpu) if same softirqre-activated
local_irq_count(cpu) = = 0 nested local_bh_count(cpu) = = 0 enabled
serializing execution on a CPU
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 37
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 long softirq latency (even on idle machine)
or continuous re-checking for pending softirqs user starving (long response time)
softirq kernel threads ksoftirqd_CPUn at low priority
give user a priority but run immediately on idle
for ( ) set_current_state(TASK_INTERRPTIBLE) schedule( ) now in TASK_RUNNING state while (softirq_pending(cpu)) do_doftirq( ) if (current-gtneed_resched) schedule( )
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 38
Tasklets
Implemented on top of softirqs HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet structure (linuxinterrupth)
tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] to store scheduled tasklets (like raised softirqs) for regular tasklets and high-priority tasklets
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
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 39
Tasklets
Declaring your tasklets statically (linuxinterrupth)
dynamically
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 devopen_softirq(TASKLET_SOFTIRQ tasklet_action NULL)open_softirq(HI_SOFTIRQ tasklet_hi_action NULL)
void tasklist_init(struct tasklet_struct t tasklet_handler dev)
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 40
Tasklets
Writing your tasklet handler
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
void tasklet_handler(unsigned long data) hellip
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 41
Tasklets
Activating your tasklet tasklet_schedule( ) tasklet_hi_schedule( )
tasklet_schedule(ampmy_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 3insert 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 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 42
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 ltgt 0 ) reinsert and activate TASKLET_SOFTIRQ defer tasklet otherwise clear TASKLET_STATE_SCHED and execute the tasklet function
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 43
Tasklets
Controlling tasklets Enable Disable tasklets
tasklet_enable( ) tasklet_disable( )
Remove a tasklet from the pending queue tasklet_kill( )
tasklet_disable(ampmy_tasklet)
we can now do stuff knowing that the tasklet cannot run
tasklet_enable(ampmy_tasklet)
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 44
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 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 45
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 22 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 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 46
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 24 )
Can dynamically create new task queues Laterhellip
various queues 1048774 tasklets scheduler queue + keventd --gt ldquowork queuerdquo in 26
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 47
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 --gt no softirqs)
How to disable bottom halves without disabling interrupts void local_bh_disable( ) --gt __local_bh_count++ (of irq_stat[cpu]) void local_bh_enable( )
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 48
GPIO 디바이스 드라이버 GPIO 입력
button 를 제어하는 gpio 드라이버 프로그램 소스 예제 (gpioc)
include ltlinuxkernelhgt gpioc include ltlinuxmodulehgtinclude ltlinuxinithgtinclude ltlinuxconfighgtinclude ltlinuxschedhgtinclude ltlinuxstringhgtinclude ltlinuxdelayhgtinclude ltlinuxerrnohgtinclude ltlinuxtypeshgt
include ltasmhardwarehgtinclude ltasmuaccesshgtinclude ltasmirqhgtinclude ltasmparamhgt
define IRQ_BUTTON IRQ_GPIO(16)static void button_interrupt(int irq void dev_id struct pt_regs regs)
static int GPIO_MAJOR = 0static DECLARE_WAIT_QUEUE_HEAD(wait_queue)
GPIO 에 할당된 IRQ값과인터럽트 핸들러
블럭킹 IO 를 위한 대기 큐
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 49
GPIO 디바이스 드라이버 (2)static void button_interrupt(int irq void dev_id struct pt_regs regs)
wake_up_interruptible(ampwait_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(ampwait_queue) copy_to_user(bufamphellosizeof(hello))
return 0
static int gpio_open(struct inode inode struct file filp)
int resunsigned int gafr
gafr = 0x3GAFR0_U amp= ~gafr
printk(IRQ_BUTTON = dnIRQ_BUTTON)printk(IRQ_TO_GPIO =dnIRQ_TO_GPIO_2_80(IRQ_BUTTON))
GPDR(IRQ_TO_GPIO_2_80(IRQ_BUTTON)) amp=~GPIO_bit(IRQ_TO_GPIO_2_80(IRQ_BUTTON))
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_BUTTON)GPIO_FALLING_EDGE)
16 번 포트를 출력으로 설정과 및 에지 신호 설정
16 번포트의 GAFR 설정 해제
대기큐에 있는 프로세스를 깨움( 버튼이 눌러졌을 때 )
대기큐로 진입 ndash 블록킹 상태( 버튼이 눌러지길 기다림 )
블록킹이 해제 후 메시지 출력
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 50
GPIO 디바이스 드라이버 (3)res = request_irq(IRQ_BUTTONampbutton_interruptSA_INTERRUPT
ButtonNULL)
if(res lt 0)printk(KERN_ERR s Request for IRQ d failedn
__FUNCTION__IRQ_BUTTON)else
enable_irq(IRQ_BUTTON)
MOD_INC_USE_COUNTreturn 0
static int gpio_release(struct inode inode struct file filp)
free_irq(IRQ_BUTTONNULL)disable_irq(IRQ_BUTTON)
MOD_DEC_USE_COUNTreturn 0
static struct file_operations gpio_fops = read gpio_readopen gpio_openrelease gpio_release
인터럽트 핸들러 설치
인터럽트를 활성화
설정된 irq 를 설정 해제한다
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 51
GPIO 디바이스 드라이버 (4)int init_module(void)
int resultresult = register_chrdev(GPIO_MAJORGPIOINTERRUPTampgpio_fops)
if(result lt 0) printk(KERN_WARNINGCant get major dnGPIO_MAJOR)return result
if(GPIO_MAJOR == 0) GPIO_MAJOR = resultprintk(init module GPIO major number dnresult)
return 0
void cleanup_module(void)
unregister_chrdev(GPIO_MAJORGPIO INTERRUPT)return
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 52
GPIO 디바이스 드라이버 응용 프로그램
include ltstdiohgt testc include ltstdlibhgtinclude ltunistdhgtinclude ltsystypeshgtinclude ltsysstathgtinclude ltfcntlhgtinclude lterrnohgt
static int dev
int main(void)
char buff[40]dev = open(devgpioO_RDWR)if(dev lt 0)
printf( Device Open ERRORn)exit(1)
printf(Please push the GPIO_16 portn)read(devbuff40)printf(snbuff)
close(dev)return 0
gpio 장치를 open
버튼이 눌러지길 기다림( 블록킹 상태가 됨 )
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 53
Makefile 작성 예 led Device Driver Makefile Makefile CC = arm-linux-gcc
KERNELDIR = usrsrclinux-2419INCLUDEDIR = -I$(KERNELDIR)includeCFLAGS = -D__KERNEL__ -DMODULE -Wall -O2 $(INCLUDEDIR)
MODULE_OBJS = gpiooMODULE_SRCS = gpioc
TEST_TARGET = testTEST_OBJS = testoTEST_SRCS = testc
all $(MODULE_OBJS) $(TEST_TARGET)
$(TEST_TARGET) $(TEST_OBJS)$(CC) $(TEST_OBJS) -o $
cleanrm -f orm -f $(TEST_TARGET)
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 54
GPIO 디바이스 드라이버 실행
GPIO 디바이스 드라이버 컴파일 방법
GPIO 디바이스 드라이버 실행 방법 생성된 gpioo 와 test 파일을 타겟 보드로 전송 insmod 로 드라이버를 커널에 loading mknod 를 통해 특수 장치 파일을 만듦
응용프로그램을 실행
$ make
$ insmod gpioo Using gpioo init module GPIO major number 252$ mknod devgpio c 252 0
$ test
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행
Embedded System Lab II 55
GPIO 디바이스 드라이버 실행 예 디바이스 드라이버 컴파일
디바이스 드라이버 실행