嵌入式Linux设备驱动开发——selec/poll

应用程序调用select,select系统调用的原型:

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

fd_set数据结构来表示要监听的设备,里面存放的是设备的文件描述符

nfds:当前进程最大的文件描述符+1

readfds:监听设备的读操作

writefds:监听设备的写操作

execptfds:监听设备的异常情况

timeout:超时处理,如果指定为NULL,select如果以上三种情况都没有发生,进行永远的休眠,如果指定超时时间,超时到期立即返回

功能:监听设备,前提是要将设备的文件描述符放置在fd_set文件描述符

集合中,如果三种情况都不满足,当前进程调用select进行休眠操作,

如果设备可用或者超时,select函数立即返回,执行后续代码

关于文件描述符结合fd_set的操作宏:

void FD_CLR(int fd, fd_set *set); //将集合中的某一个设备清除

int  FD_ISSET(int fd, fd_set *set); //判断设备是否可用

void FD_SET(int fd, fd_set *set); //添加要监听的设备

void FD_ZERO(fd_set *set); //初始化fd_set

对应底层驱动的实现:

当应用程序调用select或者poll时,都会调用到底层驱动的同一个poll接口,

底层驱动的poll接口在struct file_operations中:

unsigned int (*poll) (struct file *, struct poll_table_struct *);

这个底层poll的实现及其简单,都会调用poll_wait函数,然后判断

数据的可用性或者异常,根据此信息返回0或者非0值,这样就完成了

底层poll的实现。

通过分析大量内核其他的驱动代码,得到关于poll接口使用的特点:

1.首先驱动程序都会定义一个等待队列头

2.然后调用poll_wait(文件指针,等待队列头,设备列表),调用

这个函数的效果是让当前进程添加到这个队列头中,注意只是添加,

不会立即休眠

3.最后判断设备的状态,根据状态返回0或者非0

如果要深入了解poll和select的使用,需要了解内核的系统调用流程:

fs/select.c

app:select/poll->sys_select/sys_poll->drv_poll:

sys_poll:

  do_sys_poll

    do_poll

    int timed_out = 0, count = 0;

      for (;;) {

        for(遍历所监听的设备)

          if (do_pollfd(pfd, pt)) {

          //根据fd找到对应的file,然后判断file->f_op(它指向的是

          驱动的file_operations,)中是否有poll接口,如果有,

          那么调用它,并且返回poll接口的返回值,说明变量count

          的值有底层驱动poll的返回值决定

            count++;

            pt = NULL;

          }

          

          //如果遍历设备,执行各个设备驱动的poll接口,都返回0

          ,接下来就会判断当前进程是否接收到信号,如果接收到

          信号count为非0值

          if (!count) {

            count = wait->error;

            if (signal_pending(current))

            count = -EINTR;

          }

          

          //跳出死循环的条件是:count非0或者time_out非0(超时)

          if (count || timed_out)

            break;

          //如果设备即不可用,有没有接收到信号,那么当前进程

          进入休眠,如果指定了超时时间,超时时间到期,

          立即返回0,timed_out=1,死循环再执行一次 

          if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack))

              timed_out = 1;

}

      } //如果调出死循环,就会立即返回到用户空间,也表示select

      或者poll系统调用函数的返回

总结:

1.驱动的poll接口完成一下两个工作

  1.调用poll_wait将当前进程添加到等待队列头中,注意每个驱动程序

  都有对应的唯一的队列头,但里面添加的指定的进程只有应用程序对应的主进程

  2.判断设备的状态,返回0或者非0,0表示设备不可用或者正常,菲0表示设备可用或者异常

  

2.系统调用过程完成:

  1.调用底层驱动poll接口来判断数据的可用性或者异常情况

  2.判断当前进程是否接收到信号,如果接收到信号,理解返回到用户空间

  3.如果设备不可用或者没有接收到信号,进入真正的休眠

  

  4.如果休眠,要返回用户空间的条件:

    1.数据可用,底层驱动来判断数据可用,比如中断,在中断处理函数中唤醒休眠的进程

    2.接收到信号

    3.超时返回

    

案例:给第二版本的按键驱动程序添加poll的实现

案例:在测试程序中将此段代码放在while(1)前面,看看效果

    FD_ZERO(&rdfd);

FD_SET(fd, &rdfd); //监听按键

//FD_SET(fd2, &rdfd); 监听串口

tv.tv_sec = 5; //超时时间5秒

tv.tv_usec = 0;

______________________________________________________________________________________

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

//定义驱动硬件私有结构体

struct button_hw_priv {

char *name;

unsigned long gpio;

int irq;

int code; 

};

//定义平台私有结构

struct button_platdata {

struct button_hw_priv *buttons;

int nbuttons;

};

//定义驱动私有结构体

struct button_sf_priv {

struct cdev btn_cdev;

int major;

struct class *cls;

};

//定义按键状态结构

struct button_event{

int code;  //键值

int state; //状态

};

static struct button_event btn_event;

static wait_queue_head_t btn_wq;

static int is_press;

//初始化按键信息

static struct button_hw_priv btn_info[] = {

[0] = {

.name = "KEY_UP",

.gpio = S5PV210_GPH0(0),

.irq = IRQ_EINT(0),

.code = KEY_UP

},

[1] = {

.name = "KEY_DOWN",

.gpio = S5PV210_GPH0(1),

.irq = IRQ_EINT(1),

.code = KEY_DOWN

},

};

//初始化平台私有的硬件信息

