#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 "asm/arch/regs-gpio.h"
#include "asm/hardware.h"
#include "linux/poll.h"
#define DEV_NAME "tenth_drv"//设备名,用于创建字符设备用
static struct class
*tenthdrv_class;//定义一个类tenthdrv_class,用于在创建设备节点时用
static struct class_device
*tenthdrv_class_device;//定义一个设备节点tenthdrv_class_device
static struct fasync_struct
*buttons_fasync;//定义一个异步操作文件指针结构
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;
static
DECLARE_WAIT_QUEUE_HEAD(buttons_waitq);//生成一个等待队列头wait_queue_head_t,名字为buttons_waitq
static
DECLARE_MUTEX(buttons_lock);//*声明一个信号量buttons_lock并初始化它的值为0,即声明一个互斥锁
static volatile int
ev_press=0;//中断事件标志,中断服务程序将它置1,tenth_drv_read将它设置为0
struct pin_desc{
unsigned int pin;
unsigned int key_val;
};
static unsigned char key_vals;//定义一个变量,获取按键值
static struct pin_desc pins_desc[4]={
{S3C2410_GPF0,0X01},
{S3C2410_GPF2,0X02},
{S3C2410_GPG3,0X03},
{S3C2410_GPG11,0X04},
};
static irqreturn_t buttons_irq(int
irq,void *dev_id)//中断处理函数
{
struct pin_desc *pinsdec=(struct pin_desc
*)dev_id;
unsigned int pinval;
pinval=s3c2410_gpio_getpin(pinsdec->pin);//获取GPIO引脚值
if(pinval)
{
key_vals=0x80|pinsdec->key_val;
}
else
{
key_vals=pinsdec->key_val;
}
ev_press=1;
wake_up_interruptible(&buttons_waitq);
kill_fasync(&buttons_fasync,SIGIO,POLL_IN);//当数据到达时,可使用kill_fasync通知所有的相关进程
return IRQ_RETVAL(IRQ_HANDLED);
}
static int tenth_drv_open(struct inode *inode,struct
file *file)
{
if(file->f_flags & O_NONBLOCK)//如果为非阻塞
{
if(down_trylock(&buttons_lock))//如果无法获取信号量
return -EBUSY;
}
else//阻塞
{
down(&buttons_lock);//获取信号量
}
//中断处理函数
request_irq(IRQ_EINT0,buttons_irq,IRQT_BOTHEDGE,"s1",&pins_desc[0]);
request_irq(IRQ_EINT2,buttons_irq,IRQT_BOTHEDGE,"s2",&pins_desc[1]);
request_irq(IRQ_EINT11,buttons_irq,IRQT_BOTHEDGE,"s3",&pins_desc[2]);
request_irq(IRQ_EINT19,buttons_irq,IRQT_BOTHEDGE,"s4",&pins_desc[3]);
return 0;
}
ssize_t tenth_drv_read(struct file *file,char __user
*buf,size_t size,loff_t *ppos)
{
if(size!=1)
return -EINVAL;
if(file->f_flags & O_NONBLOCK)
{
if(!ev_press)
return -EAGAIN;
}
else
{
wait_event_interruptible(buttons_waitq,
ev_press);
}
copy_to_user(buf,&key_vals,1);
ev_press=0;
return 1;
}
static int tenth_drv_close(struct inode *inode,struct
file *file)
{
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]);
up(&buttons_lock);//释放信号量
return 0;
}
static unsigned tenth_drv_poll(struct
file *file,poll_table *wait)
{
unsigned int mask=0;
poll_wait(file,&buttons_waitq,wait);
if(ev_press)
mask|=POLLIN|POLLRDNORM;
return mask;
}
static int tenth_drv_fasync(int fd,struct file
*filp,int on)
{
printk("driver:tenth_drv_fasync\n");
return fasync_helper(fd, filp, on,
&buttons_fasync);
}
static struct file_operations tenth_drv_fops={
.owner=THIS_MODULE,
.open=tenth_drv_open,
.read=tenth_drv_read,
.release=tenth_drv_close,
.poll=tenth_drv_poll,
.fasync=tenth_drv_fasync,
};
int major;
static int tenth_drv_init(void)
{
major=register_chrdev(0,DEV_NAME,&tenth_drv_fops);
tenthdrv_class=class_create(THIS_MODULE,DEV_NAME);
tenthdrv_class_device=class_device_create(tenthdrv_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;
return 0;
}
static void tenth_drv_exit(void)
{
unregister_chrdev(major,DEV_NAME);
class_device_unregister(tenthdrv_class_device);
class_destroy(tenthdrv_class);
iounmap(gpfcon);
iounmap(gpgcon);
}
module_init(tenth_drv_init);
module_exit(tenth_drv_exit);
MODULE_LICENSE("GPL");