在Linux中,中斷處理是通過中斷描述符表(Interrupt Descriptor Table, IDT)和中斷服務例程(Interrupt Service Routine, ISR)來實現的。以下是Linux驅動中進行中斷處理的基本步驟:
首先,需要在Linux內核中注冊中斷。這通常通過request_irq函數來完成。
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *devname, void *dev_id);
irq: 中斷號。handler: 中斷服務例程的指針。flags: 中斷處理的標志,例如IRQF_SHARED表示多個設備共享同一個中斷線。devname: 設備名稱。dev_id: 設備標識符,用于區分不同的設備。中斷服務例程(ISR)是一個特殊的函數,當發生中斷時,內核會調用這個函數。
irq_handler_t my_irq_handler(int irq, void *dev_id) {
// 中斷處理代碼
return IRQ_HANDLED;
}
irq: 中斷號。dev_id: 設備標識符。ISR應該盡可能快地完成處理,并返回IRQ_HANDLED或IRQ_NONE。
使用request_irq函數注冊ISR。
int ret = request_irq(irq_number, my_irq_handler, IRQF_SHARED, "my_device", NULL);
if (ret) {
printk(KERN_ALERT "Failed to register IRQ %d\n", irq_number);
}
在中斷服務例程中,處理中斷事件。這可能包括讀取硬件狀態、清除中斷標志、執行必要的操作等。
irq_handler_t my_irq_handler(int irq, void *dev_id) {
// 讀取硬件狀態
unsigned int status = readl(my_device->base_addr + MY_DEVICE_STATUS_REG);
// 清除中斷標志
writel(status, my_device->base_addr + MY_DEVICE_STATUS_REG);
// 處理中斷事件
if (status & MY_DEVICE_INTERRUPT_FLAG) {
// 執行中斷處理代碼
}
return IRQ_HANDLED;
}
當設備不再需要中斷處理時,應該注銷中斷服務例程。這通過free_irq函數來完成。
void free_irq(unsigned int irq, void *dev_id);
irq: 中斷號。dev_id: 設備標識符。free_irq(irq_number, NULL);
需要注意的是,中斷服務例程運行在中斷上下文中,而普通函數運行在進程上下文中。中斷上下文不能睡眠或執行某些阻塞操作,因為它會阻塞整個系統。
可以通過設置中斷親和性來指定中斷處理應該在哪幾個CPU上運行。
int irq_set_affinity(unsigned int irq, cpumask_t mask);
irq: 中斷號。mask: CPU掩碼。Linux驅動中的中斷處理涉及注冊中斷、編寫和注冊中斷服務例程、處理中斷事件以及注銷中斷。中斷服務例程應該盡可能快地完成處理,并返回適當的狀態碼。中斷上下文和進程上下文有不同的限制和要求。