// 在Linux下的中断方式读取按键驱动程序
//包含外部中断 休眠 加入poll机制
// 采用异步通知的方式 // 驱动程序发 ---> app接收 (通过kill_fasync()发送)
// 为了使设备支持异步通知机制,驱动程序中涉及以下3项工作:
// 1. 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。
// 不过此项工作已由内核完成,设备驱动无须处理。
// 2. 支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。
// 驱动中应该实现fasync()函数。
// 3. 在设备资源可获得时,调用kill_fasync()函数激发相应的信号
// 应用程序:
// fcntl(fd, F_SETOWN, getpid()); // 告诉内核,发给谁 // Oflags = fcntl(fd, F_GETFL);
// fcntl(fd, F_SETFL, Oflags | FASYNC); // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_struct // 外部中断测试程序 包含poll机制 进程之间异步通信 加入原子操作
// 原子操作:指的是在执行过程中不会被别的代码路径所中断的操作。
// 信号量的实现
// 阻塞 :是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。
// 被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。 // 非阻塞:进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。 // 加入定时器消抖动功能 #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/poll.h> #define usingatomic (0) // 0使用信号量 1使用的是原子操作 //设备类
static struct class *Eint_class;
// 设备节点
static struct class_device *Eint_class_devs;
// 地址映射
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat; // 全局变量 存放中断读出的键值
static unsigned int key_val; //创建一个休眠队列
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
static volatile int ev_press = ; //信号量初始化结构体
static struct fasync_struct *button_async_queue; // 定时器结构体
struct timer_list buttons_timer; // 存储外部中断号和键值结构体变量
static struct pin_desc *irq_pd; //定义结构体 存放按键 pin 端口 key_val键值
struct pin_desc
{
unsigned int pin;
unsigned int key_val;
}; #if usingatomic
//定义原子变量v并初始化为1
atomic_t canopen = ATOMIC_INIT();
#else
//定义互斥锁 信号量
static DECLARE_MUTEX(button_lock);
#endif //定义结构体数组 存放中断端口和键值
struct pin_desc pins_desc[]={ {S3C2410_GPF0,0x01},
{S3C2410_GPF2,0x02},
{S3C2410_GPG3,0x03},
{S3C2410_GPG11,0x04}};
//中断服务程序
//读取键值
static irqreturn_t buttons_irq(int irq, void *ignored)
{
irq_pd = ( struct pin_desc *)ignored;
mod_timer(&buttons_timer, jiffies+HZ/); //10ms 产生中断
// return IRQ_RETVAL(IRQ_HANDLED);
return IRQ_HANDLED;
}
//定时器中断函数
static void buttons_timer_function(unsigned long data)
{
struct pin_desc *pins_desc= irq_pd;
unsigned int pinval; if(!pins_desc)
return; pinval=s3c2410_gpio_getpin(pins_desc->pin); //读取IO的值
if(pinval)
{
key_val =0x80|pins_desc->key_val;
}
else
{
key_val =pins_desc->key_val;
}
ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync (&button_async_queue, SIGIO, POLL_IN);//发送信号给app } //打开设备调用
//初始化IO端口 配置为输入模式
//GPF0-->S2 GPF2-->S3 GPG3-->S4 GPG11-->S5
static int Eint_drv_open(struct inode *inode, struct file *file)
{
// *gpfcon &=~((3<<2*0)|(3<<2*2));
// *gpgcon &=~((3<<3*2)|(3<<11*2));
#if usingatomic
if(!atomic_dec_and_test(&canopen))// 原子操作
{
atomic_inc(&canopen);//自加1
printk("this a user in the use of\n");
return -EBUSY;//返回忙
}
#else
if (file->f_flags & O_NONBLOCK)
{
//非阻塞 立马返回
if (down_trylock(&button_lock))
return -EBUSY;
}
else
{
down(&button_lock);
}
#endif
printk("Eint_drv_open successed!\n");
request_irq(IRQ_EINT0,buttons_irq, IRQT_BOTHEDGE, "s2", &pins_desc[]);//EINT0边沿触发方式
request_irq(IRQ_EINT2,buttons_irq, IRQT_BOTHEDGE, "s3", &pins_desc[]);//EINT2边沿触发方式
request_irq(IRQ_EINT11,buttons_irq, IRQT_BOTHEDGE, "s4", &pins_desc[]);//EINT11边沿触发方式
request_irq(IRQ_EINT19,buttons_irq, IRQT_BOTHEDGE, "s5", &pins_desc[]);//EINT19边沿触发方式 return ;
}
//write时候调用
static ssize_t Eint_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{ }
//read时候调用
//读取按键值
ssize_t Eint_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{ if(size != )
return -EINVAL;
if (file->f_flags & O_NONBLOCK)
{
//非阻塞 立马返回
if (!ev_press)
return -EAGAIN;
}
else
{
//如果没有按键按下 则休眠
wait_event_interruptible(button_waitq,ev_press);
}
//如果有按键动作发生的话 则返回
copy_to_user(buf, &key_val, );
ev_press = ;//清中断标志位
// printk("key_val = 0x%x\n", key_val); } //关闭驱动时候调用
static int Eint_drv_colse(struct inode *inode, struct file *file)
{
#if usingatomic
atomic_inc(&canopen);//自加1
#else
up(&button_lock);
#endif free_irq(IRQ_EINT0, &pins_desc[]);//EINT0释放中断
free_irq(IRQ_EINT2, &pins_desc[]);//EINT2释放中断
free_irq(IRQ_EINT11, &pins_desc[]);//EINT11释放中断
free_irq(IRQ_EINT19, &pins_desc[]);//EINT19释放中断
printk("Eint_drv_colse successed!\n");
} //poll时候调用
// 在规定时间内没有按下按键 就返回超时
//中断没有发生 就return 0,在do_sys_poll中就会让系统休眠,唤醒休眠是chedule_timeout(__timeou)超时
//中断发生 return POLLIN | POLLRDNORM,在do_sys_poll退出休眠,唤醒进程
static unsigned int Eint_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); //加入队列 不会立即休眠 if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
} //在应用程序中使用fcnt() 时候调用
static int Eint_drvl_fasync (int fd, struct file *filp, int on)
{
printk("\ndrivec:signal_fasync successed !\n");
return fasync_helper (fd, filp, on, &button_async_queue);
} //告诉内核
static struct file_operations Eint_drv_fops = {
.owner = THIS_MODULE, // 这是一个宏,推向编译模块时自动创建的__this_module变量
.open = Eint_drv_open,
.write = Eint_drv_write,
.read = Eint_drv_read,
.release= Eint_drv_colse,
.poll = Eint_drv_poll,
.fasync = Eint_drvl_fasync,
}; int major;//自动分配主设备号
//安装驱动的时候调用
//注册驱动 创建设备类 创建设备节点 创建虚拟地址 创建定时器任务
int Eint_drv_init(void)
{ // 创建一个定时器
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
add_timer(&buttons_timer); major=register_chrdev( , "key_drv",&Eint_drv_fops);//告诉内核 注册驱动 Eint_class = class_create(THIS_MODULE, "key_drv");//获取一个设备信息类
if (IS_ERR(Eint_class))
return PTR_ERR(Eint_class); Eint_class_devs = class_device_create(Eint_class, NULL, MKDEV(major, ), NULL, "buttons");
if (unlikely(IS_ERR(Eint_class_devs)))
return PTR_ERR(Eint_class_devs); //转换虚拟地址
gpfcon = (volatile unsigned long *)ioremap(0x56000050, );
gpfdat = gpfcon+; gpgcon =(volatile unsigned long *)ioremap(0x56000060,);
gpgdat =gpgcon+;
printk("Eint_drv_init successed!\n");
return ;
} //卸载驱动程序的时候调用
//卸载驱动 删除设备 删除设备节点 删除地址映射
void Eint_drv_exit(void)
{
unregister_chrdev( major, "key_drv");//卸载驱动
class_device_unregister(Eint_class_devs);//删除设备
class_destroy(Eint_class);//删除设备节点
iounmap(gpgcon);//删除地址映射
iounmap(gpfcon);
printk("\nEint_drv_exit successed!\n");
}
module_init(Eint_drv_init);
module_exit(Eint_drv_exit);
MODULE_LICENSE("GPL");

