Linux Interrupt Handling Mechanism

Linux Interrupt Fundamentals

Interrupt API Functions

Interrupt Identifiers

Each interrupt has a unique numerical identifier used for differentiation. The Linux kernel represents this identifier using an integer variable.

request_irq Function

Interrupts must be explicitly registered in Linux. The request_irq function activates and enables an interrupt. Note that this function may sleep and cannot be used in interrupt contexts or non-sleep-safe code segments.

int register_interrupt(unsigned int irq_num,
                      irq_handler_t isr,
                      unsigned long attributes,
                      const char *label,
                      void *device_ref)
{
    /* Registers and enables interrupt handler */
}
FlagDescription
IRQF_SHAREDMultiple devices share interrupt line
IRQF_ONESHOTSingle-shot interrupt
IRQF_TRIGGER_RISINGRising-edge trigger
IRQF_TRIGGER_FALLINGFalling-edge trigger

free_irq Function

Registered interrupts must be released using free_irq. For non-shared interrupts, this disables the interrupt and removes the handler.

void release_interrupt(unsigned int irq_num, void *device_ref);

Interrupt Service Routines

typedef irqreturn_t (*isr_func)(int, void*);

enum interrupt_status {
    IRQ_UNHANDLED = 0,
    IRQ_PROCESSED = 1,
    IRQ_WAKE_THREAD = 2
};

Interrupt Control Functions

void activate_irq(unsigned int irq_num);
void deactivate_irq(unsigned int irq_num);
void deactivate_irq_async(unsigned int irq_num);
void local_irq_enable(void);
void local_irq_disable(void);
void save_local_irq(unsigned long *state);
void restore_local_irq(unsigned long state);

Top and Bottom Halves

Interrupt processing is divided into two segments:

  • Top Half: Fast, non-blocking operations
  • Bottom Half: Time-consuming processing deferred for later execution

Deferred Execution Mechanisms

Tasklets
struct tasklet_struct {
    struct tasklet_struct *next_task;
    unsigned long status;
    atomic_t ref_count;
    void (*callback)(unsigned long);
    unsigned long callback_data;
};

void setup_tasklet(struct tasklet_struct *t, 
                   void (*func)(unsigned long), 
                   unsigned long data);
Work Queues
struct work_struct {
    /* Work queue structure */
};

#define INIT_WORK(work_ptr, work_func)
#define DECLARE_WORK(work_name, func_ptr)
bool schedule_work(struct work_struct *work_item);

Device Tree Interrupt Configuration

GIC Controller

Example device tree node:

interrupt_controller@a0021000 {
    compatible = "arm,cortex-a7-gic";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0xa0021000 0x1000>,
          <0xa0022000 0x2000>;
};

EXTI Controller

external_interrupts: interrupt-controller@5000d000 {
    compatible = "st,stm32mp1-exti", "syscon";
    interrupt-controller;
    #interrupt-cells = <2>;
    reg = <0x5000d000 0x400>;
};

Interrupt Handling Implementation

struct custom_gpio_device {
    struct gpio_desc *gpio;
    unsigned int irq_id;
    struct timer_list debounce_timer;
    spinlock_t lock;
};

static irqreturn_t gpio_isr(int irq, void *dev_id) {
    struct custom_gpio_device *dev = dev_id;
    mod_timer(&dev->debounce_timer, 
              jiffies + msecs_to_jiffies(15));
    return IRQ_HANDLED;
}

static void debounce_handler(struct timer_list *t) {
    struct custom_gpio_device *dev = 
        container_of(t, struct custom_gpio_device, debounce_timer);
    unsigned long flags;
    
    spin_lock_irqsave(&dev->lock, flags);
    /* Read and process GPIO state */
    spin_unlock_irqrestore(&dev->lock, flags);
}

static int configure_gpio_interrupt(struct device_node *node) {
    struct custom_gpio_device *dev;
    int gpio_num = of_get_named_gpio(node, "interrupt-gpio", 0);
    unsigned int irq_id = irq_of_parse_and_map(node, 0);
    
    /* GPIO and interrupt setup */
    return request_irq(irq_id, gpio_isr, IRQF_TRIGGER_BOTH, 
                       "gpio_irq", dev);
}

Driver Initialization

static int __init gpio_driver_init(void) {
    struct device_node *node = of_find_node_by_path("/gpio_device");
    /* Device tree parsing */
    /* GPIO and interrupt configuration */
    /* Character device setup */
    timer_setup(&dev->debounce_timer, debounce_handler, 0);
    return 0;
}

Thẻ: Linux_kernel Interrupt_handling Device_tree GIC Tasklet

Đăng vào ngày 7 tháng 6 lúc 23:47