tasklet
1、什么是tasklet
tasklet是中断处理中断下文常用的一种方法,tasklet是一种特殊的软中断。处理中断下文的机制还有工作队列和软中断。
2、怎么使用tasklet来设计中断下文
框图:
linux把中断分成两个部分,一个是上半部分,一个是下半部分。上半部分我们值处理紧急的事情;同时调用tasklet来启动中断下文,比较消耗事件的就要放到下文来处理。调用tasklet以后,tasklet绑定的函数并不会立马执行,而是出中断以后,经过一个很短的不确定时间在来执行。
3、tasklet定义
tasklet由tasklet_struct 结构表示,每个结构体单独代表一个tasklet,在<linux/interrupt.h>中定义为:
c
struct tasklet_struct
{
struct tasklet_struct *next;//链表中的下一个tasklet,方便管理和设置tasklet
unsigned long state;//tasklet状态
atomic_t count;//表示tasklet是否在激活状态,0表示激活,非0表示非激活
void (*func)(unsigned long);//结构体中的func成员是tasklet的绑定函数,data是他的唯一参数
unsigned long data;//函数执行的时候传递的参数
};
tasklet相关函数
<1>tasklet_schedule函数
c
void tasklet_schedule(struct tasklet_struct *t)
作用:调度tasklet
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),
unsigned long data);
作用:动态初始化tasklet
参数:*t:指向tasklet_struct结构的指针
func:tasklet绑定的函数
data:函数执行时候传递的参数
tasklet_kill(struct tasklet_struct *t)
参数:指向tasklet_struct结构的指针
注意:这个函数会等待tasklet执行完毕,然后在将它移除。该函数可能会引起休眠,所以要禁止在中断
上下文中使用
使用tasklet设计中断上下文步骤
步骤1:定义一个tasklet结构体
2、动态初始化tasklet
3、编写tasklet绑定的函数
4、在中断上文调用tasklet
5、卸载模块的时候删除tasklet
示例
c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
static struct device_node *test_device_node;
static struct property *test_node_property;
struct tasklet_struct key_test;
int gpio_nu;
int irq;
void test(unsigned long data)
{
int i=100;
printk("i is %d \n",i);
while(i--)
{
printk("test_key is %d \n",i);
}
}
irq_handler_t test_key(int irq,void *args)
{
printk("start \n");
tasklet_schedule(&key_test);
printk("end \n");
return IRQ_HANDLED;
}
int beep_probe(struct platform_device *pdev)
{
int ret;
//method 1
//printk("node name is %s\n",pdev->dev.of_node->name);
//method 2
test_device_node = of_find_node_by_path("/test_key");
if(test_device_node == NULL)
{
printk("of_find_node_by_path is error\n");
return -1;
}
gpio_nu = of_get_named_gpio(test_device_node,"gpios",0);
if(gpio_nu<0)
{
printk("of_get_named_gpio is error \n");
return -1;
}
gpio_direction_input(gpio_nu);
// irq = gpio_to_irq(gpio_nu);
irq = irq_of_parse_and_map(test_device_node,0);
printk("irq is %d\n",irq);
ret = request_irq(irq,test_key,IRQF_TRIGGER_RISING,"test_key",NULL);
if(ret<0)
{
printk("request_irq is error \n");
return -1;
}
tasklet_init(&key_test,test,10);
return 0;
}
int beep_remove(struct platform_device *pdev)
{
printk("beep_remove\n");
return 0;
}
const struct platform_device_id beep_id_table={
.name = "beep_test"
};
const struct of_device_id of_match_table_test[]={
{.compatible = "keys"},
{}
};
struct platform_driver beep_device = {
.probe =beep_probe,
.remove = beep_remove,//当driver和device热议一个remove时会执行该函数
.driver = {
.owner = THIS_MODULE,
.name = "123",
.of_match_table = of_match_table_test,
},
.id_table = &beep_id_table,
};
static int beep_driver_init(void)
{
int ret = 0;
ret = platform_driver_register(&beep_device);
if(ret<0)
{
printk("platform_driver_register err\n");
return ret;
}
printk("platform_driver_register OK\n");
return 0;
}
static void beep_driver_exit(void)
{
platform_driver_unregister(&beep_device);
free_irq(irq,NULL);
tasklet_kill(&test);
printk("bye");
}
module_init(beep_driver_init);
module_exit(beep_driver_exit)
MODULE_LICENSE("GPL");