在Linux下的中断方式读取按键驱动程序的更多相关文章

  1. linux驱动之中断方式获取键值

    linux驱动之中断方式获取键值 ----------------------------------------------------------------------------------- ...

  2. linux下通过脚本方式对中间件weblogic进行补丁升级

    转至:http://bbs.learnfuture.com/topic/48 linux下通过脚本方式对中间件weblogic进行补丁升级 刘五奎 [摘要] 在运维行业,系统或软件漏洞每每牵动着每一个 ...

  3. Django项目部署在Linux下以进程方式启动

    Django项目部署在Linux下以进程方式启动 这是一篇关于如何在linux下,以后台进程的方式运行服务,命令改改基本上就通用了. 开发完Django项目后,需要把项目部署到linux环境下.当然, ...

  4. linux下c/c++方式访问curl的帮助手册

    自:http://blog.chinaunix.net/u1/47395/showart_1768832.html 有个业务需求需要通过curl 代理的方式来访问外网 百度了一把,测试可以正常使用.记 ...

  5. Linux下通用二进制方式安装MySQL

    1.下载glibc版本的MySQL: https://downloads.mysql.com/archives/community/ 2.查看mysql用户和mysql组是否存在(用户和组的信息存在/ ...

  6. Linux 下三种方式设置环境变量

    1.在Windows 系统下,很多软件安装都需要配置环境变量,比如 安装 jdk ,如果不配置环境变量,在非软件安装的目录下运行javac 命令,将会报告找不到文件,类似的错误. 2.那么什么是环境变 ...

  7. 【环境变量】Linux 下三种方式设置环境变量

    1.在Windows 系统下,很多软件安装都需要配置环境变量,比如 安装 jdk ,如果不配置环境变量,在非软件安装的目录下运行javac 命令,将会报告找不到文件,类似的错误. 2.那么什么是环境变 ...

  8. 【环境变量】Linux 下三种方式设置环境变量与获取环境变量

    1.在Windows 系统下,很多软件安装都需要配置环境变量,比如 安装 jdk ,如果不配置环境变量,在非软件安装的目录下运行javac 命令,将会报告找不到文件,类似的错误. 2.那么什么是环境变 ...

  9. Linux下基于源代码方式安装MySQL 5.6

    MySQL为开源数据库,因此能够基于源代码实现安装.基于源代码安装有很多其它的灵活性. 也就是说我们能够针对自己的硬件平台选用合适的编译器来优化编译后的二进制代码.依据不同的软件平台环境调整相关的编译 ...

