完整驱动代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/gpio.h>
#define MODULE_NAME_IRQ "button_fasync"
#define BUTTON_IRQ_MAJOR 210
static struct class *btn_fasync_class;
static unsigned char key_vals[4] = {'0', '0', '0', '0'};
static DECLARE_WAIT_QUEUE_HEAD(btn_fasync_waitq);
static DEFINE_MUTEX(btn_lock);
static volatile int ev_press = 0;
struct fasync_struct *btn_fasync_queue;
struct btn_fasync_desc {
unsigned int irq_num;
unsigned int pin;
unsigned int pin_set;
unsigned int key_num;
unsigned char *btn_name;
};
static struct btn_fasync_desc btns_irq_desc[] = {
{IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 0, "KEY1"}, /* K1 */
{IRQ_EINT11, S3C2410_GPG(3), S3C2410_GPG3_EINT11, 1, "KEY2"}, /* K2 */
{IRQ_EINT2, S3C2410_GPF(2), S3C2410_GPF2_EINT2, 2, "KEY3"}, /* K3 */
{IRQ_EINT0, S3C2410_GPF(0), S3C2410_GPF0_EINT0, 3, "KEY4"} /* K4 */
};
static irqreturn_t irq_interrupt(int irq, void *dev_id)
{
struct btn_fasync_desc *btn_fasyncs = (struct btn_fasync_desc *)dev_id;
int down;
down = !s3c2410_gpio_getpin(btn_fasyncs->pin);
if (down != (key_vals[btn_fasyncs->key_num] & 1))
{
key_vals[btn_fasyncs->key_num] = '0' + down;
ev_press = 1;
wake_up_interruptible(&btn_fasync_waitq);
kill_fasync(&btn_fasync_queue, SIGIO, POLL_IN);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
static int btn_fasync_open (struct inode *inode, struct file *file)
{
unsigned int i;
down(&btn_lock);
for (i = 0; i < sizeof(btns_irq_desc)/sizeof(btns_irq_desc[0]); i++) {
if (btns_irq_desc[i].irq_num < 0) {
continue;
}
request_irq(btns_irq_desc[i].irq_num, irq_interrupt, IRQ_TYPE_EDGE_BOTH,
btns_irq_desc[i].btn_name,(void *)&btns_irq_desc[i]);
}
return 0;
}
static ssize_t btn_fasync_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long cnt;
if (!ev_press)
{
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
else
wait_event_interruptible(btn_fasync_waitq, ev_press);
}
ev_press = 0;
cnt = copy_to_user(buf, (const void *)key_vals, min(sizeof(key_vals), count));
return cnt ? -EFAULT : min(sizeof(key_vals), count);
}
static int btn_fasync_close (struct inode *inode, struct file *file)
{
unsigned int i;
for (i = 0; i < sizeof(btns_irq_desc)/sizeof(btns_irq_desc[0]); i++) {
if (btns_irq_desc[i].irq_num < 0) {
continue;
}
free_irq(btns_irq_desc[i].irq_num, (void *)&btns_irq_desc[i]);
}
up(&btn_lock);
return 0;
}
static unsigned int btn_poll (struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &btn_fasync_waitq, wait);
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
static int btn_fasync (int fd, struct file *filp, int on)
{
return fasync_helper(fd, filp, on, &btn_fasync_queue);
}
static struct file_operations btn_fasync_fops = {
.owner = THIS_MODULE,
.open = btn_fasync_open,
.read = btn_fasync_read,
.release = btn_fasync_close,
.poll = btn_poll,
.fasync = btn_fasync,
};
static int __init __btn_fasync_init(void)
{
unsigned int ret;
ret = register_chrdev(BUTTON_IRQ_MAJOR, MODULE_NAME_IRQ, &btn_fasync_fops);
btn_fasync_class = class_create(THIS_MODULE, MODULE_NAME_IRQ);
device_create(btn_fasync_class, NULL, MKDEV(BUTTON_IRQ_MAJOR, 0), NULL, MODULE_NAME_IRQ);
return ret;
}
static void __exit __btn_fasync_exit(void)
{
device_destroy(btn_fasync_class, BUTTON_IRQ_MAJOR);
class_destroy(btn_fasync_class);
unregister_chrdev(BUTTON_IRQ_MAJOR, MODULE_NAME_IRQ);
printk("<0>button_fasync module remove.\n");
}
module_init(__btn_fasync_init);
module_exit(__btn_fasync_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("toriejiang1991@sina.cn");
MODULE_DESCRIPTION("Button irq_module test for GT2440 Board");
完整测试程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/poll.h>
int fd;
void button_signal_func(int signal_num)
{
int i;
unsigned char key_vals[4] = {'0', '0', '0', '0'};
unsigned char key_tmp_vals[4];
signal_num = signal_num;
if (read(fd, key_tmp_vals, sizeof(key_tmp_vals)) != sizeof(key_tmp_vals)) {
printf("return values error.\n");
}
for (i = 0; i < sizeof(key_vals)/sizeof(key_vals[0]); i++) {
if (key_vals[i] != key_tmp_vals[i]) {
key_vals[i] = key_tmp_vals[i];
printf("The key %d is %s\n", i+1, key_vals[i] == '0' ? "up" : "down");
}
}
}
int main(int argv,char **argc)
{
int Oflags;
signal(SIGIO, button_signal_func);
if (!(fd = open("/dev/button_fasync", O_RDWR))) {
printf("open the driver_node error!\n");
return -EINVAL;
}
fcntl(fd, F_SETOWN, getpid());
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC);
while (1)
{
sleep(1000);
}
return 0;
}
Makefile如下:
ifeq ($(KERNELRELEASE),)
# learn to write Makefile for driver
KERN_VERSION = /opt/GTStudio/GT2440/linux-2.6.38.6
PWD := $(shell pwd)
#Specify flags for the module compilation.
#EXTAR_CFLAGS = -g -O0
# Build Kernerl module
modules:
$(MAKE) -C $(KERN_VERSION) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERN_VERSION) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: modules modules_install clean
else
# Kernel module
obj-m += btn_fasync_dev.o
endif