- 中断的处理有几个原则:
① 不能嵌套;
② 越快越好。
- 在处理当前中断时,即使发生了其他中断,其他中断也不会得到处理,所以中断的处理要越快越好
- 某些中断要做的事情稍微耗时,这时可以把中断拆分为上半部、下半部
在上半部处理紧急的事情,在上半部的处理过程中,中断是被禁止的;
在下半部处理耗时的事情,在下半部的处理过程中,中断是使能的。
- 上半部和下半部是多对一的关系
- 如何使用下半部?
- 先定义 tasklet,需要使用时调用 tasklet_schedule,驱动卸载前调用 tasklet_kill
- tasklet_schedule 只是把 tasklet 放入内核队列,它的 func 函数会在软件中断的执行过程中被调用
- 内核相关函数
- 定义 tasklet
- state 有 2 位:
- 定义 tasklet
① bit0 表示 TASKLETSTATESCHED
等于 1 时表示已经执行了 taskletschedule 把该 tasklet 放入队列了;tasklet_schedule 会判断该
位,如果已经等于 1 那么它就不会再次把 tasklet 放入队列。
② bit1 表示 TASKLET_STATERUN
等于 1 时,表示正在运行 tasklet 中的 func 函数;函数执行完后内核会把该位清 0
- count 表示该 tasklet 是否使能:等于 0 表示使能了,非 0 表示被禁止了。对于 count 非 0 的tasklet,里面的 func 函数不会被执行
- 用这 2 个宏来定义结构体
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1658545/1619177584821-28759f8b-dacd-4ad5-8515-f8b88f5aa19d.png#align=left&display=inline&height=90&margin=%5Bobject%20Object%5D&name=image.png&originHeight=180&originWidth=970&size=34766&status=done&style=none&width=485)
- 初始化 tasklet 结构体
- 有多个按键,想为每个按键都定义一个tasklet_struct结构体的话,使用tasklet_init函数
- 使能/禁止 tasklet
- 调度 tasklet
- kill tasklet
- 修改驱动程序
- 在中断服务程序里启动中断下半部tasklet ```c struct gpio_key{ int gpio; struct gpio_desc gpiod; int flag; int irq; struct timer_list key_timer; struct tasklet_struct tasklet; / tasklet结构体 */ } ;
/ 中断下半部的处理函数 / static void key_tasklet_func(unsigned long data) { / data ==> gpio / struct gpio_key *gpio_key = data; int val; int key;
val = gpiod_get_value(gpio_key->gpiod);
printk("key_tasklet_func key %d %d\n", gpio_key->gpio, val);
}
static irqreturn_t gpio_key_isr(int irq, void dev_id) { struct gpio_key gpio_key = dev_id; //printk(“gpio_key_isr key %d irq happened\n”, gpio_key->gpio);
/* 在中断处理函数中开启中断下半部tasklet */
tasklet_schedule(&gpio_key->tasklet);
mod_timer(&gpio_key->key_timer, jiffies + HZ/50);
return IRQ_HANDLED;
}
/* 1. 从platform_device获得GPIO
- gpio=>irq
request_irq / static int gpio_key_probe(struct platform_device pdev) { int err; struct device_node *node = pdev->dev.of_node; int count; int i; enum of_gpio_flags flag;
printk(“%s %s line %d\n”, FILE, FUNCTION, LINE);
count = ofgpiocount(node); if (!count) { printk(“%s %s line %d, there isn’t any gpio available\n”, FILE, FUNCTION, __LINE); return -1; }
gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL); for (i = 0; i < count; i++) {
gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, &flag); if (gpio_keys_100ask[i].gpio < 0) {printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
} gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio); gpio_keys_100ask[i].flag = flag & OF_GPIO_ACTIVE_LOW; gpio_keys_100ask[i].irq = gpio_to_irq(gpio_keys_100ask[i].gpio);
setup_timer(&gpio_keys_100ask[i].key_timer, key_timer_expire, &gpio_keys_100ask[i]); gpio_keys_100ask[i].key_timer.expires = ~0; add_timer(&gpio_keys_100ask[i].key_timer);
/ 在probe函数中初始化tasklet结构体 / tasklet_init(&gpio_keys_100ask[i].tasklet, key_tasklet_func, &gpio_keys_100ask[i]); }
for (i = 0; i < count; i++) { err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, “100ask_gpio_key”, &gpio_keys_100ask[i]); }
/ 注册file_operations / major = register_chrdev(0, “100ask_gpio_key”, &gpio_key_drv); / /dev/gpio_key /
gpiokeyclass = classcreate(THISMODULE, “100askgpiokey_class”); if (IS_ERR(gpio_key_class)) { printk(“%s %s line %d\n”, __FILE, __FUNCTION, __LINE); unregister_chrdev(major, “100ask_gpio_key”); return PTR_ERR(gpio_key_class); }
device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, “100ask_gpio_key”); / /dev/100ask_gpio_key /
return 0;
}
static int gpio_key_remove(struct platform_device pdev) { //int err; struct device_node node = pdev->dev.of_node; int count; int i;
device_destroy(gpio_key_class, MKDEV(major, 0));
class_destroy(gpio_key_class);
unregister_chrdev(major, "100ask_gpio_key");
count = of_gpio_count(node);
for (i = 0; i < count; i++)
{
free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]);
del_timer(&gpio_keys_100ask[i].key_timer);
/* 在remove函数中kill掉中断下半部tasklet */
tasklet_kill(&gpio_keys_100ask[i].tasklet);
}
kfree(gpio_keys_100ask);
return 0;
} ```