// 在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. mui框架中底部导航的跳转1

    mui框架极大的方便了app的开发但是我们在做页面之间的切换时发现不能实现 a 链接的跳转,这是应为mui相关的一些控件是通过拦截a标签上的href来实现的,所以mui.js会阻止a标签上的href跳 ...

  2. bzoj4325: NOIP2015 斗地主(爆搜+模拟)

    去年的我还不会打斗地主呵呵 觉得这道题挺难的..抄了一遍题解,感触挺多的= = 首先出牌的方式太多了不能每次都枚举所有的出牌方式, 于是分成两部分:1.顺子 2.带牌等其他 每次dfs都搜顺子,而且顺 ...

  3. IntelliJ添加Emacs编辑器

    Intellij只支持emacs as a external tool: https://www.jetbrains.com/help/idea/2016.2/tutorial-using-emacs ...

  4. C语言内存分配

      (1)代码区(text segment).存放CPU执行的机器指令(machine instructions).通常,代码区是可共享的 (即另外的执行程序可以调用它),因为对于频繁被执行的程序,只 ...

  5. PYTHON之全局变量

    应该尽量避免使用全局变量.不同的模块都可以自由的访问全局变量,可能会导致全局变量的不可预知性.对全局变量,如果程序员甲修改了_a的值,程序员乙同时也要使用_a,这时可能导致程序中的错误.这种错误是很难 ...

  6. 6.数组和Hash表

    当显示多条结果时,存储在变量中非常智能,变量类型会自动转换为一个数组. 在下面的例子中,使用GetType()可以看到$a变量已经不是我们常见的string或int类型,而是Object类型,使用-i ...

  7. 使用java反射机制编写Student类并保存

    定义Student类 package org; public class Student { private String _name = null; ; ; public Student() { } ...

  8. Code First开发系列之管理数据库创建,填充种子数据以及LINQ操作详解

    返回<8天掌握EF的Code First开发>总目录 本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LINQ to ...

  9. Module Zero之Nuget包

    返回<Module Zero学习目录> ABP module-zero已经发布在了nuget上了.这里是所有的包列表. Abp.Zero module zero的核心包. Abp.Zero ...

  10. Html5绘制时钟

    最近在对Html5比较感兴趣,就用空闲时间做一些小例子进行练习,今天绘制一个走动的时钟,具体如下图所示: 具体思路在上图已有说明,代码如下: <script type="text/ja ...