2021-12-05 20:12:04 +08:00

150 lines
5.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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");