Input_key(按键驱动)

Posted panda-w

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Input_key(按键驱动)相关的知识,希望对你有一定的参考价值。

 

 


 

button_drv.c


//头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/sched.h>

 

#include <asm/io.h>
#include <asm/string.h>
#include <asm/uaccess.h>
#include <asm-generic/ioctl.h>

#define LED_NUM_ON _IOW(‘L‘,0x1122,int)
#define LED_NUM_OFF _IOW(‘L‘,0x3344,int)
#define LED_ALL_ON _IO(‘L‘,0x1234)
#define LED_ALL_OFF _IO(‘L‘,0x5678)

//定义一个按键的数据包
struct button_event{
int code; //按键的名称---键值:KEY_DOWN
int value; //按键的状态---按下:1,松开:0
};


//面向对象编程----设计设备的类型
struct s5pv210_button{
//unsigned int major;
dev_t devno;
struct class * cls;
struct device * dev;
struct cdev *cdev;
unsigned int irqno;
struct button_event event;
wait_queue_head_t wq_head;

int have_data; //表示当前是否有数据可读,可读--1,不可读--0
};
struct s5pv210_button *button_dev;

//实现中断处理函数--------当触发中断时会被执行
irqreturn_t button_irq_svc(int irqno, void *dev)
{
int value;
printk("--------^_^ %s------------\n",__FUNCTION__);
//获取按键信息
value = gpio_get_value(S5PV210_GPH0(1));
//判断是按下还是松开
if(value){
//松开
printk("kenel:keydown up!\n");
button_dev->event.code = KEY_DOWN;
button_dev->event.value = 0;
}else{
//按下
printk("kenel:keydown pressed!\n");
button_dev->event.code = KEY_DOWN;
button_dev->event.value = 1;
}

//此时有数据可读
button_dev->have_data = 1;

//从等待队列中唤醒阻塞的进程
wake_up_interruptible(&button_dev->wq_head);

return IRQ_HANDLED;
}

//实现设备操作接口
int button_open(struct inode *inode, struct file *filp)
{

printk("--------^_^ %s------------\n",__FUNCTION__);

return 0;
}
ssize_t button_read(struct file *filp , char __user *buf , size_t size, loff_t *flags)
{
int ret;
printk("--------^_^ %s------------\n",__FUNCTION__);
//判读open时,有没有设置flags为NONBLOCK
if(filp->f_flags & O_NONBLOCK && !button_dev->have_data)
return -EAGAIN;

//判断此时是否有数据可读
wait_event_interruptible(button_dev->wq_head,button_dev->have_data);

//将内核数据转换为用户空间数据
ret = copy_to_user(buf,&button_dev->event,size);
if(ret > 0){
printk("copy_to_user error!\n");
return -EFAULT;
}

//将数据返回给应用空间后,清空数据包,同时将hava_data置零
memset(&button_dev->event,0,sizeof(button_dev->event));
button_dev->have_data = 0;
return size;
}

ssize_t button_write(struct file *filp, const char __user *buf, size_t size, loff_t *flags)
{

printk("--------^_^ %s------------\n",__FUNCTION__);

return size;
}

long button_ioctl(struct file *filp, unsigned int cmd , unsigned long args)
{

printk("--------^_^ %s------------\n",__FUNCTION__);

return 0;
}

int button_close(struct inode *inode, struct file *filp)
{
printk("--------^_^ %s------------\n",__FUNCTION__);

return 0;
}


static struct file_operations fops = {
.open = button_open,
.read = button_read,
.write = button_write,
.unlocked_ioctl = button_ioctl,
.release = button_close,
};


