一步一步学习 Linux 驱动之 tiny210 按键驱动

/**
* @Author: ZP1015 
*
* @Copyright:SCUT.
* 
* @Function:char driver for button 
* 
* @Creat:2015-6-12
*
* @Modify:
**/

#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 <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#define DEVICE_NAME	"mybuttons"

struct button_desc {
	int gpio;
	int number;
	char *name;	
	struct timer_list timer;
};

/*传给用户态空间的数据*/
struct buttons_status_copy_to_user {
	int keynum;/*按键编号*/
	int buttons_cur_press;/*该按键被按了多少次*/
};

/*buttons的中断号*/
static struct button_desc buttons[] = {
	{ S5PV210_GPH2(0), 0, "KEY0" },
	{ S5PV210_GPH2(1), 1, "KEY1" },
	{ S5PV210_GPH2(2), 2, "KEY2" },
	{ S5PV210_GPH2(3), 3, "KEY3" },
	{ S5PV210_GPH3(0), 4, "KEY4" },
	{ S5PV210_GPH3(1), 5, "KEY5" },
	{ S5PV210_GPH3(2), 6, "KEY6" },
	{ S5PV210_GPH3(3), 7, "KEY7" },
};

static volatile int key_values[] = {
	0, 0, 0, 0, 0, 0, 0, 0
};

struct buttons_status_copy_to_user buttons_status_statistics;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static volatile int ev_press = 0;

static void mini210_buttons_status(struct button_desc *_data)
{
	struct button_desc *button_data = (struct button_desc *)_data;
	int down = 0;
	int tmp = 0;
	
	/*内核打印当前按键状态*/
	printk("KEY %d: %d\n", button_data->number,key_values[button_data->number]);
	
	/*TO DO:For IRQ_TYPE_EDGE_FALLING*/
	#if 1 
	key_values[button_data->number] = key_values[button_data->number] + 1;
	buttons_status_statistics.buttons_cur_press = key_values[button_data->number];
	buttons_status_statistics.keynum = button_data->number;
	ev_press = 1;
	wake_up_interruptible(&button_waitq);

	/*TO DO:For IRQ_TYPE_EDGE_BOTH*/
	#else
	/*获取电平*/
	tmp = gpio_get_value(button_data->gpio);

	/* active low */
	down = !tmp;
	if(down)   /*如果是弹起的状态*/
		/*那么就要在key_values数组的相应位存入很大的一个值*/
		/*同时又要能从值里辨别出是哪个按键*/
		key_values[button_data->number] = (button_data->number + 1) + 100;
    /*比如K1键开启的状态下,key_values[0]被置为(0+1)+99,即为101*/
	else /*如果按键是闭合的状态*/
		/*那么就要在key_values数组的相应位存入一个很小的数*/
		/*同时又要能从值中辨别出是哪个键*/
		key_values[button_data->number] = (button_data->number + 1);

	ev_press = 1;
	wake_up_interruptible(&button_waitq);
	#endif
}

static irqreturn_t button_interrupt(int irq, void *dev_id)
{
	struct button_desc *button_data = (struct button_desc *)dev_id;

	mini210_buttons_status(button_data);

	return IRQ_HANDLED;
}

static int mini210_buttons_open(struct inode *inode, struct file *file)
{
	int irq;
	int i;
	int err = 0;

	for (i = 0; i < ARRAY_SIZE(buttons); i++) {
		if (!buttons[i].gpio)
			continue;

		irq = gpio_to_irq(buttons[i].gpio);
		err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, 
				buttons[i].name, (void *)&buttons[i]);
		if (err)
			goto request_irq_fail;
	}

	ev_press = 1;
	return 0;
	
request_irq_fail:
	i--; /*当前没注册成功不需要清除注册的中断函数*/
	for (; i >= 0; i--) {
		if (!buttons[i].gpio)
			continue;
		
		irq = gpio_to_irq(buttons[i].gpio);
		disable_irq(irq);
		free_irq(irq, (void *)&buttons[i]);
	}

	return -EBUSY;
}