随机推荐

  1. 常用类-StringBuffer

    总结:凡是要频繁的修改字符串内容的时候,都要使用StringBuffer类来完成,而且在StringBuffer类里提供了一些在String类中没有的,包括:delete(),insert()等等 / ...

  2. 吐槽scala

    scala可能是唯一一个编译器和IDE对代码有不同理解的语言.当你开始用scala的高级特性的时候,他们的分歧特别的大,以至于现在,intellij上的scala插件已经不敢对可能编译不通过的代码标记 ...

  3. 不能链接云服务器mysql

    如果报host'' 不允许连接MySQL服务器  1130 错误的话如果你的3306端口打开了,也把user 里的host改为% 还是不行的话,请记得更改 站点域名设置,和你服务器相同,端口一致.

  4. VB6.0 为批量字体改名

    从网上下载了一个字符包,解压以后查看,发现文件名是这种形式:0120_XXXXXX_GBK.ttf,看上去很不雅观.我想改成 XXXXXX简体.ttf 这种形式,但字体有300多个,手动修改太浪费时间 ...

  5. Disruptor-NET和内存栅栏

    Disruptor-NET算法(是一种无锁算法)需要我们自己实现某一种特定的内存操作的语义以保证算法的正确性.这时我们就需要显式的使用一些指令来控制内存操作指令的顺序以及其可见性定义.这种指令称为内存 ...

  6. nodejs事件轮询详述

    目录 概述 nodejs特点 事件轮询 关于异步方法 概述 关于nodejs的介绍网上资料非常多,最近由于在整理一些函数式编程的资料时,多次遇到nodejs有关的内容.所以就打算专门写一篇文章总结一下 ...

  7. 基于空项目模板创建使用Owin来host的WebApi项目

    首先创建一个空的web项目,如下图所示: 项目创建成功以后,安装下面三个package. Install-Package Microsoft.AspNet.WebApi -Version 5.2.2I ...

  8. Atitit  图像处理Depixelizing Pixel Art像素风格画的矢量化

    Atitit  图像处理Depixelizing Pixel Art像素风格画的矢量化 在去年的时候,偶然看到hqx算法. 一个高质量的插值放大算法. 与双线性插值等插值算法相比,这个算法放大后对人眼 ...

  9. WCF学习之旅—WCF概述(四)

    一.WCF概述 1) 什么是WCF? Windows Communication Foundation (WCF) 是用于构建面向服务的应用程序的框架.借助 WCF,可以将数据作为异步消息从一个服务终 ...

  10. Spring学习记录(九)---通过工厂方法配置bean

    1. 使用静态工厂方法创建Bean,用到一个工厂类 例子:一个Car类,有brand和price属性. package com.guigu.spring.factory; public class C ...