字符设备驱动学习笔记--按键驱动深化:poll或select操作

========使用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;

}

//返回的条件为: count0 或是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);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值