快捷搜索:   服务器  安全  linux 安全  MYSQL  dedecms

FreeBSD 5 内核源代码分析之中断处理(2)


       if (isrc == NULL)
          return (EINVAL);
       error = ithread_add_handler(isrc->is_ithread, name, handler, arg,
           ithread_priority(flags), flags, cookiep);
       if (error == 0) {
          intrcnt_updatename(isrc);
          isrc->is_pic->pic_enable_intr(isrc);
          isrc->is_pic->pic_enable_source(isrc);
       }
       return (error);
    }

    int
    ithread_add_handler(struct ithd* ithread, const char *name,
        driver_intr_t handler, void *arg, u_char pri, enum intr_type flags,
        void **cookiep)
    {
       struct intrhand *ih, *temp_ih;

       if (ithread == NULL || name == NULL || handler == NULL)
          return (EINVAL);

       ih = malloc(sizeof(struct intrhand), M_ITHREAD, M_WAITOK | M_ZERO);
       ih->ih_handler = handler;
       ih->ih_argument = arg;
       ih->ih_name = name;
       ih->ih_ithread = ithread;
       ih->ih_pri = pri;
       if (flags & INTR_FAST)
          ih->ih_flags = IH_FAST;
       else if (flags & INTR_EXCL)
          ih->ih_flags = IH_EXCLUSIVE;
       if (flags & INTR_MPSAFE)
          ih->ih_flags |= IH_MPSAFE;
       if (flags & INTR_ENTROPY)
          ih->ih_flags |= IH_ENTROPY;

       mtx_lock(&ithread->it_lock);
       if ((flags & INTR_EXCL) != 0 && !TAILQ_EMPTY(&ithread->it_handlers))
          goto fail;
       if (!TAILQ_EMPTY(&ithread->it_handlers)) {
          temp_ih = TAILQ_FIRST(&ithread->it_handlers);
          if (temp_ih->ih_flags & IH_EXCLUSIVE)
             goto fail;
          if ((ih->ih_flags & IH_FAST) && !(temp_ih->ih_flags & IH_FAST))
             goto fail;
          if (!(ih->ih_flags & IH_FAST) && (temp_ih->ih_flags & IH_FAST))
             goto fail;
       }

       TAILQ_FOREACH(temp_ih, &ithread->it_handlers, ih_next)
           if (temp_ih->ih_pri > ih->ih_pri)
              break;
       if (temp_ih == NULL)
          TAILQ_INSERT_TAIL(&ithread->it_handlers, ih, ih_next);
       else
          TAILQ_INSERT_BEFORE(temp_ih, ih, ih_next);
       ithread_update(ithread);
       mtx_unlock(&ithread->it_lock);

       if (cookiep != NULL)
          *cookiep = ih;
       CTR3(KTR_INTR, "%s: added %s to %s", __func__, ih->ih_name,
           ithread->it_name);
       return (0);

    fail:
       mtx_unlock(&ithread->it_lock);
       free(ih, M_ITHREAD);
       return (EINVAL);
    }


    1.2 8259A的登记过程

    下面我们以8259A为例,看看系统是如何为其注册中断源的,即注册INTSRC(0)~INTSRC(15)。
    描述8259A中断控制器的数据结构是struct atpic_intsrc,其第一个成员是一个中断源结构,
    这种类型定义方法是BSD中常用的方法,起到了面向对象编程中继承的作用。

    由于两个级连的8259A中断控制器可以控制16个中断,因此系统注册16个struct atpic_intsrc。
    这些中断响应程序的的入口地址是IDTVEC(atpic_intr ## irq )。IDTVEC在.c文件中将扩展成
    Xatpic_intr0 至Xatpic_intr15,即为函数名的引用。而在.s文件中将扩展成
    代码:

       ALIGN_TEXT;
       .globl Xatpic_intr0;
       .type Xatpic_intr0,@function;
    Xatpic_intr0:


    等等,即定义一个全局的函数,也就是说在.c文件中只是引用该函数,真正定义该函数的是
    在sys/i386/isa/atpic_vector.s中,该函数实际上就是一个对atpic_handle_intr()
    函数的包装,我们后面还将看到该函数。
    代码:

    struct atpic_intsrc {
       struct intsrc at_intsrc;
       int   at_irq;      /* Relative to PIC base. */
       inthand_t *at_intr;
       u_long   at_count;
       u_long   at_straycount;
    };

    static struct atpic_intsrc atintrs[] = {
       INTSRC(0),
       INTSRC(1),
       INTSRC(2),
       INTSRC(3),
       INTSRC(4),
       INTSRC(5),
       INTSRC(6),
       INTSRC(7),
       INTSRC(8),
       INTSRC(9),
       INTSRC(10),
       INTSRC(11),
       INTSRC(12),
       INTSRC(13),
       INTSRC(14),
       INTSRC(15),
    };

    #define   INTSRC(irq)                     \
       { { &atpics[(irq) / 8].at_pic }, (irq) % 8,         \
           IDTVEC(atpic_intr ## irq ) }


    系统启动时,调用8259A的初始化函数atpic_init(),为非SLAVE IRQ号注册中断源。
    并在i386初始化时调用atpic_startup()函数,注册中断向量
    IDTVEC(atpic_intr ## irq ),注意,这只是注册总的包装函数,
    具体IRQ号的中断处理函数将由设备驱动通过intr_add_handler()函数来注册。
    代码:

    SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)

    static void
    atpic_init(void *dummy __unused)
    {
       int i;

       /* Loop through all interrupt sources and add them. */
       for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
          if (i == ICU_SLAVEID)
             continue;
          intr_register_source(&atintrs[i].at_intsrc);
       }
    }

    void
    init386(first)
       int first;
    {
       ......

    #ifdef DEV_ISA
       atpic_startup();
    #endif

       ......
    }

    void
    atpic_startup(void)
    {
       struct atpic_intsrc *ai;
       int i;

       /* Start off with all interrupts disabled. */
       imen = 0xffff;
       i8259_init(&atpics[MASTER], 0);
       i8259_init(&atpics[SLAVE], 1);
       atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);

       /* Install low-level interrupt handlers for all of our IRQs. */
       for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
          if (i == ICU_SLAVEID)
             continue;
          ai = &atintrs[i];
          ai->at_intsrc.is_count = &ai->at_count;
          ai->at_intsrc.is_straycount = &ai->at_straycount;
          setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
              ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL,
              GSEL(GCODE_SEL, SEL_KPL));
       }
    }

    2,IRQ中断的处理过程
    代码:

    /*
     * Macros for interrupt interrupt entry, call to handler, and exit.
     */
    #define   INTR(irq_num, vec_name) \
       .text ;                        \
       SUPERALIGN_TEXT ;                  \
    IDTVEC(vec_name) ;                     \
       pushl    ;      /* dummy error code */         \
       pushl    ;      /* dummy trap type */         \
       pushal ;      /* 8 ints */            \
       pushl   %ds ;      /* save data and extra segments ... */   \
       pushl   %es ;                     \
       pushl   %fs ;                     \
       mov   $KDSEL,%ax ;   /* load kernel ds, es and fs */      \
       mov   %ax,%ds ;                  \
       mov   %ax,%es ;                  \
       mov   $KPSEL,%ax ;                  \
       mov   %ax,%fs ;                  \
    ;                           \
       FAKE_MCOUNT(13*4(%esp)) ;   /* XXX late to avoid double count */ \
       pushl   $irq_num;    /* pass the IRQ */         \
       call   atpic_handle_intr ;               \
       addl   , %esp ;   /* discard the parameter */      \
    ;                           \
       MEXITCOUNT ;                     \
       jmp   doreti


 

   IRQ产生时,系统根据产生中断的IRQ号找到相应的中断向量入口,即此处的IDT_VEC(vec_name),
    再这里,构造好函数atpic_handle_intr()的调用栈后,将转到atpic_handle_intr()进行处理。

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论