应用程序:
fd = open("/dev/mybuttons",O_RDONLY); //阻塞方式
fd = open("/dev/mybuttons",O_RDNLY|O_NONBLOCK);//非阻塞方式
驱动程序:
也需要支持非阻塞方式
代码示例:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <mach/platform.h>
#include <linux/sched.h>
#include <linux/gpio.h>
MODULE_LICENSE("GPL");
struct cdev btn_cdev;
dev_t dev;
struct class *cls;
struct semaphore btn_sem;
unsigned char key_buf;
volatile unsigned int ev_press = 0;
wait_queue_head_t btn_wqh;
typedef struct btn_desc
{
unsigned char code;
int irq;
char *name;
int gpio;
}btn_desc_t;
btn_desc_t buttons[]=
{
{0x10, IRQ_GPIO_A_START+28, "up", PAD_GPIO_A+28},
{0x20, IRQ_GPIO_B_START+30, "down", PAD_GPIO_B+30},
{0x30, IRQ_GPIO_B_START+31, "left", PAD_GPIO_B+31},
{0x40, IRQ_GPIO_B_START+9, "right", PAD_GPIO_B+9},
};
struct timer_list btn_timer;
int btn_open(struct inode *inode, struct file *filp)
{
if(down_interruptible(&btn_sem))
{
return -ERESTARTSYS;
}
return 0;
}
int btn_close(struct inode *inode, struct file *filp)
{
up(&btn_sem);
return 0;
}
ssize_t btn_read(struct file *filp, char __user *buf, size_t len, loff_t *offset)
{
int ret = 0;
if((filp->f_flags&O_NONBLOCK) && (ev_press==0))
{
return -EAGAIN;
}
wait_event_interruptible(btn_wqh, ev_press);
ret = copy_to_user(buf, &key_buf, len);
ev_press = 0;
return len;
}
struct file_operations btn_fops =
{
.owner = THIS_MODULE,
.open = btn_open,
.release = btn_close,
.read = btn_read,
};
irqreturn_t btn_isr(int irq, void *dev)
{
btn_timer.data = (unsigned long)dev;
mod_timer(&btn_timer, jiffies+HZ/100);
return IRQ_HANDLED;
}
void btn_timer_func(unsigned long data)
{
int stat = 0;
btn_desc_t *pdata = (btn_desc_t *)data;
stat = gpio_get_value(pdata->gpio);
key_buf = pdata->code + stat;
ev_press = 1;
wake_up_interruptible(&btn_wqh);
}
int __init btn_drv_init(void)
{
int ret = 0;
int i = 0;
alloc_chrdev_region(&dev, 0, 1, "mybuttons");
cdev_init(&btn_cdev, &btn_fops);
cdev_add(&btn_cdev, dev, 1);
cls = class_create(THIS_MODULE, "buttons");
device_create(cls, NULL, dev, NULL, "mybuttons");
sema_init(&btn_sem, 1);
init_waitqueue_head(&btn_wqh);
for(; i<ARRAY_SIZE(buttons); i++)
{
ret = request_irq(buttons[i].irq, btn_isr,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
buttons[i].name, buttons+i);
}
init_timer(&btn_timer);
btn_timer.function = btn_timer_func;
return 0;
}
void __exit btn_drv_exit(void)
{
int i = 0;
del_timer(&btn_timer);
for(; i<ARRAY_SIZE(buttons); i++)
{
free_irq(buttons[i].irq, buttons+i);
}
device_destroy(cls, dev);
class_destroy(cls);
cdev_del(&btn_cdev);
unregister_chrdev_region(dev, 1);
}
module_init(btn_drv_init);
module_exit(btn_drv_exit);
原理: