// 在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. 一鼓作气 博客--第八篇 note8

    0.,222] list[33] except IndexError as e : print('index error ') except ValueError as e : print('valu ...

  2. HDFS 异常处理与恢复

    在前面的文章 <HDFS DataNode 设计实现解析>中我们对文件操作进行了描述,但并未展开讲述其中涉及的异常错误处理与恢复机制.本文将深入探讨 HDFS 文件操作涉及的错误处理与恢复 ...

  3. 标准数据源访问库 - JayData

    JayData 是一个标准的.跨平台的库和方法,用于访问和操作各种不同的数据源,最适合用于 JavaScript 和 HTML5 应用. 官方网站:http://jaydata.org/  ASP.N ...

  4. 【VC++技术杂谈008】使用zlib解压zip压缩文件

    最近因为项目的需要,要对zip压缩文件进行批量解压.在网上查阅了相关的资料后,最终使用zlib开源库实现了该功能.本文将对zlib开源库进行简单介绍,并给出一个使用zlib开源库对zip压缩文件进行解 ...

  5. 《Entity Framework 6 Recipes》中文翻译系列 (31) ------ 第六章 继承与建模高级应用之自引用关联

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-4  使用TPH建模自引用关系 问题 你有一张自引用的表,它代表数据库上不同类型 ...

  6. Linux study

    在centos5.5中编译LNMP环境 一.配置好ip, dns, 网关, 确保使用远程连接工具能够连接服务器 centos设置ip地址,网关, dns教程: http://www.osyumwei. ...

  7. Python virtualenv with Sublime Text 3

    背景介绍 最近喜欢上了Sublime编辑器,刚开始学着用.不过对我这个python狂人来讲,首要问题是需要sublime支持python virtualenv包的导入.所以我就找了google最后找到 ...

  8. js事件(event)的运行原理

    昨天写click事件时候突然脑袋抽筋想了想浏览器是怎么执行click事件的,为什么我们可以用e或者window.event这个对象获取一些事件的属性呐?以下是我的理解.如果您有更好的理解,欢迎评论!! ...

  9. iOS-App上架流程

    前言:作为一名IOS开发者,把开发出来的App上传到App Store是必须的.下面就来详细介绍下具体流程. 1.打开苹果开发者中心:https://developer.apple.com 打开后点击 ...

  10. 巧用Ajax的beforeSend 提高用户体验

    jQuery是经常使用的一个开源js框架,其中的$.ajax请求中有一个beforeSend方法,用于在向服务器发送请求前执行一些动作.具体可参考jQuery官方文档:http://api.jquer ...