//加载函数和卸载函数
static int __init button_init(void) //加载函数-----在驱动被加载时执行
{
int ret;
printk("--------^_^ %s------------\n",__FUNCTION__);
//0,实例化设备对象
//参数1 ---- 要申请的空间的大小
//参数2 ---- 申请的空间的标识
button_dev = kzalloc(sizeof(struct s5pv210_button),GFP_KERNEL);
if(IS_ERR(button_dev)){
printk("kzalloc error!\n");
ret = PTR_ERR(button_dev);
return -ENOMEM;
}

//1,申请设备号-----新方法
#if 0
//静态申请设备号
button_dev->major = 256;
ret = register_chrdev_region(MKDEV(button_dev->major,0),1,"button_drv");
if(ret < 0){
printk("register_chrdev_region error!\n");
ret = -EINVAL;
goto err_kfree;
}
#else
//动态申请设备号
ret = alloc_chrdev_region(&button_dev->devno,0,1,"button_drv");
if(ret < 0){
printk("register_chrdev_region error!\n");
ret = -EINVAL;
goto err_kfree;
}
#endif

//创建cdev

//申请cdev的空间
button_dev->cdev = cdev_alloc();
if(IS_ERR(button_dev->cdev)){
printk("button_dev->cdev error!\n");
ret = PTR_ERR(button_dev->cdev);
goto err_unregister;
}

//初始化cdev的成员
cdev_init(button_dev->cdev,&fops);

//将cdev加入到内核中----链表
ret = cdev_add(button_dev->cdev,button_dev->devno,1);



//2,创建设备文件-----/dev/button
button_dev->cls = class_create(THIS_MODULE,"button_cls");
if(IS_ERR(button_dev->cls)){
printk("class_create error!\n");
ret = PTR_ERR(button_dev->cls);
goto err_cdev_del;
}

button_dev->dev = device_create(button_dev->cls,NULL,button_dev->devno,NULL,"button");
if(IS_ERR(button_dev->dev)){
printk("device_create error!\n");
ret = PTR_ERR(button_dev->dev);
goto err_class;
}


//3,硬件初始化---申请中断
button_dev->irqno = IRQ_EINT(1);
ret = request_irq(button_dev->irqno,button_irq_svc,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,"eint-keydown",NULL);
if(ret != 0){
printk("request_irq error!\n");
ret = -EBUSY;
goto err_device;
}

//初始化等待队列头
init_waitqueue_head(&button_dev->wq_head);

return 0;

err_device:
device_destroy(button_dev->cls,button_dev->devno);
err_class:
class_destroy(button_dev->cls);

err_cdev_del:
cdev_del(button_dev->cdev);

err_unregister:
unregister_chrdev_region(button_dev->devno,1);

err_kfree:
kfree(button_dev);
return ret;


}

static void __exit button_exit(void) //卸载函数-----在驱动被卸载时执行
{
printk("--------^_^ %s------------\n",__FUNCTION__);
free_irq(button_dev->irqno,NULL);
device_destroy(button_dev->cls,button_dev->devno);
class_destroy(button_dev->cls);
cdev_del(button_dev->cdev);
unregister_chrdev_region(button_dev->devno,1);
kfree(button_dev);
}

//声明和认证
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");

 

 

 


 

Makefile


#指定内核源码路径
KERNEL_DIR = /home/linux-3.0.8
CUR_DIR = $(shell pwd)
MYAPP = test

all:
#让make进入内核源码编译,同时将当前目录中的c程序作为内核模块一起编译
make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c

clean:
#删除上面编译生成的文件
make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
rm -rf $(MYAPP)

install:
cp *.ko $(MYAPP) /opt/rootfs/drv_module

#指定当前目录下哪个文件作为内核模块编
obj-m = button_drv.o

 

 

 


 

test


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/input.h>

//定义一个按键的数据包
struct button_event{
int code; //按键的名称---键值:KEY_DOWN
int value; //按键的状态---按下:1,松开:0
};

int main(void)
{

int fd;
int ret;
struct button_event event;

//fd = open("/dev/button",O_RDWR|O_NONBLOCK);
fd = open("/dev/button",O_RDWR);
if(fd < 0){
perror("open");
exit(1);
}

while(1){
bzero(&event,sizeof(event));
ret = read(fd,&event,sizeof(event));
#if 0
if(ret < 0){
perror("read");
exit(1);
}
#endif
if(event.code == KEY_DOWN){
if(event.value)
printf("按下了下键!\n");
else
printf("松开了下键!\n");
}
sleep(1);
}

close(fd);
return 0;
}

 

以上是关于Input_key(按键驱动)的主要内容,如果未能解决你的问题,请参考以下文章

中断方式按键驱动程序

字符设备驱动按键异步通知

Linux驱动开发-编写按键驱动

字符设备驱动按键优化休眠

USB鼠标按键驱动

《驱动学习 - 按键驱动程序》