drv_buttons.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gcr.h>
#include <mach/irqs.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h> // copy_from_user
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/pinctrl/consumer.h>
#include <linux/err.h>
#include <linux/kdev_t.h>
#include <asm/io.h>
#include <linux/sched.h>
#define BUTTONS_CNT 1 /* 设备号个数 */
#define BUTTONS_NAME "buttons" /* 名字 */
/* 设备结构体 */
struct buttons_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct buttons_dev buttons; /* led 设备 */
struct pin_desc{
int pin;
int val;
unsigned long flags;
char *name;
};
static struct pin_desc pins_desc[3] = {
{NUC970_PB2, 0x1,IRQF_TRIGGER_FALLING,"KEY1"},
{NUC970_PB3, 0x2,IRQF_TRIGGER_FALLING,"KEY2"},
{NUC970_PB4, 0x3,IRQF_TRIGGER_FALLING,"KEY3"},
};
static unsigned char val;
static DECLARE_WAIT_QUEUE_HEAD(buttons_waitq);
/* 中断事件标志, 中断服务程序将它置1,nuc977_buttons_read 将它清0 */
static volatile int ev_press = 0;
static int nuc977_buttons_open(struct inode *ino, struct file *file)
{
file->private_data = &buttons; /* 设置私有数据 */
return 0;
}
static ssize_t nuc977_buttons_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long err;
/* 如果ev_press等于0,休眠 */
wait_event_interruptible(buttons_waitq, ev_press);
/* 执行到这里时,ev_press等于1,将它清0 */
ev_press = 0;
/* 将按键状态复制给用户,并清0 */
copy_to_user(buf, &val, 1);
val=0;
return 0;
}
/* 设备操作函数 */
static struct file_operations buttons_fops = {
.owner = THIS_MODULE,
.open = nuc977_buttons_open,
.read = nuc977_buttons_read,
};
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
struct pin_desc *pin = (struct pin_desc *)dev_id;
val = 0x80 | pin->val;
ev_press = 1;
wake_up_interruptible(&buttons_waitq);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int keyio_init(void)
{
int i;
int err;
for (i = 0; i < sizeof(pins_desc)/sizeof(pins_desc[0]); i++) {
// 注册中断处理函数
err = request_irq(gpio_to_irq(pins_desc[i].pin), buttons_interrupt, pins_desc[i].flags,
pins_desc[i].name, (void *)&pins_desc[i]);
if (err)
break;
}
if (err) {
printk("keyio_init err **********\r\n");
// 释放已经注册的中断
i--;
for (; i >= 0; i--)
free_irq(gpio_to_irq(pins_desc[i].pin), (void *)&pins_desc[i]);
return -EBUSY;
}
return 0;
}
static int __init nuc977_buttons_init(void)
{
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (buttons.major) { /* 定义了设备号 */
buttons.devid = MKDEV(buttons.major, 0);
register_chrdev_region(buttons.devid, BUTTONS_CNT,BUTTONS_NAME);
}
else
{ /* 没有定义设备号 */
alloc_chrdev_region(&buttons.devid, 0, BUTTONS_CNT, BUTTONS_NAME); /* 申请设备号 */
buttons.major = MAJOR(buttons.devid); /* 获取主设备号 */
buttons.minor = MINOR(buttons.devid); /* 获取次设备号 */
}
printk("buttons major=%d,minor=%d\r\n",buttons.major,buttons.minor);
/* 2、初始化 cdev */
buttons.cdev.owner = THIS_MODULE;
cdev_init(&buttons.cdev, &buttons_fops);
/* 3、添加一个 cdev */
cdev_add(&buttons.cdev, buttons.devid, BUTTONS_CNT);
/* 4、创建类 */
buttons.class = class_create(THIS_MODULE, BUTTONS_NAME);
if (IS_ERR(buttons.class)) {
return PTR_ERR(buttons.class);
}
/* 5、创建设备 */
buttons.device = device_create(buttons.class, NULL,
buttons.devid, NULL, BUTTONS_NAME);
if (IS_ERR(buttons.device)) {
return PTR_ERR(buttons.device);
}
keyio_init();
printk(KERN_INFO "buttons module initialized\n");
return 0;
}
static void __exit nuc977_buttons_exit(void)
{
int i;
/* 注销字符设备 */
cdev_del(&buttons.cdev);/* 删除 cdev */
unregister_chrdev_region(buttons.devid, BUTTONS_CNT);
device_destroy(buttons.class, buttons.devid);
class_destroy(buttons.class);
for (i = 0; i < sizeof(pins_desc)/sizeof(pins_desc[0]); i++) {
// 释放已经注册的中断
free_irq(gpio_to_irq(pins_desc[i].pin), (void *)&pins_desc[i]);
}
printk(KERN_INFO "nuc977_buttons_exit module exited\n");
}
module_init(nuc977_buttons_init);
module_exit(nuc977_buttons_exit);
MODULE_LICENSE("GPL");
app_button.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "string.h"
#include "linux/ioctl.h"
int main(int argc, char *argv[])
{
int fd,i;
char *filename;
unsigned char key_val;
for(i=0;i<argc;i++)
printf(" argv[%d]= %s \n",i,argv[i]);
filename=argv[1];
fd = open(filename, O_RDWR); // 打开设备
if (fd < 0) {
printf("Can't open %s \n",argv[1]);
return -1;
}
while(1)
{
read(fd, &key_val, 1);
printf("key_val = 0x%x\n", key_val);
}
close(fd);
return 0;
}
/mnt/driver/drv_buttons # insmod /lib/modules/drv_buttons.ko
buttons major=252,minor=0
buttons module initialized
/mnt/driver/drv_buttons # lsmod
drv_buttons 1550 0 - Live 0xbf000000 (O)
/mnt/app # ./app_button /dev/buttons
argv[0]= ./app_button
argv[1]= /dev/buttons
key_val = 0x81
key_val = 0x81
key_val = 0x83
key_val = 0x83
key_val = 0x83
key_val = 0x82
key_val = 0x82
key_val = 0x82
key_val = 0x82
key_val = 0x82
key_val = 0x82
key_val = 0x82
key_val = 0x82
key_val = 0x81
key_val = 0x81
key_val = 0x81
key_val = 0x81
key_val = 0x81
key_val = 0x81
因为没消抖,所以按一下会输出多次。