static int mini210_buttons_close(struct inode *inode, struct file *file)
{
	int irq, i;

	for (i = 0; i < ARRAY_SIZE(buttons); i++) {
		if (!buttons[i].gpio)
			continue;

		irq = gpio_to_irq(buttons[i].gpio);
		disable_irq(irq);
		free_irq(irq, (void *)&buttons[i]);
	}

	return 0;
}
/*mini210_buttons_read()函数的申明*/
/*read调用的具体函数*/
/*由它读取键盘输入的结果*/
/*实质上就是读取自定义结构体的值*/
/*它完成了键盘作为输入设备的核心功能*/
/*数组是否可读,要根据标志位ev_press来判断*/
/*如果数组可读,则读取数据到用户buffer中*/
/*如果数组不可读,则进程进入等待队列,等待到数组可读为止*/
/*等待队列机制,是中断管理中常用到的机制*/
/*因为有些进程经常需要等待某一事件的发生*/
static int mini210_buttons_read(struct file *filp, char __user *buff,
		size_t count, loff_t *offp)
{
	unsigned long err;
	
	/*靠ev_press标志位来判断的*/
	/*进程等待队列的机制,是进程调度的一种方法*/
	if (!ev_press) {
		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;
		/*进程休眠,放进button_waitq等待队列*/
		/*这里,把ev_press标志位设成了休眠进程的标志位了*/
		/*这是为了便于利用poll_wait函数*/
		/*也就是利于select函数*/
		else
			wait_event_interruptible(button_waitq, ev_press);
		/*在中断处理函数中,此进程会被唤醒*/
		/*唤醒前,ev_press 已被置1了*/
		/*唤醒后的执行点从这里开始*/
	}

	ev_press = 0;
	
	/*TO DO:For IRQ_TYPE_EDGE_FALLING*/
	#if 1
	err = copy_to_user((struct buttons_status_copy_to_user *)buff, (struct buttons_status_copy_to_user *)(&buttons_status_statistics),
					min(sizeof(buttons_status_statistics), count));
	
	
	/*TO DO:For IRQ_TYPE_EDGE_BOTH*/
	#else
	err = copy_to_user((int*)buff, (const int *)(&key_values),
				min(sizeof(key_values), count));

	memset((int *)key_values,0,sizeof(key_values));
	#endif
	
	return err ? -EFAULT : min(sizeof(key_values), count);
}

static unsigned int mini210_buttons_poll( struct file *file,
		struct poll_table_struct *wait)
{
	unsigned int mask = 0;
	/*poll_wait()函数*/
	/*会监测进程队列button_waitq里的进程*/
	/*例如,如果mini210_button_read所在的进程的标志位ev_press置为1了*/
	/*那么就不会再等待了*/
	/*这实质上就是select函数的运行机制*/

	poll_wait(file, &button_waitq, wait);
	if (ev_press)
		mask |= POLLIN | POLLRDNORM;

	return mask;
}

static struct file_operations dev_fops = {
	.owner		= THIS_MODULE,
	.open		= mini210_buttons_open,
	.release	= mini210_buttons_close, 
	.read		= mini210_buttons_read,
	.poll		= mini210_buttons_poll,
};

static struct miscdevice misc = {
	.minor		= MISC_DYNAMIC_MINOR,
	.name		= DEVICE_NAME,
	.fops		= &dev_fops,
};

static int __init button_dev_init(void)
{
	int ret;

	ret = misc_register(&misc);

	printk(DEVICE_NAME"\tinitialized\n");

	return ret;
}

static void __exit button_dev_exit(void)
{
	misc_deregister(&misc);
}

module_init(button_dev_init);
module_exit(button_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZP1015");


MakeFile
 
 
ifneq ($(KERNELRELEASE),)

obj-m :=mini210.o

else

KERNELDIR :=/home/ZP1015/Desktop/linux
ARM_LINUX_DIR =-I $(KERNELDIR)/arch/arm/mach-s5pv210/include/mach/  \
			   -I $(KERNELDIR)//arch/arm/include/asm
			   
all:
	 make -C $(KERNELDIR) $(ARM_LINUX_DIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
	rm -f *.o *.ko *.mod.o *.mod.c *.symvers modul* *.*~

endif

测试程序
 
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

struct buttons_status_copy_to_user {
	int keynum;
	int buttons_cur_press;
};

int main()
{
    int fd,ret;
	struct buttons_status_copy_to_user buttons_status_statistics;
    fd = open("/dev/mybuttons", 0);
    if(fd < 0) {
        printf("open error!");
        return -1;
    }
    while(1) {
        ret = read(fd, &buttons_status_statistics,\
			sizeof(struct buttons_status_copy_to_user));
        printf("you press the key %d ,%d\n", buttons_status_statistics.keynum+1,\
			                   buttons_status_statistics.buttons_cur_press);
    }
    close(fd);
	
    return 0;
}


 

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