linux驱动之按键(中断)

时间:2021-09-15 23:36:57

说明:以下由两部分组成,按键驱动、按键应用程序构成;

1.驱动程序;

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/device.h>
#include <linux/poll.h>


#define DEVICE_NAME "CL_key" /*驱动加载后,执行"cat /proc/devices"命令可以查看到该设备名。*/

#define BUTTON_MAJOR 232 /*主设备号*/

typedef struct{
int irq; /*中断号*/
int pin; /*中断管脚*/
int pin_setting; /*管脚功能设置*/
int number; /*按键编号*/
char *name; /*按键名称*/
}Button_irq_desc;

static Button_irq_desc button_irq[] = {
{IRQ_EINT1, S3C2410_GPF1, S3C2410_GPF1_EINT1, 0, "KEY1"}, /*K1*/
{IRQ_EINT4, S3C2410_GPF4, S3C2410_GPF4_EINT4, 1, "KEY2"}, /*K2*/
{IRQ_EINT2, S3C2410_GPF2, S3C2410_GPF2_EINT2, 2, "KEY3"}, /*K3*/
{IRQ_EINT0, S3C2410_GPF0, S3C2410_GPF0_EINT0, 3, "KEY4"}, /*K4*/
};

/*按键被按下的次数*/
static volatile int key_values[] = {0, 0, 0, 0};

/*等待队列,应用层调用读按键接口,如果没有按键按下时,它将休眠*/
static DECLARE_WAIT_QUEUE_HEAD(Button_waitq);

/*中断事件标识*/
static volatile int env_press = 0;

static irqreturn_t button_interrupt(int irq, void *dev_id)
{
Button_irq_desc *button_irqs = (Button_irq_desc*)dev_id;

/*读取管脚的状态*/
int key_state = s3c2410_gpio_getpin(button_irqs->irq);

if (!key_state) /*按键按下*/
key_values[button_irqs->number] = (button_irqs->number+1) + 0x80;
else
key_values[button_irqs->number] = (button_irqs->number+1);

printk("KER_INFO: [isr] key_values[%d] = %d\n", button_irqs->number+1, key_values[button_irqs->number]);

env_press = 1;
/*唤醒等待队列*/
wake_up_interruptible(&Button_waitq);

return IRQ_RETVAL(IRQ_HANDLED);
}

static int CL_buttons_open(struct inode *node, struct file *file)
{
int i;
int err;

for (i = 0; i < sizeof(button_irq)/sizeof(button_irq[0]); i++) {
/*配置管脚功能*/
s3c2410_gpio_setpin(button_irq[i].pin, button_irq[i].pin_setting);
/*注册中断处理函数*/
err = request_irq(button_irq[i].irq, button_interrupt, NULL, button_irq[i].name, (void*)&button_irq[i]);
if (err) {
printk("KER_INFO: open %s pin=%d faild\n", button_irq[i].name, button_irq[i].pin);
break;
}
}

if (err) {
i--;
for (; i >= 0; i--) {
/*关中断函数*/
disable_irq(button_irq[i].irq);
/*释放中断函数*/
free_irq(button_irq[i].irq, (void*)&button_irq[i]);
}

return -EBUSY;
}
}

static int CL_buttons_close(struct inode *node, struct file *file)
{
int i;

for (i = 0; i < sizeof(button_irq)/sizeof(button_irq[0]); i++) {
/*关中断函数*/
disable_irq(button_irq[i].irq);
/*释放中断函数*/
free_irq(button_irq[i].irq, (void*)&button_irq[i]);
}

return 0;
}

/*应用程序调用read()函数时调用的接口*/
static int CL_buttons_read(struct file *file, char __user *buff, size_t conut, loff_t *offp)
{
unsigned long err;
int i;

/*按键没有按下*/
if (!env_press){
if (file->f_flags & O_NONBLOCK) {
return -EAGAIN;
}else {
/*如果全局变量env_press为0,休眠等待, 注意下面函数是宏定义*/
wait_event_interruptible(Button_waitq, env_press);
}
}

/*到这里已经env_press为1,清零*/
env_press = 0;
err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), conut));

for (i = 0; i < sizeof(key_values)/sizeof(key_values[0]); i++) {
printk("KER_INFO: [loop] key_values[%d] = %d\n", i+1, key_values[i]);
}


memset(key_values, 0, sizeof(key_values));

return err? -EFAULT : min(sizeof(key_values), conut);
}

/*应用程序调用select()函数时调用*/
static int CL_button_poll(struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;

/*用于非阻塞访问,当有数据时将立即返回,否则进入等待状态*/
poll_wait(file, &Button_waitq, wait);

if (env_press) {
/*普通或优先级带数据可读 | 普通数据可读*/
mask |= POLLIN|POLLRDNORM;
}

return mask;
}

static struct file_operations CL_button_fops = {
.owner = THIS_MODULE,
.open = CL_buttons_open,
.release = CL_buttons_close,
.read = CL_buttons_read,
.poll = CL_button_poll,
};

static struct class *button_class;
static int __init CL_buttons_init(void)
{
int ret;

/*注册字符设备驱动程序*/
ret = register_chrdev(BUTTON_MAJOR, DEVICE_NAME, &CL_button_fops);
if (ret) {
printk(DEVICE_NAME"can`t register major number.\n");
return ret;
}

/*注册一个类,使mdev可以在/dev目录下创建设备节点*/
button_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(button_class)) {
printk("Err: create class failed.\n");
return -1;
}

/*创建一个设备节点,节点名为DEVICE_NAME*/
device_create(button_class, NULL, MKDEV(BUTTON_MAJOR, 0), NULL, DEVICE_NAME);

printk(DEVICE_NAME" initialized.\n");

return 0;
}


static void __exit CL_button_exit(void)
{
unregister_chrdev(BUTTON_MAJOR, DEVICE_NAME);
device_destroy(button_class, MKDEV(BUTTON_MAJOR, 0));
class_destroy(button_class);

printk(DEVICE_NAME" exit!\n");
}

module_init(CL_buttons_init);
module_exit(CL_button_exit);

MODULE_AUTHOR("Cl learning...");
MODULE_DESCRIPTION("[2016-02-20] Cl interrupt for button test");
MODULE_LICENSE("GPL");


2.按键应用;

#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 <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>

#define DEVICE_NAME "/dev/CL_key"

int main(void)
{
int fd;
int key_values[4];

if((fd=open(DEVICE_NAME, 0)) < 0) {
perror("open()");
return -1;
}

while (1) {
fd_set rfds;
int ret;

FD_ZERO(&rfds);
FD_SET(fd, &rfds);

ret = select(fd+1, &rfds, NULL, NULL, NULL);
if (ret < 0) {
perror("select()");
exit(1);
} else if (ret == 0) {
perror("select() timeout");
} else if (FD_ISSET(fd, &rfds)) { /*能够读取到数据*/
memset(key_values, 0, sizeof(key_values));
int ret = read(fd, &key_values, sizeof(key_values));
if (ret != sizeof(key_values)) {
if (errno != EAGAIN)
perror("read key");
continue;
}else {
int i;
for (i = 0; i < sizeof(key_values)/sizeof(key_values[0]); i++) {
if (key_values[i]) {
printf("Key_%d %s\n", i+1, ((key_values[i]&0x80)? "press" : "release"));
}
}
}
}
}

return 1;
}


3.运行结果

linux驱动之按键(中断)