static struct button_platdata button_data = {

.buttons = btn_info,

.nbuttons = ARRAY_SIZE(btn_info)

};

static void button_release(struct device *dev)

{

}

//分配初始化platform_device

static struct platform_device button_dev = {

.name = "mybutton",

.id = -1,

.dev = {

.release = button_release,

.platform_data = &button_data

}

};

static int button_open(struct inode *inode,struct file *file)

{

struct button_sf_priv *pbtn = container_of(inode->i_cdev, struct button_sf_priv,btn_cdev);

file->private_data = pbtn;

return 0;

}

static unsigned int button_poll(struct file *file,struct poll_table_struct *wait)

{

unsigned int mask = 0;

poll_wait(file, &btn_wq, wait); //不会立即休眠

if(is_press) {

mask |= POLLIN | POLLRDNORM;//设备可用,数据可用

}

return mask;

}

static ssize_t button_read(struct file *file,char __user *buf,size_t count,loff_t *ppos)

{

wait_event_interruptible(btn_wq, is_press != 0);

is_press = 0;

copy_to_user(buf, &btn_event, sizeof(btn_event));

return count;

}

static struct file_operations button_fops = {

.owner = THIS_MODULE,

.open = button_open,

.poll = button_poll,

.read = button_read

};

static irqreturn_t button_isr(int irq, void *dev_id)

{

struct button_hw_priv *pbtn_hw = 

(struct button_hw_priv *)dev_id;

btn_event.state = !gpio_get_value(pbtn_hw->gpio);

btn_event.code = pbtn_hw->code;

wake_up_interruptible(&btn_wq);

is_press = 1;

return IRQ_HANDLED;

}

static int button_probe(struct platform_device *pdev)

{

int i;

dev_t dev_id;

struct button_platdata *pbtndata =pdev->dev.platform_data;

struct button_sf_priv *pbtn = kzalloc(sizeof(struct button_sf_priv), GFP_KERNEL);

if (pbtn->major) {

dev_id = MKDEV(pbtn->major, 0);

register_chrdev_region(dev_id, 1, "button");

} else {

alloc_chrdev_region(&dev_id, 0, 1, "button");

pbtn->major = MAJOR(dev_id);

}

cdev_init(&pbtn->btn_cdev, &button_fops);

cdev_add(&pbtn->btn_cdev, dev_id, 1);

pbtn->cls = class_create(THIS_MODULE, "button");

device_create(pbtn->cls, NULL, dev_id, NULL, "button");

for(i = 0; i < pbtndata->nbuttons; i++) {

struct button_hw_priv *pbtn_hw = NULL;

pbtn_hw = &pbtndata->buttons[i];

gpio_request(pbtn_hw->gpio, pbtn_hw->name);

request_irq(pbtn_hw->irq, button_isr,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,pbtn_hw->name, pbtn_hw);

}

init_waitqueue_head(&btn_wq);

dev_set_drvdata(&pdev->dev, pbtn);

return 0;

}

static int button_remove(struct platform_device *pdev)

{

int i;

struct button_platdata *pbtndata = pdev->dev.platform_data;

struct button_sf_priv *pbtn = dev_get_drvdata(&pdev->dev);

dev_t dev_id = MKDEV(pbtn->major, 0);

for(i = 0; i < pbtndata->nbuttons; i++) {

struct button_hw_priv *pbtn_hw = &pbtndata->buttons[i];

gpio_free(pbtn_hw->gpio);

free_irq(pbtn_hw->irq, pbtn_hw);

}

device_destroy(pbtn->cls, dev_id);

class_destroy(pbtn->cls);

cdev_del(&pbtn->btn_cdev);

unregister_chrdev_region(dev_id, 1);

kfree(pbtn);

return 0;

}

//分配初始化platform_driver

static struct platform_driver button_drv = {

.driver = {

.name = "mybutton",

.owner = THIS_MODULE

},

.probe = button_probe,

.remove = button_remove

};

static int button_init(void)

{

//注册platform_device

platform_device_register(&button_dev);

//注册platform_driver

platform_driver_register(&button_drv);

return 0;

}

static void button_exit(void)

{

platform_device_unregister(&button_dev);

platform_driver_unregister(&button_drv);

}

module_init(button_init);

module_exit(button_exit);

MODULE_LICENSE("GPL v2");

______________________________________________________________________________________

测试APP

______________________________________________________________________________________

#include

#include

#include

#include

#include

#include

#include

#include

struct button_event {

int code;

int state;

};

int main(int argc, char *argv[])

{

struct button_event btn_event;

int fd;

fd_set rdfd; //读设备集合

struct timeval tv;

int ret = 0;

fd = open("/dev/button", O_RDWR);

if (fd < 0) {

printf("open button failed.\n");

return -1;

}

while (1) {

FD_ZERO(&rdfd);

FD_SET(fd, &rdfd); //监听按键

//FD_SET(fd2, &rdfd); 监听串口

tv.tv_sec = 5; //超时时间5秒

tv.tv_usec = 0;

//启动监听

ret = select(fd+1, &rdfd, NULL, NULL, &tv);

if (ret < 0)

printf("select error!\n");

else if (ret == 0)

printf("timeout!\n");

else {

if (FD_ISSET(fd, &rdfd)) {

//判断数据是否来源按键

read(fd, &btn_event, 

sizeof(btn_event));

printf("code = %d, state = %d\n",

btn_event.code,

btn_event.state);

}

}

}

close(fd);

return 0;

}

猜你喜欢

转载自blog.csdn.net/qq_33611327/article/details/82734716