150 lines
5.0 KiB
C
150 lines
5.0 KiB
C
#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"); |