(1)原子操作
vi third_drv.c 增加原子量canopen
static atomic_t canopen = ATOMIC_INIT(1);
static int third_drv_open(struct inode *inode,struct file *file)
{
if(!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY;
}
request_irq(IRQ_EINT0, buttons_irq,IRQ_TYPE_EDGE_BOTH,"S2",&pin_desc[0]);
request_irq(IRQ_EINT2, buttons_irq,IRQ_TYPE_EDGE_BOTH,"S3",&pin_desc[1]);
request_irq(IRQ_EINT11,buttons_irq,IRQ_TYPE_EDGE_BOTH,"S4",&pin_desc[2]);
request_irq(IRQ_EINT19,buttons_irq,IRQ_TYPE_EDGE_BOTH,"S5",&pin_desc[3]);
return 0;
}
static int third_drv_close(struct inode *inode,struct file *file)
{
atomic_inc(&canopen);
free_irq(IRQ_EINT0, &pin_desc[0]);
free_irq(IRQ_EINT2, &pin_desc[1]);
free_irq(IRQ_EINT11,&pin_desc[2]);
free_irq(IRQ_EINT19,&pin_desc[3]);
return 0;
}
测试驱动:# ./thirddrvtest &
# ./thirddrvtest &
# can't open /dev/buttons!
(2)信号量
vi third_drv.c增加信号量button_lock
static DECLARE_MUTEX(button_lock);
static int third_drv_open(struct inode *inode,struct file *file)
{
down(&button_lock);
request_irq(IRQ_EINT0, buttons_irq,IRQ_TYPE_EDGE_BOTH,"S2",&pin_desc[0]);
request_irq(IRQ_EINT2, buttons_irq,IRQ_TYPE_EDGE_BOTH,"S3",&pin_desc[1]);
request_irq(IRQ_EINT11,buttons_irq,IRQ_TYPE_EDGE_BOTH,"S4",&pin_desc[2]);
request_irq(IRQ_EINT19,buttons_irq,IRQ_TYPE_EDGE_BOTH,"S5",&pin_desc[3]);
return 0;
}
static int third_drv_close(struct inode *inode,struct file *file)
{
up(&button_lock);
free_irq(IRQ_EINT0, &pin_desc[0]);
free_irq(IRQ_EINT2, &pin_desc[1]);
free_irq(IRQ_EINT11,&pin_desc[2]);
free_irq(IRQ_EINT19,&pin_desc[3]);
return 0;
}
make出现error: type defaults to 'int' in declaration of 'DECLARE_MUTEX' [-Werror=implicit-int],则将DECLARE_MUTEX(button_lock);更换为DEFINE_SEMAPHORE(button_lock);
测试驱动:# insmod third_drv.ko
third_drv_init
# ./thirddrvtest &
# ./thirddrvtest &
# ps
PID USER TIME COMMAND
911 0 0:00 ./thirddrvtest
912 0 0:00 ./thirddrvtest
913 0 0:00 ps
(3)阻塞与非阻塞
vi third_drv.c增加阻塞判断
static int third_drv_open(struct inode *inode,struct file *file)
{
if(file->f_flags & NONBLOCK)
{
down_trylock(&button_lock);
return -1;
}
else
down(&button_lock);
request_irq(IRQ_EINT0, buttons_irq,IRQ_TYPE_EDGE_BOTH,"S2",&pin_desc[0]);
request_irq(IRQ_EINT2, buttons_irq,IRQ_TYPE_EDGE_BOTH,"S3",&pin_desc[1]);
request_irq(IRQ_EINT11,buttons_irq,IRQ_TYPE_EDGE_BOTH,"S4",&pin_desc[2]);
request_irq(IRQ_EINT19,buttons_irq,IRQ_TYPE_EDGE_BOTH,"S5",&pin_desc[3]);
return 0;
}
static ssize_t third_drv_read(struct file *file,char __user *buf,size_t len,loff_t *ppos)
{
if(len!=1)
return -EINVAL;
if(file->f_flags & NONBLOCK)
{
if(!ev_press)
return -1;
}
else
wait_event_interruptible(button_waitq,ev_press);
copy_to_user(buf,&key_val,1);
ev_press = 0;
return 1;
}
make出现error: 'NONBLOCK' undeclared (first use in this function),则NONBLOCK因为O_NONBLOCK
vi thirddrvtest.c(阻塞)
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
int ret;
fd = open("/dev/buttons",O_RDWR);
if(fd<0)
{
printf("can't open /dev/buttons!\n");
return -1;
}
while(1)
{
read(fd,&retval,1);
printf("key_value = 0x%x\n",ret);
sleep(1000);
}
return 0;
}
测试驱动:
# insmod third_drv.ko
third_drv_init
# ./thirddrvtest &
# 按键
key_value = 0x1
vi thirddrvtest.c(非阻塞)
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
unsigned char ret;
fd = open("/dev/buttons",O_RDWR|O_NONBLOCK);
if(fd<0)
{
printf("can't open /dev/buttons!\n");
return -1;
}
while(1)
{
read(fd,&ret,1);
printf("key_value = 0x%x\n",ret);
sleep(1000);
}
return 0;
}
测试驱动:# ./thirddrvtest &
# can't open /dev/buttons!
在驱动程序中third_drv_open(struct inode *inode,struct file *file)
if(file->f_flags & O_NONBLOCK)
{
down_trylock(&button_lock);
return -1;
}
写错了,应该是 if(file->f_flags & O_NONBLOCK)
{
if (down_trylock(&button_lock))
return -1;
}
注意:semaphore:down_trylock() 函数尝试原子地获取信号量sem,成功或不成功获取信号量,函数都将立即返回;成功返回0,失败返回1。
重新编译后测试:
# insmod third_drv.ko
third_drv: loading out-of-tree module taints kernel.
third_drv_init
# ./thirddrvtest &
key_value = 0x0, oflags = -1
key_value = 0x0, oflags = -1
key_value = 0x81, oflags = 1
如果要打印错误原因,如下操作
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main() {
int fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);
if (fd == -1) {
printf("Failed to open /dev/buttons: %s\n", strerror(errno));
return 1;
}
// 其他操作
close(fd);
return 0;
}
(4)定时器防抖动
vi third_drv.c增加定时器
static struct timer_list button_timer;
static struct pin_desc *irq_pd;
static irqreturn_t buttons_irq(int irq,void *dev_id)
{
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&button_timer,jiffies+HZ/100);
return IRQ_HANDLED;
}
static void button_timer_function(unsigned long data)
{
unsigned int pinval;
struct pin_desc *pindesc = irq_pd;
if(!pindesc)
return;
pinval = gpio_get_value(pindesc->pin);
if(pinval)
{
key_val = 0x80|pindesc->key_val;
}
else
{
key_val = pindesc->key_val;
}
ev_press = 1;
wake_up_interruptible(&button_waitq);
kill_fasync(&button_fasync,SIGIO,POLL_IN);
}
static int third_drv_init(void)
{
init_timer(&button_timer);
button_timer.function = button_timer_function;
add_timer(&button_timer);
major = register_chrdev(0,"third_drv",&third_drv_fops);
thirddrv_class = class_create(THIS_MODULE,"thirddrv");
thirddrv_device = device_create(thirddrv_class,NULL,MKDEV(major,0),NULL,"buttons");
printk("third_drv_init\n");
return 0;
}
make出现error: implicit declaration of function 'init_timer',
error: passing argument 2 of 'init_timer_key' from incompatible pointer type [-Werror=incompatible-pointer-types]
参考Linxu内核编程报错:implicit declaration of function ‘init_timer’_linux implicit declaration of function itoa-CSDN博客将init_timer(&button_timer);button_timer.function = button_timer_function;add_timer(&button_timer);变更为timer_setup(&button_timer,button_timer_function,0);
将static void button_timer_function(unsigned long data){}变更为static void button_timer_function(struct timer_list *t){}
编译通过。
测试驱动:# insmod third_drv.ko
third_drv_init
# ./thirddrvtest &
key_value = 0x1, oflags = 1
key_value = 0x81, oflags = 1
key_value = 0x1, oflags = 1
key_value = 0x81, oflags = 1
驱动代码:
#include <asm/mach/map.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <mach/gpio-samsung.h>
/*
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpgcon = NULL;
volatile unsigned long *gpfdat = NULL;
volatile unsigned long *gpgdat = NULL;
*/
static struct timer_list button_timer;
//static atomic_t canopen = ATOMIC_INIT(1);
static DEFINE_SEMAPHORE(button_lock);
static struct fasync_struct *button_fasync;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;
static unsigned char key_val;
struct pin_desc{
unsigned int pin;
unsigned int key_val;
};
struct pin_desc pin_desc[4]={
{S3C2410_GPF(0), 0X01},
{S3C2410_GPF(2), 0X02},
{S3C2410_GPG(3), 0X03},
{S3C2410_GPG(11),0X04},
};
static struct pin_desc *irq_pd;
static irqreturn_t buttons_irq(int irq,void *dev_id)
{
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&button_timer,jiffies+HZ/100);
return IRQ_HANDLED;
}
static void button_timer_function(struct timer_list *t)
{
unsigned int pinval;
struct pin_desc *pindesc = irq_pd;
if(!pindesc)
return;
pinval = gpio_get_value(pindesc->pin);
if(pinval)
{
key_val = 0x80|pindesc->key_val;
}
else
{
key_val = pindesc->key_val;
}
ev_press = 1;
wake_up_interruptible(&button_waitq);
kill_fasync(&button_fasync,SIGIO,POLL_IN);
}
static int third_drv_open(struct inode *inode,struct file *file)
{
/*if(!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -1;
}
*/
if(file->f_flags & O_NONBLOCK)
{
if(down_trylock(&button_lock))
return -EBUSY;
}
else
down(&button_lock);
request_irq(IRQ_EINT0, buttons_irq,IRQ_TYPE_EDGE_BOTH,"S2",&pin_desc[0]);
request_irq(IRQ_EINT2, buttons_irq,IRQ_TYPE_EDGE_BOTH,"S3",&pin_desc[1]);
request_irq(IRQ_EINT11,buttons_irq,IRQ_TYPE_EDGE_BOTH,"S4",&pin_desc[2]);
request_irq(IRQ_EINT19,buttons_irq,IRQ_TYPE_EDGE_BOTH,"S5",&pin_desc[3]);
return 0;
}
static ssize_t third_drv_read(struct file *file,char __user *buf,size_t len,loff_t *ppos)
{
if(len!=1)
return -EINVAL;
if(file->f_flags & O_NONBLOCK)
{
if(!ev_press)
return -EAGAIN;
}
else
wait_event_interruptible(button_waitq,ev_press);
copy_to_user(buf,&key_val,1);
ev_press = 0;
return 1;
}
static int third_drv_close(struct inode *inode,struct file *file)
{
// atomic_inc(&canopen);
up(&button_lock);
free_irq(IRQ_EINT0, &pin_desc[0]);
free_irq(IRQ_EINT2, &pin_desc[1]);
free_irq(IRQ_EINT11,&pin_desc[2]);
free_irq(IRQ_EINT19,&pin_desc[3]);
return 0;
}
static int third_drv_fasync (int fd, struct file *file, int on)
{
return fasync_helper(fd,file,on,&button_fasync);
}
static struct file_operations third_drv_fops = {
.owner = THIS_MODULE,
.open = third_drv_open,
.read = third_drv_read,
.fasync = third_drv_fasync,
.release = third_drv_close,
};
int major;
static struct class *thirddrv_class;
static struct device *thirddrv_device;
static int third_drv_init(void)
{
/* init_timer(&button_timer);
button_timer.function = button_timer_function;
add_timer(&button_timer);
*/
timer_setup(&button_timer,button_timer_function,0);
major = register_chrdev(0,"third_drv",&third_drv_fops);
thirddrv_class = class_create(THIS_MODULE,"thirddrv");
thirddrv_device = device_create(thirddrv_class,NULL,MKDEV(major,0),NULL,"buttons");
/*
gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
gpfdat = gpfcon + 1;
gpgcon = (volatile unsigned long *)ioremap(0x56000060,16);
gpgdat = gpgcon + 1;
*/
printk("third_drv_init\n");
return 0;
}
static void third_drv_exit(void)
{
// iounmap(gpfcon);
// iounmap(gpgcon);
unregister_chrdev(major,"third_drv");
device_destroy(thirddrv_class,MKDEV(major,0));
class_destroy(thirddrv_class);
printk("third_drv_exit\n");
}
module_init(third_drv_init);
module_exit(third_drv_exit);
MODULE_LICENSE("GPL");