按键驱动(s3c2440)

s3c2440_buttons.c

#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/gpio.h>

#define DEVICE_NAME     "buttons111"  //设备名称

//定义按键中断结构体
struct button_irq_desc {
    int irq;
    int pin;
    int pin_setting;//定义键值,以传递给应用层
    int number;
    char *name;	
};


/*
gpio_to_irq
*/

/*结构体实体定义*/
static struct button_irq_desc button_irqs [] = {
	 /*1:中断号2:IO 3:引脚功能4:按键编号,5:名称   结构体数组 */
    {IRQ_EINT8 , S3C2410_GPG(0) ,  S3C2410_GPG0_EINT8  , 0, "KEY0"},
    {IRQ_EINT11, S3C2410_GPG(3) ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},
    {IRQ_EINT13, S3C2410_GPG(5) ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},
    {IRQ_EINT14, S3C2410_GPG(6) ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},
    {IRQ_EINT15, S3C2410_GPG(7) ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},
    {IRQ_EINT19, S3C2410_GPG(11),  S3C2410_GPG11_EINT19, 5, "KEY5"},
};

static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};/*按键缓冲区*/


/*内核的一个函数,声明一个队列,定义并初始化。因为本驱动是基于中断方式的,
在此创建一个等待队列,以配合中断函数使用;当有按键按下并读取到键值时,将
会唤醒此队列,并设置中断标志,以便能通过 read 函数判断和读取键值传递到用
户态;当没有按键按下时,系统并不会轮询按键状态,以节省时钟资源*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

/*中断标识变量,配合上面的队列使用,中断服务程序会把它设置为1,read 函数会把它清零*/
static volatile int ev_press = 0;


static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{//*dev_id内核提供的参数是(void *)&button_irqs[i]传递来的参数,判断第几个中断
       

        //屏蔽按键中断
    struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
    int down;
	
	/*获取键状态*/
    down = !s3c2410_gpio_getpin(button_irqs->pin);

	//按键 按下 down = 1;
	//     松开 down = 0;

	printk("down = %d\n", down);
   
	/*状态改变,按键被按下,从这句可以看出,当按键没有被按下的时候,寄存器的值为1(上拉),
          当按键被按下的时候,寄存器对应的值为0*/
    if (down != (key_values[button_irqs->number] & 1)) { 

	       /*如果key1 被按下,则key_values[1]就变为’1’,对应的ASCII 码十六进制为31*/
		key_values[button_irqs->number] = '0' + down;
	
        ev_press = 1;

                /*中断唤醒函数:消息队列,消息唤醒*/
        wake_up_interruptible(&button_waitq);
    }
    
    return IRQ_RETVAL(IRQ_HANDLED);
}


/**在应用程序执行open(“/dev/buttons111”,…)时会调用到此函数,在这里,它的作用主要
是注册6 个按键的中断。所用的中断类型是IRQ_TYPE_EDGE_BOTH,也就是双沿触发,在上
升沿和下降沿均会产生中断,这样做是为了更加有效地判断按键状态*/
static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
    int i;
    int err = 0;
    
    for (i = 0; i <6; i++) {//占空间大小
	if (button_irqs[i].irq < 0) {
			continue;
	}        /*申请6个中断*/
                  /*1;中断号;2:中断函数3:类型:双沿触发IRQ_TYPE_EDGE_BOTH 4:中断名称5:传给中
                     断函数的参数,根据参数确定是哪个中断*/
        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, 
                          button_irqs[i].name, (void *)&button_irqs[i]);


        if (err)  //button_irqs[i].irq发生 中断时,调用中断函数buttons_interrupt,中断共享,要注意优先级。注册成功返回0,失败是其他值。
            break;
    } 
     /*如果出错,释放已经注册的中断,并返回*/
    if (err) {
        i--;
        for (; i >= 0; i--) {
	     	if (button_irqs[i].irq < 0) {
		 		continue;
	      	}
			
	 		disable_irq(button_irqs[i].irq);
       		free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
        }
        return -EBUSY; /*出错处理*/
    }

       /*注册成功,则中断队列标记为0,表示可以通过read 读取*/
    ev_press = 0;
    
    return 0;
}


/*此函数对应应用程序的系统调用close(fd)函数,在此,它的主要
作用是当关闭设备时释放6 个按键的中断*处理函数*/
static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
    int i;
    
   for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
		if (button_irqs[i].irq < 0) {
		    continue;
		}

		/*释放中断号,并注销中断处理函数*/
		free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
    	}

    return 0;
}