等待队列
当我们的进程去访问设备的时候,经常需要等待有特定时间发生以后在继续往下运行,这个时候就需要在驱动里面实现当条件不满足的时候进程休眠,当条件满足的时候在由内核唤醒进行。那么等待队列就实现了在时间上的条件等待。
<1>等待队列头
等待队列头就是一个等待队列的头部,设个访问设备的进程都是一个队列项,当设备不可用的时候就要将这些进程对应的等待队列项添加到等待队列里面。
等待队列头结构体:
c
#include <linux/wait.h>
wait_queue_head_t;
wait_queue_head_t test_wq;//定义等待队列头
void init_waitqueue_head(wait_queue_head_t *q)//初始化等待队列头
DECLARE_WAIT_QUEUE_HEAD(wait_queue_head_t *q)//一次性完成等待队列头的定义和初始化
等待队列相关函数
c
void init_waitqueue_head(wait_queue_head_t *q)//初始化等待队列头
wait_event(wq,condition);
//wq:wait_queue_head_t 类型变量
//condition:等待条件,为假时进入休眠
//功能:不可中断的阻塞等待,让调用程序进入不可中断的睡眠状态,在等待队列里面睡眠,直到条件为真,
// 被内核唤醒
wait_event_interruptible(wq,condition)
//参数同上
//功能:可中断的阻塞等待,让调用程序进入可中断的睡眠状态,直到condition变成真被内核唤醒或
// 被信号打断唤醒
wake_up(x)
//功能:唤醒所有休眠进程
//x:等待队列头结构体指针
wake_up_interruptible(x)
//功能:唤醒可中断的休眠进程
示例代码:
c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/atomic.h>
#include <linux/wait.h>
#include <linux/sched.h>
static int irq = -1;
static int key_gpio_num = -1;
static atomic_t key_value = ATOMIC_INIT(0);
int value=0;
int wq_flags=0;
DECLARE_WAIT_QUEUE_HEAD(key_wq);
static irqreturn_t test_key(int irq, void *dev_id)
{
/* 这里翻转值:0/1 */
value = !value;
wq_flags=1;
wake_up(&key_wq);
atomic_set(&key_value, value);
pr_info("test_key interrupt, value=%d\n", value); // 调试输出
return IRQ_HANDLED;
}
/* --------- misc device --------- */
static int misc_open(struct inode *inode, struct file *file)
{
pr_info("hello misc_open\n");
return 0;
}
static int misc_release(struct inode *inode, struct file *file)
{
pr_info("open -> release\n");
return 0;
}
static ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *ppos)
{
wait_event_interruptible(key_wq,wq_flags);
int v;
v = atomic_read(&key_value);
if (size < sizeof(v))
return -EINVAL;
if (copy_to_user(ubuf, &v, sizeof(v)))
return -EFAULT;
wq_flags=0;
/* 返回读到的字节数 */
return sizeof(v);
}
static ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *ppos)
{
/* 当前不支持写 */
return -EINVAL;
}
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.release = misc_release,
.read = misc_read,
.write = misc_write,
};
static struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "hello_misc",
.fops = &misc_fops,
};
/* --------- platform driver --------- */
static int key_probe(struct platform_device *pdev)
{
int ret;
/* 1) 读取 GPIO 号(4.1.15 还在用 of_get_named_gpio) */
key_gpio_num = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);
if (key_gpio_num < 0) {
dev_err(&pdev->dev, "of_get_named_gpio failed: %d\n", key_gpio_num);
return key_gpio_num;
}
dev_info(&pdev->dev, "test_key = %d\n", key_gpio_num);
/* 2) 申请并配置为输入 */
ret = gpio_request(key_gpio_num, "key");
if (ret) {
dev_err(&pdev->dev, "gpio_request failed: %d\n", ret);
return ret;
}
ret = gpio_direction_input(key_gpio_num);
if (ret) {
dev_err(&pdev->dev, "gpio_direction_input failed: %d\n", ret);
goto err_free_gpio;
}
/* 3) 映射为 IRQ 并注册中断 */
irq = gpio_to_irq(key_gpio_num);
if (irq < 0) {
dev_err(&pdev->dev, "gpio_to_irq failed: %d\n", irq);
ret = irq;
goto err_free_gpio;
}
dev_info(&pdev->dev, "IRQ number: %d\n", irq);
ret = request_irq(irq, test_key,
IRQF_TRIGGER_RISING,
"test_key", NULL);
if (ret) {
dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
goto err_free_gpio;
}
/* 4) 注册 misc 设备 */
ret = misc_register(&misc_dev);
if (ret) {
dev_err(&pdev->dev, "misc_register failed: %d\n", ret);
goto err_free_irq;
} else {
dev_info(&pdev->dev, "misc_register succeeded\n");
}
dev_info(&pdev->dev, "key_probe OK\n");
return 0;
err_free_irq:
free_irq(irq, NULL);
irq = -1;
err_free_gpio:
gpio_free(key_gpio_num);
key_gpio_num = -1;
return ret;
}
static int key_remove(struct platform_device *pdev)
{
pr_info("key_remove\n");
/* 注销 misc 设备 */
misc_deregister(&misc_dev);
/* 释放 IRQ 和 GPIO */
if (irq >= 0) {
free_irq(irq, NULL);
irq = -1;
}
if (key_gpio_num >= 0) {
gpio_free(key_gpio_num);
key_gpio_num = -1;
}
return 0;
}
static const struct of_device_id of_match_table_test[] = {
{ .compatible = "keys" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_match_table_test);
static const struct platform_device_id key_id_table[] = {
{ .name = "key_test" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, key_id_table);
static struct platform_driver key_device = {
.probe = key_probe,
.remove = key_remove,
.driver = {
.owner = THIS_MODULE,
.name = "key_driver",
.of_match_table = of_match_table_test,
},
.id_table = key_id_table,
};
static int __init key_driver_init(void)
{
int ret = platform_driver_register(&key_device);
if (ret)
pr_err("platform_driver_register err: %d\n", ret);
else
pr_info("platform_driver_register OK\n");
return ret;
}
static void __exit key_driver_exit(void)
{
platform_driver_unregister(&key_device);
pr_info("bye\n");
}
module_init(key_driver_init);
module_exit(key_driver_exit);
MODULE_LICENSE("GPL");
应用程序
c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
int value;
fd = open("/dev/hello_misc",O_RDWR);
if(fd<0)
{
perror("open error");
return fd;
}
while(1)
{
read(fd,&value,sizeof(value));
printf("value is %d\n",value);
}
return 0;
}
实验结果: