========使用poll修改按建驱动程序====================================
场景是在一段时间内没的中断请求则程序也有返回
应用app调用poll函数-->
内核中的sysPoll函数-->
Do_sys_poll函数--->
poll_initwait(&table);-->
fdcount = do_poll(nfds, head, &table,end_time);--->
For(;;){
for (; pfd != pfd_end; pfd++) {
if(do_pollfd(pfd, pt)) {
count++;
pt = NULL;
}
}
if (count || timed_out)
break;
poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack)
}
do_sys_poll函数在fs/select.c文件中定义,示例代码如下:
int do_sys_poll(struct pollfd __user *ufds,unsigned int nfds,
struct timespec *end_time)
{
structpoll_wqueues table;
int err = -EFAULT, fdcount, len, size;
/*Allocate small arguments on the stack to save memory and be
faster - use long to make sure the buffer isaligned properly
on 64 bit archs to avoid unaligned access */
longstack_pps[POLL_STACK_ALLOC/sizeof(long)];
structpoll_list *const head = (struct poll_list *)stack_pps;
struct poll_list *walk = head;
unsigned long todo = nfds;
if(nfds > rlimit(RLIMIT_NOFILE))
return -EINVAL;
len= min_t(unsigned int, nfds, N_STACK_PPS);
for(;;) {
walk->next = NULL;
walk->len = len;
if (!len)
break;
if (copy_from_user(walk->entries, ufds+ nfds-todo,
sizeof(structpollfd) * walk->len))
gotoout_fds;
todo -= walk->len;
if (!todo)
break;
len = min(todo, POLLFD_PER_PAGE);
size = sizeof(struct poll_list) +sizeof(struct pollfd) * len;
walk = walk->next = kmalloc(size,GFP_KERNEL);
if (!walk) {
err= -ENOMEM;
gotoout_fds;
}
}
poll_initwait(&table);
fdcount= do_poll(nfds, head, &table, end_time);
poll_freewait(&table);
for(walk = head; walk; walk = walk->next) {
struct pollfd *fds = walk->entries;
int j;
for (j = 0; j < walk->len; j++,ufds++)
if(__put_user(fds[j].revents, &ufds->revents))
goto out_fds;
}
err= fdcount;
out_fds:
walk= head->next;
while(walk) {
struct poll_list *pos = walk;
walk = walk->next;
kfree(pos);
}
returnerr;
}
void poll_initwait(struct poll_wqueues*pwq)
{
init_poll_funcptr(&pwq->pt,__pollwait);
pwq->polling_task= current;
pwq->triggered= 0;
pwq->error= 0;
pwq->table= NULL;
pwq->inline_index= 0;
}
init_poll_funcptr函数在include/linux/poll.h文件中定义
static inline voidinit_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
{
//将函数指针指向 __pollwait
pt->qproc= qproc;
pt->key = ~0UL; /* all events enabled */
}
static int do_poll(unsigned int nfds, struct poll_list *list,
struct poll_wqueues *wait, struct timespec *end_time)
{
poll_table*pt = &wait->pt;
ktime_texpire, *to = NULL;
inttimed_out = 0, count = 0;
unsignedlong slack = 0;
/*Optimise the no-wait case */
if(end_time && !end_time->tv_sec && !end_time->tv_nsec) {
pt = NULL;
timed_out = 1;
}
if(end_time && !timed_out)
slack =select_estimate_accuracy(end_time);
for(;;) {
struct poll_list *walk;
for (walk = list; walk != NULL; walk =walk->next) {
structpollfd * pfd, * pfd_end;
pfd= walk->entries;
pfd_end= pfd + walk->len;
for(; pfd != pfd_end; pfd++) { //查询多个设备文件
/*
*Fish for events. If we found one, record it
*and kill the poll_table, so we don't
*needlessly register any other waiters after
*this. They'll get immediately deregistered
*when we break out and return.
*/
//最终会调用到驱动程序中的poll函数
if (do_pollfd(pfd, pt)) {
count++; //如果驱动的poll返回的为非0值,count++
pt= NULL;
}
}
}
/*
*All waiters have already been registered, so don't provide
*a poll_table to them on the next loop iteration.
*/
//有信号在等待,程序也会返回
pt = NULL;
if (!count) {
count= wait->error;
if(signal_pending(current))
count = -EINTR;
}
//返回的条件为: count为0 或是timed_out超时
if (count || timed_out)
break;
/*
*If this is the first loop and we have a timeout
*given, then we convert to ktime_t and set the to
*pointer to the expiry value.
*/
if (end_time && !to) {
expire= timespec_to_ktime(*end_time);
to= &expire;
}
//修眠
if (!poll_schedule_timeout(wait,TASK_INTERRUPTIBLE, to, slack))
timed_out= 1;
}
returncount;
}
驱动程序示例代码如下:
/****************************************
*第五个驱动程序 添加poll机制的中断方式实现按键驱动
*****************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>
#define DEVICE_NAME"fifth_button_dev"
static struct class *fifth_button_dev_class;
int major;
staticDECLARE_WAIT_QUEUE_HEAD(button_waitq);
/*中断事件标志,中断处理函数将其置1,third_drv_read将其置0*/
static volatile int ev_press=0;
/*自定义中断结构体*/
struct button_irq_desc{
intirq;//按键中断号
intnumber;//
char*name;//按键名
};
//按键数组
static struct button_irq_descbutton_irqs[]={
{IRQ_EINT(0),0,"K0"},
{IRQ_EINT(1),1,"K1"},
{IRQ_EINT(2),2,"K2"},
{IRQ_EINT(3),3,"K3"},
{IRQ_EINT(4),4,"K4"},
{IRQ_EINT(5),5,"K5"},
{IRQ_EINT(19),6,"K6"},
{IRQ_EINT(20),7,"K7"},
};
//staticvolatile char key_values[]={'0','0','0','0','0','0','0','0'};
staticvolatile int keyValue=0;
static irqreturn_t buttons_irq(int irq,void *dev_id)
{
printk("irq=%d\n",irq);
//确定按键值
structbutton_irq_desc *button_irqs=(struct button_irq_desc *)dev_id;
intnumber;
intdown;
unsignedtmp;
number = button_irqs->number;
//检查哪个键按下
switch(number){
case0: case 1: case 2: case 3: case 4: case 5:
tmp = readl(S3C64XX_GPNDAT);
down = !(tmp & (1<<number));
break;
case6: case 7:
tmp = readl(S3C64XX_GPLDAT);
down = !(tmp & (1 << (number +5)));
break;
default:
down = 0;
}
printk("number=%d\n",number);
printk("down=%d\n",down);
/*按下down=1*/
if (down) {
keyValue=10+number;
printk("key %d down,key value= %d\n",number,keyValue);
}else{//松开
keyValue=number;
printk("key %d up,keyvalue = %d\n",number,keyValue);
}
ev_press= 1;
wake_up_interruptible(&button_waitq);
returnIRQ_RETVAL(IRQ_HANDLED);
}
static int fifth_button_dev_open(structinode *inode, struct file *file)
{
printk("fifth_button_dev_open!\n");
//采用中断的方式
//注册中断处理函数
inti;
interr=0;
for(i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++){
if(button_irqs[i].irq<0){
continue;
}
err=request_irq(button_irqs[i].irq,buttons_irq,IRQ_TYPE_EDGE_BOTH,button_irqs[i].name,(void*)&button_irqs[i]);
if(err)
break;
}
return0;
}
int fifth_button_dev_close(struct inode*inode, struct file *file){
//注销中断处理程序
inti;
for (i = 0; i <sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
if (button_irqs[i].irq < 0) {
continue;
}
free_irq(button_irqs[i].irq, (void*)&button_irqs[i]);
}
return0;
}
static ssize_t fifth_button_dev_read(structfile *file,const char __user *buf,size_t size,loff_t * ppos)
{
if(size!=1){
return -EINVAL;
}
//如果没有按键动作发生就休眠
wait_event_interruptible(button_waitq,ev_press);
//如果有动作发生直接返回
copy_to_user(buf,&keyValue,1);
ev_press=0;
return1;
}
static unsignedfifth_button_dev_poll(struct file *file, poll_table *wait)
{
unsignedint mask=0;
//不会产即休眠
poll_wait(file,&button_waitq, wait);
if(ev_press)
{
mask |= POLLIN | POLLWRNORM;
}
returnmask;
}
static struct file_operationsfifth_button_dev_fops = {
.owner = THIS_MODULE,
.open = fifth_button_dev_open,
.release = fifth_button_dev_close,
.read = fifth_button_dev_read,
.poll = fifth_button_dev_poll,
};
/*注册驱动程序*/
static int __initfifth_button_dev_init(void){
/*major设备的主设备号,name是驱动程序的名称,fops默认的是file_operations结构*/
//如果主设备号为0,系统会自动分配
major=register_chrdev(0,DEVICE_NAME,&fifth_button_dev_fops);
fifth_button_dev_class= class_create(THIS_MODULE,DEVICE_NAME);
//创建设备节点
device_create(fifth_button_dev_class,//
NULL,//
MKDEV(major,0),//
NULL,//
DEVICE_NAME);//
return0;
}
static void __exitfifth_button_dev_exit(void){
//删除设备节点
device_destroy(fifth_button_dev_class,MKDEV(major,0));
if(fifth_button_dev_class){
class_destroy(fifth_button_dev_class);
}
/*major和name必须和注册时的值一致*/
unregister_chrdev(major,DEVICE_NAME);
return;
}
module_init(fifth_button_dev_init);
module_exit(fifth_button_dev_exit);
MODULE_AUTHOR("RETACN");
MODULE_DESCRIPTION("FIFTH BUTTONdriver");
MODULE_LICENSE("GPL");
测试程序示例代码如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
/*测试添加pull机制后的中断方式按键驱动*/
int main(int argc,char **argv){
intfd;
unsignedchar key_vals;
int ret;
structpollfd fds[1];
fd=open("/dev/fifth_button_dev",O_RDWR);
if(fd<0){
printf("can not open!\n");
}
fds[0].fd = fd;
fds[0].events= POLLIN;
while(1){
ret=poll(fds,1,5000);
if(ret==0){
printf("timeout!\n");
}else{
read(fd,&key_vals,1);
printf("userkey value= %d \n",key_vals);
}
}
return0;
}
查看poll函数的使用方法:
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);