#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/poll.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/delay.h>
static struct class *sevendrv_class;
static struct class_device *sevendrv_class_devs;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
volatile int ev_press = 0;
/********************************************************************************************
应用层调用read函数,会相应的调用file_operation中的read函数。其中有如下几种模式
一.轮训模式,会耗用大量cpu资源
1.自己写调用函数。需要知道GPIO的虚拟地址,此时应用vir_add=ioremap(phy_address,len)和iounmap(vir_addr)。
2.应用程序 while(1){read()};
二.中断模式
利用中断和系统自带函数。request_irq(irq,call_back,module,name,value);
1.阻塞模式。
a.定义等待队列button_waitq,
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
b. 内核read函数中如果ev_press条件不满足且没有信号到来进入睡眠阻塞状态。
wait_event_interruptible(button_waitq, ev_press);
ev_press = 0;
c.有数据过来唤醒等待队列。
wake_up_interruptible(&button_waitq);
ev_press = 1;
2.poll机制。和阻塞模式类似都是靠信号唤醒,多了个定时功能。
a.定义等待队列button_waitq,
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
b.注册poll函数
struct file_operations {.poll = seven_drv_poll,}
{
unsigned int mask = 0;
poll_wait(file,&button_waitq,wait);//不会立即睡眠,sys_poll会调用此函数进入计时和睡眠
if(ev_press)
mask |= POLLIN | POLLRDNORM;//sys_poll 会应用这个mask,ev_press还是有用的
}
c.应用层调用poll
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;read模式
ret = poll(fds, 1, 5000);//ms
3.信号机制。应用层注册信号,中断过来,内核向app发送信号。
kernel做以下工作:
a.注册
static struct fasync_struct *button_async;//声明变量
struct file_operations {.fasync = seven_drv_fasync,}
fasync_helper(fd,filp,on, &button_async);
b.中断函数中执行
kill_fasync(&button_async, SIGIO, POLL_IN);
用户程序:
a.将进程id信息告诉内核,并置FASYNC位
fcntl(fd,F_SETOWN,getpid());
oflags = fcntl(fd,F_GETFL);
fcntl(fd,F_SETFL,oflags|FASYNC);
b.注册信号函数,my_signal 为callback函数
signal(SIGIO,my_signal);
********************************************************************************************/
//互斥锁
static atomic_t canopen= ATOMIC_INIT(1);//原子操作
static DECLARE_MUTEX(button_lock);
/**********************************************************************************************
防止多个用户程序读取按键值操作
1.原子操作。都是volatile定义,直接从内存读,不经过寄存器
a.static atomic_t canopen= ATOMIC_INIT(1);定义canpen为1
b.open 中,判断如果
if(!atomic_dec_and_test(&canopen)) //canopen减1是否为0,为0则真
atomic_inc(&canopen);//加回去,返回
return -EBUSY;
c.close后 atomic_inc(&canopen);
2.互斥锁。
static DECLARE_MUTEX(button_lock);1
down(&button_lock)//取用,得不到就会阻塞其实就是-1
up(&button_lock);//释放+1
down_trylock(&button_lock)//不会和down一样阻塞。
***********************************************************************************************/
struct timer_list button_timer;
/**********************************************************************************************/
//1.定义timer list,初始化 init_timer(&button_timer);HZ=100,jiffers=10ms
//2.增加定器,add_timer(&button_timer);写调用时间,第一次时间到达执行callback函数并且只执行一次。
//3.修改定时器时间。mod_timer(&button_timer, jiffies + 2*HZ);每过jiffies + 2*HZ,就会调用一次callback
/**********************************************************************************************/
static int major;
volatile unsigned int *GPFCON = NULL;
volatile unsigned int *GPFDAT = NULL;
volatile unsigned int *GPGCON = NULL;
volatile unsigned int *GPGDAT = NULL;
unsigned int key_values;
static struct fasync_struct *button_async;
struct pin_desc{
unsigned int pin;
unsigned int key_val;
};
struct pin_desc *dev_id = NULL;
struct pin_desc pins_desc[4] = {
{S3C2410_GPF0,0X01},
{S3C2410_GPF2,0X02},
{S3C2410_GPG3,0X03},
{S3C2410_GPG11,0X04},
};
static void button_irq_timer_expired(unsigned long p)
{
struct pin_desc *tmp = (struct pin_desc*)dev_id;
#if 1
if(!tmp)
{
printk("null\n");//定时器第一次会调用这个函数
return;
}
#endif
unsigned int pinval;
pinval=s3c2410_gpio_getpin(tmp->pin);
if(pinval){
key_values = 0x80|tmp->key_val;
//printk("high,0x%x,%x\n",tmp->pin,pinval);
}else {
key_values = tmp->key_val;
}
ev_press = 1;
wake_up_interruptible(&button_waitq);
kill_fasync(&button_async, SIGIO, POLL_IN);
}
irqreturn_t button_irq(int irq, void *dev_id_tmp)
{
dev_id = (struct pin_desc*)dev_id_tmp;
mod_timer(&button_timer, jiffies + 2*HZ);
return IRQ_HANDLED;
}
static int seven_drv_open(struct inode *inode, struct file *file)
{
#if 0
if(!atomic_dec_and_test(&canopen)) {
atomic_inc(&canopen);
return -EBUSY;
}
#endif
if(file->f_flags & O_NONBLOCK){
if(down_trylock(&button_lock))
return -EBUSY;
}else {
down(&button_lock);
}
request_irq(IRQ_EINT0,button_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
request_irq(IRQ_EINT2,button_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
request_irq(IRQ_EINT11,button_irq, IRQT_BOTHEDGE, "S4",&pins_desc[2]);
request_irq(IRQ_EINT19,button_irq, IRQT_BOTHEDGE, "S5",&pins_desc[3]);
return 0;
}
static int seven_drv_write(struct file * file, const char __user * buf,size_t count, loff_t * off)
{
return 0;
}
static int seven_drv_read(struct file * file, const char __user * buf,size_t count, loff_t * off)
{
//wait_event_interruptible(button_waitq, ev_press);
//ev_press = 0;
copy_to_user(buf,&key_values,sizeof(key_values));
return sizeof(key_values);
}
int seven_drv_close(struct inode * p, struct file *p2)
{
//atomic_inc(&canopen);
up(&button_lock);
free_irq(IRQ_EINT0,&pins_desc[0]);
free_irq(IRQ_EINT2,&pins_desc[1]);
free_irq(IRQ_EINT11,&pins_desc[2]);
free_irq(IRQ_EINT19,&pins_desc[3]);
}
unsigned int seven_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
//poll_wait(file,&button_waitq,wait);
if(ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
static int seven_drv_fasync(int fd, struct file *filp, int on)
{
return fasync_helper(fd,filp,on, &button_async);
}
struct file_operations seven_drv_fops = {
.owner = THIS_MODULE,
.open = seven_drv_open,
.write = seven_drv_write,
.read = seven_drv_read,
.release = seven_drv_close,
.poll = seven_drv_poll,
.fasync = seven_drv_fasync,
};
int seven_drv_init(void) //入口函数
{
int minor = 0;
init_timer(&button_timer);
//button_timer.data = (unsigned long) SCpnt;
button_timer.expires = jiffies + 5*HZ; /* 5s 后时间到,调用 button_irq_timer_expired*/
button_timer.function = (void (*)(unsigned long)) button_irq_timer_expired;
add_timer(&button_timer);
GPFCON =(unsigned int*)ioremap(0x56000050, 16);
GPFDAT = GPFCON + 1;
GPGCON =(unsigned int*)ioremap(0x56000060, 16);
GPGDAT = GPGCON + 1;
major = register_chrdev(0,"seven_drv",&seven_drv_fops);//注册,告诉内核,0表示内核分配
sevendrv_class = class_create(THIS_MODULE,"sevendrv");
sevendrv_class_devs = class_device_create(sevendrv_class,NULL,MKDEV(major,0),NULL,"button");// 创建/dev/button结点
#if 0
sevendrv_class_devs[1] = class_device_create(sevendrv_class,NULL,MKDEV(major,1),NULL,"led1");
sevendrv_class_devs[2] = class_device_create(sevendrv_class,NULL,MKDEV(major,2),NULL,"led2");
sevendrv_class_devs[3] = class_device_create(sevendrv_class,NULL,MKDEV(major,3),NULL,"led3");
#endif
return 0;
}
int seven_drv_exit(void) //入口函数
{
int minor;
iounmap(GPFCON);
iounmap(GPGCON);
unregister_chrdev(major,"seven_drv");//卸载驱动
#if 0
for (minor = 0; minor < 4; minor++)
{
class_device_unregister(sevendrv_class_devs[minor]);
}
#endif
class_device_unregister(sevendrv_class_devs);
class_destroy(sevendrv_class);
}
module_init(seven_drv_init);
module_exit(seven_drv_exit);
MODULE_LICENSE("GPL");