目录
中断申请函数:
中断释放函数:
休眠等待宏:
休眠唤醒宏
实例代码:
程序难点分析:
中断申请函数:
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)/*
* @irq: 中断号
* @handler: 中断服务函数
* @irqflags: 触发方式
* @devname: 中断的设备名称:自己随便定义
* @dev_id: 到时候传递给中断服务函数(handler)的一个指针
*/
中断释放函数:
void free_irq(unsigned int irq, void *dev_id)
/*
* @irq: 中断号
* @dev_id: 和上面的相同
*/
休眠等待宏:
#define wait_event_interruptible(wq, condition)
@wq : 在驱动函数中自己定义的全局变量 (button_waitq) static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
@condition : 状态标志,为0表示没有发生事件,进程休眠等待,为1表示事件发生执行休眠的进程,自己定义:static volatile int ev_press = 0;
注意:编写驱动的时候把标志置1 或者清零
休眠唤醒宏
#define wake_up_interruptible(x)
@x 同上面的wq button_waitq static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
实例代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/device.h>
#include <linux/interrupt.h>
//编写任务:
//1、中断的申请和注销
//2、中断服务函数的编写 判断按键时放下还是松开
//3、返回键值
//3、中断的唤醒和睡眠
#define ledmajor 250
static struct class *button_cls ;
static struct device *button_dev;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static unsigned char key_val ; //记下键值
static volatile int ev_press = 0; //这是一个状态标志 为真时,休眠进程继续执行
struct pin_desc{
unsigned int pin;
unsigned char key_val;
};
struct pin_desc pins_desc[4] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
};
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc *pindesc = (struct pin_desc*)dev_id;
unsigned int pinval;
pinval = s3c2410_gpio_getpin(pindesc->pin);
if(pinval)
{
key_val = 0x80|pindesc->key_val; //松开按键
}
else
{
key_val = pindesc->key_val; //按下按键
}
// printk("key_val = %d",key_val);
ev_press = 1; //表示有中断发生 标志为1
wake_up_interruptible(&button_waitq); //唤醒休眠的进程
return IRQ_RETVAL(IRQ_HANDLED);
}
ssize_t led_read (struct file *filp, char __user *buf , size_t count, loff_t *fops)
{
int ret;
printk("read entry\n");
wait_event_interruptible(button_waitq, ev_press); //等待中断事件发生
ret = copy_to_user(buf, &key_val, count);
if(ret > 0)
{
printk("read filed\n");
return -EFAULT;
}
ev_press = 0; //执行完毕 清楚事件标志
printk("read leave\n");
return 0 ;
}
ssize_t led_write (struct file *filp, const char __user * buf, size_t count, loff_t *fops)
{
int value ;
int ret ;
ret = copy_from_user(&value, buf, count);
if(ret > 0)
{
printk("write filed!\n");
return -EFAULT;
}
return 0;
}
int led_open (struct inode *inod, struct file *filp)
{
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "k0", &pins_desc[0]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "k1", &pins_desc[1]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "k2", &pins_desc[2]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "k3", &pins_desc[3]); //资源的申请 出发方式:双边沿触发
printk("open ok\n");
return 0;
}
int led_close(struct inode *inod, struct file *filp)
{
// printk("close ok\n");
free_irq(IRQ_EINT0, &pins_desc[0]);
free_irq(IRQ_EINT2, &pins_desc[1]);
free_irq(IRQ_EINT11, &pins_desc[2]);
free_irq(IRQ_EINT19, &pins_desc[3]); //释放中断资源
printk("close ok\n");
return 0;
}
const struct file_operations button_fops = {
.owner = THIS_MODULE,
.open = led_open ,
.read = led_read ,
.write = led_write ,
.release = led_close ,
};
static int __init button_drv_init(void)
{
int ret;
ret = register_chrdev(ledmajor , "button_test" , &button_fops) ; //注册设备号
if (ret == 0)
{
printk("register ok!\n" );
}
else
{
printk("register filed!\n");
return -1;
}
button_cls = class_create( THIS_MODULE , "button_class");
button_dev = device_create(button_cls , NULL , MKDEV(250 , 0), "button_device") ; //创建设备节点
return 0;
}
static void __exit button_drv_exit(void)
{
device_destroy(button_cls, MKDEV(250 , 0)); //注意三者的顺序
class_destroy(button_cls);
unregister_chrdev(ledmajor , "button_test") ; //注销设备号
}
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
程序难点分析:
首先,应用程序read 按键值,执行wait_event_interruptible ,查询标志ev_press,没有事件程序开始休眠,
当有按键按下,执行 buttons_irq ,
把标志ev_press置1,表明有事件发生,
执行wake_up_interruptible(&button_waitq),唤醒进程
然后回到read刚开始的断点处,查询标志ev_press有事件发生,事件被唤醒,
退出wait_event_interruptible,
继续往下执行,在read函数最后把标志位ev_press置0