您的位置:首页 > 其它

TQ2440简单的按键驱动程序

2013-11-15 15:59 218 查看
#include <linux/device.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/mm.h>

#include <linux/module.h>

#include <linux/miscdevice.h>

#include <linux/delay.h>

#include <linux/bcd.h>

#include <linux/interrupt.h>

#include <linux/smp_lock.h>

#include <linux/uaccess.h>

#include <linux/io.h>

#include <mach/regs-irq.h>

#include <asm/system.h>

#define BUTTON_MAJOR 121 /* local major, change later */

static const char DEVICE_NAME[] = "BUTTON";//设备名,注册设备后会在/dev下有此设备节点

static volatile int ev_press=0;

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

struct button_irq_desc{//定义一个按键中断的硬件结构体,方便信息管理

int irq;

unsigned long flags;

char *name;

};

static struct button_irq_desc button_irqs[]={

{IRQ_EINT1,IRQF_TRIGGER_FALLING,"KEY1"},

{IRQ_EINT4,IRQF_TRIGGER_FALLING,"KEY2"},

{IRQ_EINT2,IRQF_TRIGGER_FALLING,"KEY3"},

{IRQ_EINT0,IRQF_TRIGGER_FALLING,"KEY4"},

};

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//声明一个全局等待队列

static irqreturn_t buttons_interrupt(int irq, void *dev_id)

{

volatile int *press_cnt =(volatile int *)dev_id;

*press_cnt=*press_cnt+1;

ev_press=1;

wake_up_interruptible(&button_waitq);//中断服务程序,进入中断后,唤醒等待队列

return IRQ_RETVAL(IRQ_HANDLED);

}

static int s3c24xx_buttons_open(struct inode *inode, struct file *file)

{

int i;

int err;

for(i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++)

{

/* 注册中断,中断的关键步骤,1.中断号2中断服务函数名3中断类型标识4中断名

5 传入中断服务函数中的参数*/

err=request_irq(button_irqs[i].irq,buttons_interrupt,button_irqs[i].flags,button_irqs[i].name,(void *)&press_cnt[i]);

if(err)

break;

}

if(err){

i--;

for(;i>=0;i--)

free_irq(button_irqs[i].irq,(void *)&press_cnt[i]);

return -EBUSY;

}

return 0;

}

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++){

free_irq(button_irqs[i].irq,(void *)&press_cnt[i]);

}

return 0;

}

static int s3c24xx_buttons_read (struct file *filp, char __user *buf, size_t count, loff_t *offp)

{

unsigned long err;

wait_event_interruptible(button_waitq, ev_press);/* 读函数中等待事件ev_press为1时唤醒 */

ev_press=0;

err=copy_to_user(buf, (const void *)press_cnt, min(sizeof(press_cnt), count));/* 复制内核空间中的数到用户空间 */

memset((void *)press_cnt, 0, sizeof(press_cnt));

return err?-EFAULT:0;

}

/* poll机制驱动函数,该函数主要完成把当前进程加入等待队列

,设备没准备好时进程会休眠一定的时间然后退出 */

static unsigned int s3c24xx_buttons_poll(struct file *filp, poll_table *wait)

{

unsigned int mask = 0;

poll_wait(filp, &button_waitq, wait);

if (ev_press)

mask |= POLLIN | POLLRDNORM;

return mask;

}

/* 驱动中的关键部分,驱动程序的组织结构 */

static const struct file_operations s3c24xx_buttons_fops = {

.owner = THIS_MODULE,

.read = s3c24xx_buttons_read,

.open = s3c24xx_buttons_open,

.release = s3c24xx_buttons_close,

.poll=s3c24xx_buttons_poll,

};

static char __initdata banner[] = "MY FIRST LINUX DRIVER\n";

/* 创建一个设备类结构用于自动创建节点 */

static struct class *button_class;

static int __init s3c24xx_buttons_init(void)

{

int ret;

printk(banner);

ret=register_chrdev(BUTTON_MAJOR, "BUTTON", &s3c24xx_buttons_fops);

if( ret < 0 )

{

printk("buttons driver Unable to register driver\n");

return ret;

}

/* 创建类 */

button_class = class_create(THIS_MODULE, DEVICE_NAME);

if(IS_ERR(button_class))

{

printk("Err: failed in EmbedSky-leds class. \n");

return -1;

}

/* 创建设备文件 */

device_create(button_class, NULL, MKDEV(BUTTON_MAJOR, 0), NULL, DEVICE_NAME);

printk(" buttons initialized\n");

return 0;

}

static void __exit s3c24xx_buttons_exit(void)

{

unregister_chrdev(BUTTON_MAJOR, "DEVICE_NAME");

device_destroy(button_class, MKDEV(BUTTON_MAJOR, 0));

class_destroy(button_class);

}

module_init(s3c24xx_buttons_init);

module_exit(s3c24xx_buttons_exit);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: