150 lines
5.0 KiB
C
Raw Normal View History

2021-12-05 20:12:04 +08:00
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h> //含有iomap函数iounmap函数
#include <asm/uaccess.h> //含有copy_from_user函数
#include <linux/device.h> //含有类相关的处理函数
#include <linux/irq.h> //含有IRQ_HANDLED和IRQ_TYPE_EDGE_RISING
#include <linux/interrupt.h> //含有request_irq、free_irq函数
#include <linux/cdev.h>
static dev_t btn_cdev_num; //定义一个设备号
static struct cdev *btn_cdev; //定义一个设备管理结构体指针
static struct class *btn_class; //定义一个设备类
static struct device *btn; //定义一个设备
#define KEYADC_BASE (0x01C22800)
#define KEYADC_CTRL_REG (KEYADC_BASE+0x00)
#define KEYADC_INTC_REG (KEYADC_BASE+0x04)
#define KEYADC_INTS_REG (KEYADC_BASE+0x08)
#define KEYADC_DATA_REG (KEYADC_BASE+0x0C)
ssize_t volatile *keyadc_ctrl;
ssize_t volatile *keyadc_intc;
ssize_t volatile *keyadc_ints;
ssize_t volatile *keyadc_data;
static unsigned int ev_press; //一个全局变量,记录中断事件状态
DECLARE_WAIT_QUEUE_HEAD(btn_waitq);//注册一个等待队列button_waitq用宏来申明一个全局变量
static unsigned int key_value = 0; //定义一个变量保存按键值
static irqreturn_t btn_irq(int irq, void *dev_id)
{
wake_up_interruptible(&btn_waitq); /* 唤醒休眠的进程即调用read函数的进程 */
ev_press = 1;
*keyadc_ints |= ((1<<0)|(1<<1) | (1<<2) |(1<<3)|(1<<4));
return IRQ_HANDLED;
}
static int btn_cdev_open (struct inode * inode, struct file * file)
{
*keyadc_intc |= ((1<<0)|(1<<1)|(1<<4));
*keyadc_ctrl |= (1<<7);
*keyadc_ctrl &= ~(0xf<<8);
*keyadc_ctrl |= (1<<0);
return 0;
}
static int btn_cdev_close(struct inode * inode, struct file * file)
{
return 0;
}
static ssize_t btn_cdev_read(struct file * file, char __user * userbuf, size_t count, loff_t * off)
{
int ret;
unsigned int adc_value;
adc_value = *(keyadc_data);
//将当前进程放入等待队列button_waitq中,并且释放CPU进入睡眠状态
wait_event_interruptible(btn_waitq, ev_press!=0);
ret = copy_to_user(userbuf, &adc_value, 4);//将取得的按键值传给上层应用
printk(KERN_WARNING"key adc = %d\n",(adc_value&(0x3f)));
printk(KERN_WARNING"key statue = 0x%x\n",*(keyadc_ints));
ev_press = 0;//按键已经处理可以继续睡眠
if(ret)
{
printk("copy error\n");
return -1;
}
return 1;
}
static struct file_operations btn_cdev_ops = {
.owner = THIS_MODULE,
.open = btn_cdev_open,
.read = btn_cdev_read,
.release = btn_cdev_close,
};
static int btn_cdev_init(void)
{
int ret;
btn_cdev = cdev_alloc(); //动态申请一个设备结构体
if(btn_cdev == NULL)
{
printk(KERN_WARNING"cdev_alloc failed!\n");
return -1;
}
ret = alloc_chrdev_region(&btn_cdev_num,0,1,"button"); //动态申请一个设备号
if(ret !=0)
{
printk(KERN_WARNING"alloc_chrdev_region failed!\n");
return -1;
}
btn_cdev->owner = THIS_MODULE; //初始化设备管理结构体的owner为THIS_MODULE
btn_cdev->ops = &btn_cdev_ops; //初始化设备操作函数指针为led_ops函数
cdev_add(btn_cdev,btn_cdev_num,1); //将设备添加到内核中
btn_class = class_create(THIS_MODULE, "button"); //创建一个名为led_class的类
if(btn_class == NULL)
{
printk(KERN_WARNING"btn_class failed!\n");
return -1;
}
btn = device_create(btn_class,NULL,btn_cdev_num,NULL,"button0"); //创建一个设备名为led0
if(IS_ERR(btn))
{
printk(KERN_WARNING"device_create failed!\n");
return -1;
}
keyadc_ctrl = ioremap(KEYADC_CTRL_REG, 4);//重映射
keyadc_intc = ioremap(KEYADC_INTC_REG, 4);//重映射
keyadc_ints = ioremap(KEYADC_INTS_REG, 4);//重映射
keyadc_data = ioremap(KEYADC_DATA_REG, 4);//重映射
ret = request_any_context_irq(35, btn_irq, 0 , "btn",(void*)&key_value);
if(ret) //返回不为0表示申请失败
{
printk("request irq failed!\n");
return -1;
}
return 0;
}
static void btn_cdev_exit(void)
{
cdev_del(btn_cdev); //从内核中删除设备管理结构体
unregister_chrdev_region(btn_cdev_num,1); //注销设备号
device_destroy(btn_class,btn_cdev_num); //删除设备节点
class_destroy(btn_class); //删除设备类
iounmap(keyadc_ctrl);//取消重映射
iounmap(keyadc_intc);//取消重映射
iounmap(keyadc_ints);//取消重映射
iounmap(keyadc_data);//取消重映射
free_irq(35,(void*)&key_value);//释放中断
printk("unregister btn_cdev\n");
}
module_init(btn_cdev_init);
module_exit(btn_cdev_exit);
MODULE_LICENSE("GPL");