///////////////////////////////////////////////////////////////////////////////////
1. 关于 native linux interrupt handle 部分
struct irq_desc {
irq_flow_handler_t handle_irq;
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */
unsigned int status; /* IRQ status */
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned int irqs_unhandled;
unsigned long last_unhandled; /* Aging timer for unhandled count */
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
extern struct irq_desc irq_desc[NR_IRQS];
kernel 维护着这个全局局irq_desc 数组, 每个中断对应与1个 irq_desc
/**
* struct irq_chip - hardware interrupt chip descriptor
*/
struct irq_chip {
const char *name;
unsigned int (*startup)(unsigned int irq);
void (*shutdown)(unsigned int irq);
void (*enable)(unsigned int irq);
void (*disable)(unsigned int irq);
void (*ack)(unsigned int irq);
void (*mask)(unsigned int irq);
void (*mask_ack)(unsigned int irq);
void (*unmask)(unsigned int irq);
void (*eoi)(unsigned int irq);
void (*end)(unsigned int irq);
void (*set_affinity)(unsigned int irq, cpumask_t dest);
int (*retrigger)(unsigned int irq);
int (*set_type)(unsigned int irq, unsigned int flow_type);
int (*set_wake)(unsigned int irq, unsigned int on);
/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
const char *typename;
};
irq_chip 对应于实际中断控制器的处理代码 ,其中startup, 是在 driver call request_irq 设置 irq 的ISR 时
request_irq => setup_irq => startup 完成对该irq的启动时的一些操作
另外,实际arm linux 2.6.24 的 init_IRQ (init/main.c) 并没有设置irq_chip ,而是先设置每个irq_desc的statu 为no request ,no probe
然后call init_arch_irq 该函数对应于 具体的mach 的 machine_desc 的init_irq函数,
比如6410
MACHINE_START(SMDK6410, "SMDK6410")
.......
.init_irq = s3c_init_irq,
而在 s3c_init_irq 中 对各类中断设置处理函数, 比如电平中断 set_irq_handler(irqno, handle_level_irq);
当硬件中断发生时 handle_level_irq => handle_IRQ_event(irq, action); 把action 上所有的handle 执行一遍
这个handle 就是刚才request_irq 时注册的ISR
///////////////////////////////////////////////////////////////////////////////////
2. 现在来看下 oklinux 中断部分的实现
回想下l4 kernel 部分,当我们注册了中断 ,l4 lib 使用L4_RegisterInterrupt 注册 notify thread ,and notify bit
当发生中断时通过notify 通知到thread, 在oklinux 里基本也是这么做的
init_IRQ (sys_iguana.c) => iguana_init_irq 主要对每个irq_desc 做了 irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
然后设置 irq_desc[i].chip 为 iguana_irq_type
static struct hw_interrupt_type iguana_irq_type = {
.typename = "iguana",
.startup = iguana_startup_irq,
.shutdown = iguana_disable_irq,
.enable = iguana_enable_irq,
.disable = iguana_disable_irq,
.ack = iguana_ack_irq,
.end = iguana_end_irq,
.set_affinity = iguana_set_irq_affinity,
};
其中iguana_startup_irq 是关键, 它执行 L4_RegisterInterrupt(timer_thread, 31, 1, 0); 注册timer_thread 来处理中断,并设置
notify bit 为31 , 之前还call 了 L4_LoadMR(0, irq); 把irq 放到 MRS-0 ,用msg register 传递irq num 给register instrrupt 系统调用.
这样就完成了irq 对 l4 kernel 的注册,当发生实际硬件中断, l4 kernel 就会通知到timer_thread,
其他 iguana_enable_irq =>L4_AcknowledgeInterruptOnBehalf 请求kernel 在硬件中断发生时通知timer_thread,
iguana_disable_irq call L4_UnregisterInterrupt 把 中断通知和timer_thread 脱钩
timer_thread 就是 mian里的 interrupt_loop
interrupt_loop 是 代替 asm_do_IRQ ,在实际arm 里 当发生中断跳掉中断向量处理地址执行irq handle 然后 =>asm_do_IRQ
但l4 对oklinux 不是正真的 硬件, 那么只能通过一个循环(loop) 来等 l4 kernel 发来的中断notify
另外 l4 的trap_init (void) 为空,不需要做vector 的初始化
进入interrupt_loop (sys_iguana.c)
首先 L4_Call(main_thread); 通过ipc 来实现等待timer init 完成, main_thread 应该就是oklinux kernel 主线程
然后进入循环 :
L4_Set_NotifyMask(~0UL);
L4_Accept(L4_NotifyMsgAcceptor); //设置等待所有的notify bit ,设置accept 接受异步ipc
tag = L4_Wait(&sender); //然后开始等待 ,
然后 获得当前thread info , 然后resg mode ++ 就表示进入oklinux kernel mode ,进入kernel mode 是假的,就是标记下mode =1
L4_MsgStore(tag, &msg); /* get ipc msg */
num = L4_MsgWord(&msg, 0);
if (sender.raw == L4_nilthread.raw) // 做个判断,只有sender 是一个nil thread 才是合法的 ,为什么???
irq = mask_to_irq(&num); // 从 utcb 的platform_reserved[0] 去获得hardware irq num
接着call handle_irq =>__do_IRQ 根据irq call 到driver 的 ISR handle
mode -- 返回到user mode (这样标记user mode ,kernel mode 有什么 实际意义???, 因为search 了下,只有对mode 赋值,没有判断)
最后 在进入下一次l4 wait 前(让出执行权前) 做一次 l4_checksched 检查thread info 中有没need sched 标志,有的话
发L4_Notify(main_thread, L4_PREEMPT);
关于从中断中返回, native linux 里 根据是usr,or kernel mode 下 发生中断,然后如果usr mode 下 完成中断处理,然后就
返回ret_to_user ,如果kernel mode 进入中断,完成中断处理后根据重调度标志 ,是否进行调度,然后仍然返回kernel mode
对于l4 来讲 ,linux kernel ,用户进程 ,都是跑在l4 kernel 上的thread ,他们都可以运行, 但是发生中断,和syscall 必须让
oklinux kernel 部分运行, 所以退出中断时,没有明确的svc, user 模式,只是让出运行权给其他thread, 所以就发出
L4_Notify(main_thread, L4_PREEMPT) ,然后继续让loop 等在l4 wait 上
关于 :
if (sender.raw == L4_nilthread.raw) // 做个判断,只有sender 是一个nil thread 才是合法的 ,为什么???
大牛说是中断通知l4 kernel 发来的,没有threadid ,用l4 lib 在hello里测试了下也的确是这样, 收到的thread 为0xfffffffc,就是nilthread
但是l4 lib 写的关于timer1,中断部分有问题
okl4 lib 写的没问题
补充:
中断通知 的sender.raw == L4_nilthread.raw
是在l4 kernel 的:
handle_irq=>kernel_deliver_notify
=> handler_tcb->sent_from = NILTHREAD;