GT2440按键驱动添加fasync机制

完整驱动代码如下:

#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("[email protected]");
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


你可能感兴趣的:(linux,驱动)