多功能按键驱动
该驱动是基于ARM9硬件平台,仅供自己学习和记录。该驱动包含以下功能:
- 响应K1和K4按键中断
- 中断分层处理
- 使用内核处理队列
- 按键防抖动
- 使用定时器
- 阻塞功能
响应K1和K4按键中断
按键k1,k4属于字符设备,主设备号为10的字符设备称为“混杂设备”。混杂设备的中断处理包括1.混杂设备文件初始化。2.注册混杂设备3.注册中断 3.硬件初始化 4 。中断处理函数编写 5.注销中断
中断分层处理–中断嵌套
系统进行响应中断的时候,如果同一时刻到达相同类型的中断,后到达的往往会被舍弃。这显然不是我们希望见到的。可采取这样一种办法。减少中断处理的时间,越短越好。在中断处理函数中,只处理和硬件有关的工作。其余的工作交给系统工作队列。系统会在系统合适的时间去处理这一项工作。
中断嵌套有三种方式1.软中断2.tasklet3.工作队列。中断处理函数是运行在中断上下文的,他的行为会受到一些限制。如不能使用可能引起阻塞的函数、不是使用可能引起调度的函数。
中断上半部分:当中断发生时,进行相应的寄存器读写。并登记该中断。
中断上半部分:在系统空闲时 对上半部登记的中断进行后续处理。
使用内核处理队列
工作队列是将一种任务推后执行的形式,他推后的任务由一个内核线程完成。这样下半部会在进程上下文执行。每个被推送的任务叫做“工作”从而组成工作队列。struct work_struct 结构
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;//工作队列对应的执行函数
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
包括创建队列create_workqueue(一般不需要 使用系统自带队列)2.创建工作 INIT_WORK()3.提交工作 queue_work()如果提交给系统schedule_work
定时器
定时器的结构为struct timer_list key_timer;
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires;
struct tvec_base *base;
void (*function)(unsigned long);//定时器定时时间到的超时函数
unsigned long data;
int slack;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
使用流程包括1.定义定时器变量2.初始化定时器 3。注册定时器add_timer4.启动定时器mod_timer
阻塞功能
举个例子 去读某一位置,这个位置数据还没到,我们就去读,显然这是读不到的 但是并不代表永远都没有数据,为了读到数据,我们应将其变成阻塞的。将其设置成阻塞功能包括如下几个部分:
1.定义等待队列
wait_queue_head_t my_queue;
2.初始化等待队列
init_waitqueue_head (&my_queue);
3.进入等待序列
wait_enent(queue,condition)
condition为真时立即返回。否则进程进入TASK_UNINTERRUPTIBLE模式进入睡眠, 并挂载在等待队列queue上。
wait_enent_interruptible(queue,condition)
condition为真时立即返回。否则进程进入TASK_INTERRUPTIBLE模式进入睡眠 ,并挂载在等待队列queue上。
4.从等待队列中唤醒
wake_up(q);唤醒TASK_UNINTERRUPTIBLE、TASK_INTERRUPTIBLE、TASK_KILLABLE的所有进程。
wake_up_interruptible(q);唤醒状态为TASK_INTERRUPTIBLE的所有进程。
key.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
MODULE_LICENSE("GPL");
#define GPFCON 0X56000050
#define GPFDAT 0X56000054
struct work_struct *work1;//工作
struct timer_list key_timer;//定时器结构
unsigned int * gpio_dat = NULL;
unsigned int key_no = 0;;
wait_queue_head_t key_q;//内核等待队列
void work1_fun()
{
//jiffies为当前时间。1HZ 对应的时间为1s
//启动定时器
mod_timer(&key_timer, jiffies + HZ/10 );
}
irqreturn_t key_int(int irq, void *dev_id)
{
//检测是否发生按键中断
//清除按键中断
//打印按键值
//将工作交给系统队列
schedule_work(work1);
return 0;
}
void key_hw_init()
{
unsigned int *gpio_config;
unsigned short data;
printk("initial success\n");
gpio_config = ioremap(GPFCON, 4);
data = readw(gpio_config);
data &= ~0b1111;
data |= 0b1010;
writew(data, gpio_config);
//物理地址转化为虚拟地址
gpio_dat = ioremap(GPFDAT, 4);
}
int key_open(struct inode * inode, struct file *filep)
{
return 0;
}
long key_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
return 0;
}
ssize_t key_read(struct file *filep, char __user *buf, size_t size, loff_t *whence)
{
//初始化的key_no为0,key_no为真的是好wait_event立即返回
wait_event(key_q, key_no);
printk("in kernel num is %d\n", key_no);
copy_to_user(buf, &key_no, 4);
key_no = 0;
return 0;
}
static struct file_operations key_fops =
{
.open = key_open,
.read = key_read,
.unlocked_ioctl = key_ioctl,
};
static struct miscdevice key_miscdev =
{
.minor = 200,
.name = "key",
.fops = &key_fops,
};
void key_timer_fuc(unsigned long num)
{
unsigned int key_value = 0;
key_value = readw(gpio_dat) & 0x03;
if (key_value == 2)
{
printk("KEY_4 interrupt\n");
key_no = 4;
}
if (key_value == 1)
{
printk("KEY_1 interrupt\n");
key_no = 1;
}
//唤醒等待进程
wake_up(&key_q);
}
static int key1_init()
{
int n = 3;
//注册混杂设备
misc_register(&key_miscdev);
//注册中断
n = request_irq(IRQ_EINT0, key_int, IRQF_TRIGGER_FALLING, "key", 0);
request_irq(IRQ_EINT1, key_int, IRQF_TRIGGER_FALLING, "key", 0);
printk("n=%d",n);
//按键初始化
key_hw_init();
work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work1 ,work1_fun);
//定时器初始化
init_timer(&key_timer);
key_timer.function = key_timer_fuc;
add_timer(&key_timer);
init_waitqueue_head(&key_q);
return 0;
}
static void key1_exit()
{
//注销混杂设备
misc_deregister(&key_miscdev);
//注销中断
free_irq(IRQ_EINT0, 0);
}
module_init(key1_init);
module_exit(key1_exit);
key_app.c
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd = 0;
int key_num = 0;
fd = open("/dev/2440key", 0);
read(fd, &key_num, 4);
printf("key is %d",key_num);
return 0;
}
Makefile
obj-m := key.o
KDIR := /lesson1/linux-tq2440
all:
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
rm -f *.ko *.mod.o *.mod.c *.symvers *.bak *.order *.o