Chinaunix首页 | 论坛 | 博客
  • 博客访问: 403845
  • 博文数量: 53
  • 博客积分: 1910
  • 博客等级: 中尉
  • 技术积分: 1130
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-10 14:56
文章分类

全部博文(53)

文章存档

2013年(1)

2012年(17)

2011年(33)

2010年(2)

分类: LINUX

2010-05-11 13:06:47

 
///////////////////////////////////////////////////////////////////////////////////
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;      

 
阅读(1745) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:oklinux 系统调用 简单分析

给主人留下些什么吧!~~