目錄
- 中斷上下文
- 中斷下半部的實作方法
-
- 1.tasklet的實作
- 2.工作隊列workqueue的實作
中斷上下文
************
中斷下半部的實作方法
1.中斷下半部的實作方法:
- softirq:處理比較快,但是核心級别的機制,需要修改整個核心源碼,不常用
- tasklet:内部實際調用了softirq
- work queue:工作隊列,使用方法類似tasklet
2.tasklet和work queue實作的基本邏輯
1.tasklet的實作
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long); //中斷下半部執行函數
unsigned long data;
};
【1】初始化:
//初始化tasklet
tasklet_init(&key_dev->mytasklet, key_tasklet_half_irq, 45);
【2】在上半部放到核心線程中--------->啟動下半部
//啟動下半部
tasklet_schedule(&key_dev->mytasklet);
【3】子產品解除安裝時
按鍵驅動的完整代碼:(包含四種IO模型接口)
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/interrupt.h>
#include <mach/irqs.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#define GPH0DATA 0xE0200C04 //GHH0_2 (eint2data寄存器的實體位址)
#define KEY_ENTER 28
//描述按鍵資料的對象
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;
struct key_event event;
wait_queue_head_t wq_head;
int key_state; //表示是否有資料
struct fasync_struct * fasync;
struct tasklet_struct mytasklet;
};
struct key_desc* key_dev;
void key_tasklet_half_irq(unsigned long data)
{
printk("-----%s----\n",__FUNCTION__);
//表示有資料,需要去喚醒等待隊列,同時設定标志位
wake_up_interruptible(&key_dev->wq_head);
key_dev->key_state = 1;
//發送信号
kill_fasync(&key_dev->fasync, SIGIO, POLLIN);
}
irqreturn_t key_irq_handler (int irq, void * dev_id)
{
printk("------%s----\n",__FUNCTION__);
int value = readl(key_dev->reg_base) & (1<<2);
if(value)
{
//按鍵擡起
printk("key up\n");
key_dev->event.code = KEY_ENTER;
key_dev->event.value = 0;
}
else
{
//按鍵按下
printk("key press\n");
key_dev->event.code = KEY_ENTER;
key_dev->event.value = 1;
}
//啟動下半部
tasklet_schedule(&key_dev->mytasklet);
return IRQ_HANDLED;
}
ssize_t key_drv_read (struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
int ret;
//如果目前是非阻塞模式,并且沒有資料,立馬傳回一個錯誤碼
if(filp->f_flags & O_NONBLOCK && !key_dev->key_state)
return -EAGAIN;
//在等待資料的時候進行休眠
wait_event_interruptible(key_dev->wq_head, key_dev->key_state);
//表示有資料了
ret = copy_to_user(buf, &key_dev->event, count);
if(ret > 0)
{
printk("copy_to_user error.\n");
return -EFAULT;
}
//傳遞給使用者之後将資料清除
memset(&key_dev->event, 0, sizeof(key_dev->event));
//資料傳遞完成把标志位置回0
key_dev->key_state = 0;
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;
}
unsigned int key_dev_poll (struct file *filp, struct poll_table_struct *pts)
{
printk("----%s----\n",__FUNCTION__);
//傳回一個mask值
unsigned int mask;
//調用poll_wait将目前等待隊列注冊到系統中
poll_wait(filp, &key_dev->wq_head,pts);
// 1,沒有資料的時候傳回一個0
if(!key_dev->key_state)
{
mask = 0;
}
// 2,當有資料的時候傳回一個POLLIN
if(key_dev->key_state)
{
mask |= POLLIN;
}
return mask;
}
int key_dev_fasync (int fd, struct file *filp, int on)
{
//隻需要調用一個函數記錄信号該發送給誰
return fasync_helper(fd, filp,on, &key_dev->fasync);
}
const struct file_operations key_fops = {
.open = key_drv_open,
.read = key_drv_read,
.write = key_drv_write,
.release = key_drv_close,
.poll = key_dev_poll,
.fasync = key_dev_fasync,
};
static int __init key_drv_init(void)
{
int ret;
// 1,設定一個全局的裝置對象
key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);
// 2,申請主裝置号
key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);
// 3,建立裝置結點
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");
// 4,硬體初始化:--位址映射和中斷申請
key_dev->irqno = IRQ_EINT2;
ret = request_irq(key_dev->irqno, key_irq_handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key5_eint2", NULL);
if(ret != 0)
{
printk("request_irq error.\n");
}
//5,硬體如何擷取資料---讀取gph0data寄存器
key_dev->reg_base = ioremap(GPH0DATA, 8);
//6,初始化等待隊列頭
init_waitqueue_head(&key_dev->wq_head);
//7,初始化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);
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
2.工作隊列workqueue的實作
使用原理和tasklet類似:(具體代碼在這裡就不再累述)
struct work_struct:
typedef void (*work_func_t)(struct work_struct *work);
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
};
【1】初始化:
//初始化workqueue
INIT_WORK(struct work_struct* work,work_func_t func);
【2】在上半部放到核心線程中--------->啟動下半部
//啟動下半部
schedule_work(struct work_struct* work);