Linux设备驱动之字符设备---poll/select机制

写在前面

poll机制是基于等待队列wait_queue的,我个人的理解,poll机制是对wait_queue的补充,等待队列会一直等待,直到condition满足条件并且wake_up队列头。 如果不满足,程序会一直阻塞。 poll机制让wait_queue有了超时机制,如果阻塞一定时间后会直接返回。

poll机制中几个重要的函数

设备驱动

file_operations中要添加对应的.poll

头文件

#include 

添加到等待队列头

static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)

poll_wait() 函数的名称非常容易让人产生误会,以为它和 wait_event() 等一样,会阻塞地等待某事件的发生,其实这个函数并不会引起阻塞。poll_wait() 函数所做的工作是把当前进程添加到 wait 参数指定的等待列表(poll_table)中,实际作用是让唤醒参数 queue 对应的等待队列可以唤醒因 select() 而睡眠的进程。

应用程序

头文件

#include 

poll文件集合

struct pollfd fds[1];
/*
struct pollfd {
    int fd;           //文件
    short events;     //事件
    short revents;
};

events:
#define POLLIN      0x0001  //可以无阻塞的读
#define POLLPRI     0x0002
#define POLLOUT     0x0004  //可以无阻塞的写
#define POLLERR     0x0008
#define POLLHUP     0x0010
#define POLLNVAL    0x0020
*/

poll函数

int poll(struct pollfd fd[], nfds_t nfds, int timeout);
/*
nfds:  pollfd的个数
timeout: 超时时间
*/

代码实现

设备驱动

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 

#include 



#define IMX_GPIO_NR(bank, nr)               (((bank) - 1) * 32 + (nr))
#define CYNO_GPIO_BEEP_NUM                  IMX_GPIO_NR(6,10)

int char_poll_major = 0;
dev_t char_devno;

struct char_poll_dev{
    struct cdev cdev;
};

static struct pin_desc{
    int irq;
    unsigned char *name;
    unsigned int pin;
};

static struct pin_desc beep_desc = {
    0,
    "beep_num",
    CYNO_GPIO_BEEP_NUM
};

struct char_poll_dev *char_poll_devp;
struct class *char_poll_class;
DECLARE_WAIT_QUEUE_HEAD(wq);     //定义一个等待队列头
static int condition = 0;        //等待队列条件


// 按键中断执行程序    设置等待条件为真,并且唤醒挂起的进程
static irqreturn_t beep_interrupt_handler(int irq, void *dev_id)
{
    //printk("%s\n", __func__);
    condition = 1;
    wake_up_interruptible(&wq);    //唤醒
    return IRQ_HANDLED;
}


static int char_poll_open (struct inode *inode, struct file *filp)
{
    printk(KERN_INFO "%s\n", __func__);
    return 0;
}

//用户执行read,阻塞,有按键按下时唤醒
static ssize_t char_poll_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    char *data = "button falling\n\n\n";

    //printk(KERN_INFO "%s\n", __func__);
    wait_event_interruptible(wq,condition);
    condition = 0;
    //printk(KERN_INFO "%s : %s\n", __func__, data);
    copy_to_user(buf,data,strlen(data));
    return strlen(data);
}

static ssize_t char_poll_write (struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    char data[30];
    //printk(KERN_INFO "%s\n", __func__);
    memset(data, 0, 30);
    copy_from_user(data, buf, count);
    printk(KERN_INFO "%s : data is %s\n", __func__, data);
    return strlen(data);
}

//poll机制,底层设备驱动实现,加入等待队列头wq中,如果有按键按下,返回成功事件
unsigned int char_poll_poll(struct file *filp, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    poll_wait(filp, &wq , wait);
    if(condition){
        mask |= POLLIN | POLLRDNORM;      //可无阻塞都,数据可获得
    }
    return mask;

}


static struct file_operations char_poll_fops = {
    .owner = THIS_MODULE,
    .open  = char_poll_open,
    .read  = char_poll_read,
    .write = char_poll_write,
    .poll  = char_poll_poll,     //添加poll实现

};

