天天看點

核心代碼閱讀(15) - 中斷請求隊列的初始化

中斷請求隊列的初始化

資料結構

irq通道的控制

struct hw_interrupt_type {
        const char * typename;
        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 (*end)(unsigned int irq);
        void (*set_affinity)(unsigned int irq, unsigned long mask);
    };
    typedef struct hw_interrupt_type  hw_irq_controller;      
1) startup
   啟動通道
2) ack
   響應      

irq請求隊列頭部資料結構

typedef struct {
        unsigned int status;                /* IRQ status */
        hw_irq_controller *handler;
        struct irqaction *action;        /* IRQ action list */
        unsigned int depth;                /* nested irq disables */
        spinlock_t lock;
    } ____cacheline_aligned irq_desc_t;
    extern irq_desc_t irq_desc [NR_IRQS];      
1) handler 就是通道級别的操作函數。
2) action 是這個隊列的頭部,類型是 irqaction。      

irqaction結構

struct irqaction {
        void (*handler)(int, void *, struct pt_regs *);
        unsigned long flags;
        unsigned long mask;
        const char *name;
        void *dev_id;
        struct irqaction *next;
    };      
1) handler
   就是中斷服務程式。
2) dev_id
   是裝置号,因為這個通道是共享的,是以要依賴這個dev_id區分到底是誰産生的中斷。
3) next 連結清單。      

隊列頭irq_desc的初始化

在 init_IRQ -> init_ISA_irqs中      
void __init init_ISA_irqs (void)
    {
        int i;
        init_8259A(0);
        for (i = 0; i < NR_IRQS; i++) {
                irq_desc[i].status = IRQ_DISABLED;
                irq_desc[i].action = 0;
                irq_desc[i].depth = 1;
                if (i < 16) {
                        irq_desc[i].handler = &i8259A_irq_type;
                } else {
                        irq_desc[i].handler = &no_irq_type;
                }
        }
    }      
1) irq_desc[i].handler = &i8259A_irq_type;
   設定隊列頭 irq_desc中的handler為8259的處理函數。      

添加一個 interrupt line 到系統中(外設主動調用把自己的中斷服務程式注冊到相對的請求隊列中)

int request_irq(unsigned int irq, 
                void (*handler)(int, void *, struct pt_regs *),
                unsigned long irqflags, 
                const char * devname,
                void *dev_id)
    {
        int retval;
        struct irqaction * action;
    #if 1
        if (irqflags & SA_SHIRQ) {
                if (!dev_id)
                        printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);
        }
    #endif
        if (irq >= NR_IRQS)
                return -EINVAL;
        if (!handler)
                return -EINVAL;
        action = (struct irqaction *)
                        kmalloc(sizeof(struct irqaction), GFP_KERNEL);
        if (!action)
                return -ENOMEM;
        action->handler = handler;
        action->flags = irqflags;
        action->mask = 0;
        action->name = devname;
        action->next = NULL;
        action->dev_id = dev_id;
        retval = setup_irq(irq, action);
        if (retval)
                kfree(action);
        return retval;
    }      
1) if (!dev_id)
   如果沒有dev_id則報錯。
2) kmalloc(sizeof(struct irqaction), GFP_KERNEL);
   從slab配置設定一個 irqaction
3) action->handler = handler;
   設定handler
4) action->dev_id = dev_id;
   設定dev_id
5) printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);
   注意這個printk的技巧:列印出調用者的指令位址。
   &irq取出第一個參數在棧上的位址,
   (&irq)[-1] 在棧上往上找一個,就是函數的傳回位址。也就是調用者的位址。      

setup_irq 鍊入請求隊列

int setup_irq(unsigned int irq, struct irqaction * new)
    {
        int shared = 0;
        unsigned long flags;
        struct irqaction *old, **p;
        irq_desc_t *desc = irq_desc + irq;
        if (new->flags & SA_SAMPLE_RANDOM) {
                rand_initialize_irq(irq);
        }
        spin_lock_irqsave(&desc->lock,flags);
        p = &desc->action;
        if ((old = *p) != NULL) {
                if (!(old->flags & new->flags & SA_SHIRQ)) {
                        spin_unlock_irqrestore(&desc->lock,flags);
                        return -EBUSY;
                }
                do {
                        p = &old->next;
                        old = *p;
                } while (old);
                shared = 1;
        }
        *p = new;
        if (!shared) {
                desc->depth = 0;
                desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING);
                desc->handler->startup(irq);
        }
        spin_unlock_irqrestore(&desc->lock,flags);
        register_irq_proc(irq);
        return 0;
    }      
1) rand_initialize_irq(irq);
   借助外設中斷的時序來增進随機數。
2) do {
            p = &old->next;
            old = *p;
  } while (old);
  找到隊列尾端,并插入。      

繼續閱讀