/*对应应用程序的read(fd,…)函数,主要用来向用户空间传递键值*/
static int s3c24xx_buttons_read(struct file *filp, 
                                char __user *buff, 
                                size_t count, 
                                loff_t *offp)
{   //char __user *buff, size_t count,用户传递过来的参数。
    unsigned long err;

    if (!ev_press) {
	/*是否开阻塞,O_NONBLOCK标志,内核结构一个成员,
          当中断标识为0 时,并且该设备是以非阻塞方式打开时,返回*/
	if (filp->f_flags & O_NONBLOCK)
	    return -EAGAIN;

	else/*如果阻塞打开,则休眠*/

		/*当前进程睡眠,当中断标识为0 时,并且该设备是以阻塞方式打开时,
                  进入休眠状态,等待被唤醒,唤醒条件:有消息过来,标志变1。*/
	    wait_event_interruptible(button_waitq, ev_press);
    }  

       /*等待消息队列,标志位*/
    ev_press = 0;

       /*一组键值被传递到用户空间,buff:用户空间   只有6个,如果申请8个,
           只会给6个。2:取两个参数的最小值*/
    err = copy_to_user(buff, 
                       (const void *)key_values, 
                        min(sizeof(key_values), 
                        count));

/*   copy_to_user  成功  返回0
*				   失败  返回未拷贝的字节个数
*/

          /*内核里的数据 (缓冲区数据(键值))拷到用户(应用程序里面送来的),
             数据长度最小值固定的指针(地址)*/
   // memset(key_values,'0',6);

    /*err等于0取min(sizeof(key_values), count);大于0取-EFAULT*/
    return err ? -EFAULT : min(sizeof(key_values), count);
}


/*被上层应用程序调用时的select函数的驱动程序的实现*/
#if 0
static unsigned int s3c24xx_buttons_poll( struct file *file, 
                                          struct poll_table_struct *wait)

{         /*判断是否有按键,*/
    unsigned int mask = 0;

        /*把调用poll 或者select 的进程挂入队列,以便被驱动程序唤醒*/
        /*1:动态结构2:定义的等待队列3:内核的队列,把button_waitq加入到wait队列中*/
    poll_wait(file, &button_waitq, wait);


    if (ev_press)
		/*返回按键当前的状况,内核通过判断 POLLIN可以读了*/
        mask |= POLLIN | POLLRDNORM;
    return mask;
}
#endif

static struct file_operations dev_fops = {
    .owner   =   THIS_MODULE,
    .open    =   s3c24xx_buttons_open,
    .release =   s3c24xx_buttons_close, 
    .read    =   s3c24xx_buttons_read,
//    .poll    =   s3c24xx_buttons_poll,/*查询函数:设备是否在忙*/
};


//填充杂项设备结构体
static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,    //minor如果填充MISC_DYNAMIC_MINOR,则是动态次设备号,次设备号由misc_register动态分配的。
	.name = DEVICE_NAME,
	.fops = &dev_fops,
};

static int __init dev_init(void)
{
	int ret;
      /*设备注册,内核提供的注册函数*/
	ret = misc_register(&misc);

       /*打印buttons tinitialized*/
	printk (DEVICE_NAME"\tinitialized\n");

	return ret;
}

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

module_init(dev_init); //模块初始化,仅当使用insmod/podprobe 命令加载时有用,
                                                    //如果设备不是通过模块方式加载,此处将不会被调用
module_exit(dev_exit); //卸载模块,当该设备通过模块方式加载后,可以通过
                       //rmmod 命令卸载,将调用此函数
MODULE_LICENSE("GPL"); //版权信息
MODULE_AUTHOR(" Inc.");//作者名字

Makefile

# Makefile 2.6
ifneq ($(KERNELRELEASE),)

obj-m := s3c2440_buttons.o

else

#KDIR :=  /lib/modules/2.6.18-53.el5/build
KDIR :=  /home/mao/work0630/linux-2.6.32.2
all:
	make -C $(KDIR) M=$(PWD) modules
	cp s3c2440_buttons.ko /home/mao/nfsroot  
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers

endif

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

int main(void)
{
	int buttons_fd;
	char buttons[6] = {'0', '0', '0', '0', '0', '0'};

	buttons_fd = open("/dev/buttons111", 0);
	if (buttons_fd < 0) {
		perror("open device buttons error");
                close(buttons_fd);
		exit(1);
	}

	for (;;) {
		char current_buttons[6];
		int count_of_changed_key;
		int i;
		if (read(buttons_fd, current_buttons, sizeof (current_buttons)) != sizeof (current_buttons)) {
			perror("read buttons:");
         close(buttons_fd);
			exit(1);
		}

		for (i = 0, count_of_changed_key = 0; i < sizeof (buttons) / sizeof (buttons[0]); i++) {
			if (buttons[i] != current_buttons[i]) {
				buttons[i] = current_buttons[i];
				
				printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down");
				count_of_changed_key++;
			}
		}
		if (count_of_changed_key) {
			printf("\n");
		}
	}

	close(buttons_fd);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值