static int char_poll_init(void)
{
    int err, ret = -ENODEV;
    struct device *dev_temp;


    printk(KERN_INFO "%s\n", __func__);


    ret = alloc_chrdev_region(&char_devno, 0, 1, "char_poll");
    char_poll_major = MAJOR(char_devno);

    if(ret){
        printk(KERN_ERR "%s : chrdev_region fail\n", __func__);
        goto chrdev_region_fail;
    }

    char_poll_devp = kmalloc(sizeof(struct char_poll_dev), GFP_KERNEL);
    if(char_poll_devp == NULL){
        printk(KERN_ERR "%s : kmalloc is fail\n", __func__);
        goto kmalloc_fail;
    }

    memset(char_poll_devp, 0, sizeof(struct char_poll_dev));

    cdev_init(&char_poll_devp->cdev, &char_poll_fops);
    char_poll_devp->cdev.owner = THIS_MODULE;
    char_poll_devp->cdev.ops = &char_poll_fops;
    err = cdev_add(&char_poll_devp->cdev, char_devno, 1);
    if(err){
        printk(KERN_ERR "%s : cdev_add fail\n", __func__);
        goto cdev_add_fail;
    }

    //start create class point
    char_poll_class = class_create(THIS_MODULE, "char_poll");
    if(IS_ERR(char_poll_class)){
        printk(KERN_ERR "%s : class_create fail\n", __func__);
        goto class_create_fail;
    }

    dev_temp = device_create(char_poll_class, NULL, char_devno, NULL, "char_poll");
    if(IS_ERR(dev_temp)){
        printk(KERN_ERR "%s : device_create fail\n", __func__);
        goto device_create_fail;
    }

    //interrupt init
    if(gpio_request(beep_desc.pin ,beep_desc.name)){
        printk(KERN_ERR "%s : request gpio %d error\n", __func__, beep_desc.pin);
        goto err_gpio_request;
    }
    gpio_direction_input(beep_desc.pin);
    beep_desc.irq = gpio_to_irq(beep_desc.pin);
    printk(KERN_INFO "%s : the irq num is %d\n", __func__, beep_desc.irq);
    ret = request_threaded_irq(beep_desc.irq, NULL, beep_interrupt_handler , IRQF_ONESHOT | IRQF_TRIGGER_FALLING, beep_desc.name , &beep_desc);
    if(ret){
        printk(KERN_ERR "%s : request_irq is error\n", __func__);
        goto err_request_irq;
    }

    printk(KERN_INFO "%s : init end\n", __func__);

    return 0;

err_request_irq:
    free_irq(beep_desc.irq, &beep_desc);

err_gpio_request:
    gpio_free(beep_desc.pin);

device_create_fail:
    class_destroy(char_poll_class);

class_create_fail:
    cdev_del(&char_poll_devp->cdev);

cdev_add_fail:
    kfree(char_poll_devp);

kmalloc_fail:

chrdev_region_fail:
    unregister_chrdev_region(char_devno,1);
    return -1;

}

static void char_poll_exit(void)
{
    printk(KERN_INFO "%s\n", __func__);
    free_irq(beep_desc.irq, &beep_desc);
    gpio_free(beep_desc.pin);
    device_destroy(char_poll_class, char_devno);
    class_destroy(char_poll_class);
    cdev_del(&char_poll_devp->cdev);
    kfree(char_poll_devp);
    unregister_chrdev_region(char_devno,1);
}

module_init(char_poll_init);
module_exit(char_poll_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("linux char driver base");
MODULE_AUTHOR("xiaolei");

应用程序

#include 
#include 
#include 
#include 
#include 



int main(void)
{
    int fd, ret;
    char data[20];

    struct pollfd fds[1];      //poll文件集合

    fd = open("/dev/char_poll", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
    }


    write(fd, "xiaolei_write", strlen("xiaolei_write"));

    fds[0].fd = fd;
    fds[0].events = POLLIN;

    while(1){
        ret = poll(fds, 1, 5000);
        if(ret  == 0){
            printf("timeout\n");
        }
        else{
            read(fd, data, 1);
            printf("read data is %s", data);
        }
    }



    close(fd);
    return 0;

select机制中几个重要的函数

select与poll机制的设备驱动实现是一样的,仅仅上应用层的使用不同。

int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
/*
其中 readfds、writefds、exceptfds 分别是被 select() 监视的读、写和异常处理的文件描述符集合,numfds 的值是需要检查的号码最高的 fd 加 1。readfds 文件集中的任何一个文件变得可读,select() 返回;同理,writefds 文件集中的任何一个文件变得可写,select 也返回。

struct timeval {
    int tv_sec;
    int tv_usec;
};
*/

清除一个文件集合

FD_ZERO(fd_set *set)

将一个文件描述符添加到文件描述符集合

FD_SET(int fd,fd_set *set)

将一个文件描述符从文件描述符集合中删除

FD_CLR(int fd,fd_set *set)

判断文件是否被置位

FD_ISSET(int fd,fd_set *set)

示例代码

int main()
{
    int fd, i = 10;
    char data_read[20];
    fd_set rfds;

    memset(data_read, 0, sizeof(data_read));

    fd = open("/dev/char_poll", O_RDWR | O_NONBLOCK);
    if (fd < 0)
    {
        printf("can't open!\n");
    }

    while(i--){
        FD_ZERO(&rfds);       //清楚一个文件描述符集合
        FD_SET(fd, &rfds);    //将fd加入rfds文件描述符集合
        select(fd + 1, &rfds, NULL, NULL, NULL);   //阻塞,直到有可读才返回
        if(FD_ISSET(fd, &rfds)){
            printf("select : the file can be read\n");
            read(fd, data_read, 1);
            printf("data_read = %s\n", data_read);
        }
    }
    close(fd);

    return 0;
}

你可能感兴趣的:(linux,device,driver)