编写按键驱动的方法很多,这里我仅仅列举用中断法加上简单地字符设备注册来编写,并没有采用misc设备来注册。(这里的注册函数都是相对古老,以后不推荐使用!)首先编写一个设备驱动程序头文件先定义了,这很容易,照搬别人的就行了。接着确定你的设备驱动程序会用到的数据结构,这里会用到一个重要的数据结构,struct
先列出该驱动程序会用到一些重要函数以及作用:
void
request_irq(unsigned
申请中断函数,其中参数作用如下:
unsigned
irqreturn_t
unsigned
const
void
void
void
unsigned
wake_up_interruptible(&button_wait):唤醒等待队列里的某个进程这里是button_wait。
wait_event_interruptible(button_event,ev_press):等待事件中断发生,这里的事件便是ev_press
copy_to_user(void
Memset:C库函数,将某缓冲区或是数据结构写入某一数值。
static
static
对应有字符设备注销函数:
static
该驱动会用到的所有函数就这些了,理解了这些函数就差不多了解整个驱动流程了,接下来我们说一下整个按键驱动执行的过程:首先程序先从初始化函数执行,注册字符设备的同时会执行file_operations的操作函数。所以首先是打开函数,打开函数里我们处理的是注册中断,一般注册中断都是在open函数里实现的,注册完后就可以等待按键事件的发生了。所以进入等待队列并阻塞。这里就用到函数poll,一旦有按键事件发生就会检测到存储按键事件的数组里有了值,所以会唤醒等待队列的进程从而读取数据到用户空间。用户空间读完值后清空数组并继续等待下一次按键的发生,这个就是用户空间应用程序里实现的了,驱动不管。记住驱动知识负责提供功能,而不管怎么应用这个功能。即DDR3说的机制(mechanism)与策略(policy)。接下来按照驱动程序的编写来讲解整个驱动的实现。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define
static
static
static
int
int
int
int
char
};
static
{IRQ_EINT8,S3C2410_GPG(0),S3C2410_GPG0_EINT8,1,"key1"},
{IRQ_EINT11,S3C2410_GPG(3),S3C2410_GPG3_EINT11,2,"key2"},
{IRQ_EINT13,S3C2410_GPG(5),S3C2410_GPG5_EINT13,3,"key3"},
{IRQ_EINT14,S3C2410_GPG(6),S3C2410_GPG6_EINT14,4,"key4"},
{IRQ_EINT15,S3C2410_GPG(7),S3C2410_GPG7_EINT15,5,"key5"},
{IRQ_EINT19,S3C2410_GPG(11),S3C2410_GPG11_EINT19,6,"key6"},
};
static
static
{
for(i=0;i<sizeof(button_event)/sizeof(button_event[0]);i++)
{
s3c2410_gpio_cfgpin(button_event[i].pinname,
ret=request_irq(button_event[i].irp,button_handler_lg,IRQ_TYPE_EDGE_BOTH,DEVICE_NAME,(void
if(ret<0)
{
printk(KERN_INFO"can`t
break;
}
}
if(ret<0)
{
i--;
for(;i>=0;i--)
{
disable_irq(button_event[i].irq);
free_irq(button_event[i].irq,(void*)&button_event[i]);
}
return
}
return
}
{
struct
int
if(up)
key_values[button_event->pinnum]=up+0x80;
else//否则是弹起状态
key_values[button_event->pinnum]=up;
//然后置标志位为1,这是为了通知后面的read程序。因为如果没有数据可读的时候read函数是阻塞的,它会在那可中断休眠*/
ev_press=1;
//唤醒等待队列的进程,说有数据可读了,因为该进程会调用read函数读取数据但是一时没有数据所以就进入可中断阻塞状态,所以需要在这唤醒,函数里头是等待队列名字*/
wake_up_interruptible(&button_wait);
return
}
//这是read函数,在前头已经讲了很多了*/
static
{unsigned
//进程调用这个函数时一开始便判断标志位,若为0表示无数据可读,就进入if语句,然后再判断进程是否设置为非阻塞方式,若是的话就不等待立刻返回,若不是就等待事情的发生//
if(!ev_press)
{
if(filp->f_flags&O_NONBLOCK)
{
return
}
else
//调用该函数进入等待中断状态,形参是等待中断发生时的标志,即ev_press,另外如果中断发生了就返回到这个函数来执行*/ wait_event_interruptible(button_event,ev_press);
}
//将数据复制到用户空间
err=copy_to_user(buff,(const
//复制完后清空数组并清标志位
memset((void
ev_press=0;
//如果复制失败就返回一个错误值
if(err)
{
return
}
else
}
}
//阻塞函数实现步骤:初始化poll_table表,一般我们在驱动里不用做这个,然后poll方法调用的poll_wait会把当前进程挂到驱动程序提供的wait_queue_head_t中,加入能读或能写就返回,否则调用schedule_timeout函数睡眠,这是和read函数配合使用的。*/
static
{
unsigned
//将进程挂到等待队列里
poll_wait(file,&button_wait,wait);
//如果可读的话就进入if语句,将掩码置为数据可读(POLLRDNORM)与设备可读(POLLIN)
if(ev_press)
mask
return
}
//关闭函数
static
{
int
for(i=0;i<sizeof(button_event)/sizeof(button_event[0]);i++)
{
disable_irq(button_event[i].irq);
free_irq(button_event[i].irq,(void*)&button_event[i]);
}
return
}
static
.owner
.open
.read
.poll
.release
}
static
{
int
if(ret=register_chrdev(0,DEVICE_NAME,&lg_button)<0)
{
printk(DEVICE_NAME"can`t
return
}
Return
}
static
{
unregister_chrdev(0,DEVICE_NAME);
}
module_init(init_button_lg);
module_exit(exit_button_lg);