#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/sched.h>//error: ‘TASK_INTERRUPTIBLE‘ undeclared
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/poll.h>
#include <asm/gpio.h>
#include <asm/atomic.h>
#include "iomux.h"
#include "mx25.h"
#include "mxc.h"
#include "mx25_pins.h"
#include "irqs.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("beixiaobin62@163.com");
MODULE_DESCRIPTION("button kernel module");
MODULE_VERSION("V2.0.0");
#define GPIO2_21 MX25_PIN_CLKO
static int major;
static struct cdev btn_cdev;
static struct class *cls;
//分配等待队列头
static wait_queue_head_t btn_wq;
static int ispress; //=1,按键有操作;=0,按键无操作
static unsigned char key_val; //上报按键的值
//btn_read函数实现
static ssize_t btn_read(struct file *file,
char __user *buf, //对应用户的第二个参数
size_t count, //对应用户第三个参数
loff_t *ppos //文件指针
)
{
int reval;
/*0.判断是否采用非阻塞方式*/
if(file->f_flags & O_NONBLOCK)
{
//非阻塞方式
if(!ispress) //判断按键是否有操作,如果没有立即返回
{
printk("No Data\n");
return -EAGAIN; //再次读
}
}
//阻塞方式
/*1.判断按键是否有操作,通过ippress来判断,如果有操作,ispress=1*/
//这个函数立即返回,如果ispress=0,没有操作,进程进入休眠状态。
//一旦被唤醒,就可以读取按键值给用户
printk("Process:%d(%s) to Sleeping ...\n",
current->pid, current->comm);
/*2.进入可中断的休眠*/
reval = wait_event_interruptible(btn_wq, ispress);
if(reval)
{
/*信号唤醒的睡眠*/
printk("RECV SIGNAL!\n");
return -ERESTARTSYS;//返回用户空间的read
}
else
{
ispress = 0;
printk("Process:%d(%s) Wake UP!\n",
current->pid, current->comm);
/*2.一旦被唤醒,上报按键数据*/
copy_to_user(buf, &key_val, sizeof(key_val));
}
return count;
}
//监听功能
static unsigned int btn_poll(struct file *file,
struct poll_table_struct *wait)
{
unsigned int mask = 0;
//1.将进程添加到等待队列头所在的数据链中,切记进程并没有进入真正的休眠
poll_wait(file, &btn_wq, wait);
//2.判断按键键值是否可用,如果可用,返回非0,否则返回0
if(ispress){ //按键有操作
mask = POLLIN|POLLRDNORM; //设备数据可读
}
return mask;
}
static struct file_operations btn_fops = {
.owner = THIS_MODULE,
.read = btn_read, //获取按键接口
.poll = btn_poll //被监听功能接口
};
static irqreturn_t button_isr(int irq, void *dev_id)
{
unsigned int pinstatus;
/*1.获取按键对应的状态*/
pinstatus = gpio_get_value(IOMUX_TO_GPIO(GPIO2_21));
/*2.获取按键对应的状态,设置要上报的按键值*/
if(pinstatus == 1)//松开
key_val = 0x50;
else if(pinstatus == 0)//按下
key_val = 0x51;
/*3.唤醒休眠进程*/
ispress = 1; //按键有操作,按键数据可用
wake_up_interruptible(&btn_wq);
return IRQ_HANDLED;
}
static int btn_init(void)
{
dev_t dev;
//1.申请设备号
alloc_chrdev_region(&dev, 0, 1, "button");
major = MAJOR(dev);
//2.初始化注册cdev
cdev_init(&btn_cdev, &btn_fops);
cdev_add(&btn_cdev, dev, 1);
//3.自动创建设备节点是/dev/mybutton
cls = class_create(THIS_MODULE,"BUTTON");
device_create(cls, NULL, dev, NULL, "mybutton");
/*4.向内核申请为GPIO功能*/
mxc_request_iomux(GPIO2_21, MUX_CONFIG_ALT5);
/*5.申请GPIO资源*/
gpio_request(IOMUX_TO_GPIO(GPIO2_21), "BUTTON");
/*6.设置输入脚各项参数*/
mxc_iomux_set_pad(GPIO2_21, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
/*7.注册中断*/
request_irq(IOMUX_TO_IRQ(GPIO2_21), button_isr, IRQF_TRIGGER_FALLING |
IRQF_TRIGGER_RISING, "KEY", NULL);
/*8.初始化等待队列*/
init_waitqueue_head(&btn_wq);
return 0;
}
static void btn_exit(void)
{
dev_t dev = MKDEV(major, 0);
/*1.释放中断,注意注册中断传递的参数和释放中断传递的参数必须一致!!!*/
printk("unregister irq!\n");
free_irq(IOMUX_TO_IRQ(GPIO2_21), NULL);
/*2.释放GPIO资源*/
gpio_free(IOMUX_TO_GPIO(GPIO2_21));
/*3.取消GPIO功能*/
mxc_free_iomux(GPIO2_21, MUX_CONFIG_ALT5);
//4.删除设备节点
device_destroy(cls, dev);
class_destroy(cls);
//5.卸载cdev
cdev_del(&btn_cdev);
//6.释放设备号
unregister_chrdev_region(dev, 1);
printk("unregister irq!\n");
}
module_init(btn_init);
module_exit(btn_exit); |