嵌入式核心及驅動開發之學習筆記(十一) 中斷優化處理
阿新 • • 發佈:2018-12-22
ARM cortex-A系列的核心不支援中斷巢狀。在核心中斷函式中,如果中斷處理時間過長,產生中斷巢狀,重者系統崩潰,輕者也會影響其他事件處理。這也是中斷中不能使用延時函式的原因。
但是有些高實時性裝置(比如網絡卡),就是需要處理大量的業務。為了滿足中斷處理時間儘量短的原則,我們將一些簡單的處理放在中斷中實現,這個階段叫做中斷的上半部;其他一些複雜、耗時間的操作丟給核心執行緒,讓核心來排程其執行,這是中斷的下半部。
中斷事件 --> 跳轉中斷入口 --> 中斷中執行簡單處理 --> 並啟動核心排程複雜的處理 --> 結束中斷
處理方式
- softirq: 處理比較快,但是核心級別的機制,需要修改整個核心原始碼,不推薦也不常用
- tasklet: 內部實現實際呼叫了softirq
- workqueue: 工作佇列
tasklet
啟動"下半部"實際上就是把結構體描述的物件丟給核心執行緒的動作。
結構體
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long); // 下半部的實現邏輯
unsigned long data; // 傳遞給func
};
1.初始化物件
struct tasklet_struct mytasklet; tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data)
2.構造"下半部"實現邏輯
void key_tasklet_half_irq(unsigned long data)
{
//............
//............
}
3."上半部"啟動"下半部"
tasklet_schedule(&key_dev->mytasklet);
4.(在模組解除安裝時)登出核心執行緒中的物件
tasklet_kill(&key_dev->mytasklet);
完成的例子(驅動程式)
//key_drv.c #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/poll.h> #include <asm/io.h> #include <asm/uaccess.h> irqreturn_t key_irq_handler(int irqno, void *devid); void key_tasklet_half_irq(struct work_struct *work); ssize_t key_drv_read (struct file *, char __user *, size_t, loff_t *); ssize_t key_drv_write (struct file *, const char __user *, size_t, loff_t *); int key_drv_open (struct inode *, struct file *); int key_drv_close (struct inode *, struct file *); #define GPXCON_REG 0x11000C20 #define KEY_ENTER 28 const struct file_operations key_fops = { .open = key_drv_open, .read = key_drv_read, .write = key_drv_write, .release = key_drv_close, }; struct key_event{ int code; // 按鍵的型別 int value; // 狀態 }; struct key_desc{ unsigned int dev_major; struct class *cls; struct device *dev; int irqno; void *reg_base; int key_state; //表示是否有資料 struct key_event event; struct tasklet_struct mytasklet; }; struct key_desc *key_dev; int get_irqno_from_node(void) { //從裝置樹路徑,查詢節點 struct device_node *np = of_find_node_by_path("/key_int_node"); if(np){ printk("find node ok\n"); }else{ printk("find node failed\n"); } int irqno = irq_of_parse_and_map(np, 0); printk("irqno = %d\n", irqno); return irqno; } static int __init key_drv_init(void) { int ret; //物件例項化 key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL); //申請主裝置號 key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops); //建立裝置結點 key_dev->cls = class_create(THIS_MODULE, "key_cls"); key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major,0), NULL, "key0"); //硬體初始化 key_dev->irqno = get_irqno_from_node(); ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key3_eint10", NULL); if(ret != 0) { printk("request_irq error\n"); return ret; } key_dev->reg_base = ioremap(GPXCON_REG, 8); //初始化tasklet tasklet_init(&key_dev->mytasklet, key_tasklet_half_irq, 45); return 0; } static void __exit key_drv_exit(void) { tasklet_kill(&key_dev->mytasklet); iounmap(key_dev->reg_base); //去對映 free_irq(key_dev->irqno, NULL); //釋放中斷資源 device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0)); // class_destroy(key_dev->cls); // unregister_chrdev(key_dev->dev_major, "key_drv"); //登出主裝置號 kfree(key_dev); //釋放結構體記憶體 } irqreturn_t key_irq_handler(int irqno, void *devid) { printk("-------%s-------------\n", __FUNCTION__); // 啟動下半步 tasklet_schedule(&key_dev->mytasklet); return IRQ_HANDLED; } void key_tasklet_half_irq(struct work_struct *work) { printk("-------%s-------------\n", __FUNCTION__); } ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos) { printk("-------%s-------------\n", __FUNCTION__); return count; } ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos) { printk("-------%s-------------\n", __FUNCTION__); return 0; } int key_drv_open(struct inode *inode, struct file *filp) { printk("-------%s-------------\n", __FUNCTION__); return 0; } int key_drv_close (struct inode *inode, struct file *filp) { printk("-------%s-------------\n", __FUNCTION__); return 0; } module_init(key_drv_init); module_exit(key_drv_exit); MODULE_LICENSE("GPL");
演示
workqueue
結構體描述
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
};
1.初始化物件
struct work_struct mywork;
INIT_WORK(struct work_struct *work, work_func_t func);
2.構造"下半部"實現邏輯
void work_irq_half(struct work_struct *work)
{
//...填入要實現的內容
}
3."上半部"啟動"下半部"
schedule_work(&key_dev->mywork);
程式碼(驅動)
//key_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/uaccess.h>
irqreturn_t key_irq_handler(int irqno, void *devid);
void work_irq_half(struct work_struct *work);
ssize_t key_drv_read (struct file *, char __user *, size_t, loff_t *);
ssize_t key_drv_write (struct file *, const char __user *, size_t, loff_t *);
int key_drv_open (struct inode *, struct file *);
int key_drv_close (struct inode *, struct file *);
#define GPXCON_REG 0x11000C20
#define KEY_ENTER 28
const struct file_operations key_fops = {
.open = key_drv_open,
.read = key_drv_read,
.write = key_drv_write,
.release = key_drv_close,
};
struct key_event{
int code; // 按鍵的型別
int value; // 狀態
};
struct key_desc{
unsigned int dev_major;
struct class *cls;
struct device *dev;
int irqno;
void *reg_base;
int key_state; //表示是否有資料
struct key_event event;
struct work_struct mywork;
};
struct key_desc *key_dev;
int get_irqno_from_node(void)
{
//從裝置樹路徑,查詢節點
struct device_node *np = of_find_node_by_path("/key_int_node");
if(np){
printk("find node ok\n");
}else{
printk("find node failed\n");
}
int irqno = irq_of_parse_and_map(np, 0);
printk("irqno = %d\n", irqno);
return irqno;
}
static int __init key_drv_init(void)
{
int ret;
//物件例項化
key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);
//申請主裝置號
key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);
//建立裝置結點
key_dev->cls = class_create(THIS_MODULE, "key_cls");
key_dev->dev = device_create(key_dev->cls, NULL,
MKDEV(key_dev->dev_major,0), NULL, "key0");
//硬體初始化
key_dev->irqno = get_irqno_from_node();
ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
"key3_eint10", NULL);
if(ret != 0)
{
printk("request_irq error\n");
return ret;
}
key_dev->reg_base = ioremap(GPXCON_REG, 8);
//初始化work
INIT_WORK(&key_dev->mywork, work_irq_half);
return 0;
}
static void __exit key_drv_exit(void)
{
iounmap(key_dev->reg_base); //去對映
free_irq(key_dev->irqno, NULL); //釋放中斷資源
device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0)); //
class_destroy(key_dev->cls); //
unregister_chrdev(key_dev->dev_major, "key_drv"); //登出主裝置號
kfree(key_dev); //釋放結構體記憶體
}
irqreturn_t key_irq_handler(int irqno, void *devid)
{
printk("-------%s-------------\n", __FUNCTION__);
// 啟動下半步
schedule_work(&key_dev->mywork);
return IRQ_HANDLED;
}
void work_irq_half(struct work_struct *work)
{
printk("-------%s-------------\n", __FUNCTION__);
}
ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
printk("-------%s-------------\n", __FUNCTION__);
return count;
}
ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
printk("-------%s-------------\n", __FUNCTION__);
return 0;
}
int key_drv_open(struct inode *inode, struct file *filp)
{
printk("-------%s-------------\n", __FUNCTION__);
return 0;
}
int key_drv_close (struct inode *inode, struct file *filp)
{
printk("-------%s-------------\n", __FUNCTION__);
return 0;
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");