Linux驱动:POLL机制

Linux驱动程序:POLL机制

POLL机制的引入

以我们上一节 按键中断驱动程序为例,整个简易流程图如下:
Linux驱动:POLL机制_第1张图片
可以看到程序在进入read函数以后,如果没有读到键值,就会一直让进程休眠,等待按键中断唤醒。

如果我们在这个基础上引入POLL机制,就可以达到下面的效果:
Linux驱动:POLL机制_第2张图片
由上面的流程图可以看到,我们引入POLL机制以后,倘若没有按键按下,那么超过多少时间之后,进程能够继续得到执行,而不是没有按键按下,就永远休眠。

相关函数

poll()函数
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
输入参数
fds 可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回
nfds 监测驱动文件的个数
timeout 超时时间,单位为ms
输入参数中的 pollfd 结构体如下
struct pollfd {
          int fd;              /* 文件描述符 */
          short events;        /* 请求的事件类型,监视驱动文件的事件掩码 */
          short revents;       /* 驱动文件实际返回的事件 */
} ;   

事件类型events 可以为下列值:

POLLIN			有数据可读
POLLRDNORM 		有普通数据可读,等效与POLLIN
POLLPRI         有紧迫数据可读
POLLOUT			写数据不会导致阻塞
POLLER          指定的文件描述符发生错误
POLLHUP        	指定的文件描述符挂起事件
POLLNVAL      	无效的请求,打不开指定的文件描述符 

测试

实验平台

内核版本:Linux-4.19.5
开发板:SAMSUNG JZ2440

实验程序
/* 驱动程序 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int major;	//主设备号

static struct class *buttons_class;

volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;

volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;

/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;	//返回给用户的键值


static DECLARE_WAIT_QUEUE_HEAD(button_waitq);	//休眠队列
static volatile int ev_press = 0;	//条件变量

const int t_s3c2440_devid[4] = {1, 2, 3, 4};	//键值数组

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    int i_pinselect = *((int *)dev_id);
    int i_pinval = 0; 
    unsigned long ul_regval;

    switch(i_pinselect)
    {
        case 1:
        case 2:
            ul_regval = *gpfdat;		//读寄存器gpfdat的值
            if (i_pinselect == 1)
                i_pinval = (ul_regval & (1<<0)) ? 1 : 0;
            else
                i_pinval = (ul_regval & (1<<2)) ? 1 : 0;
            break;
        case 3:
        case 4:
            ul_regval = *gpgdat;		//读寄存器gpgdat的值
            if (i_pinselect == 3)
                i_pinval = (ul_regval & (1<<3)) ? 1 : 0;
            else
                i_pinval = (ul_regval & (1<<11)) ? 1 : 0;
            break;
    }

    if (i_pinval)	//按下读回来的值为0,松开读回来的值为1
    {
        /* 松开 */
        key_val = 0x80 | i_pinselect;
    }
    else
    {
        /* 按下 */
        key_val = i_pinselect;
    }

    ev_press = 1;                  			/* 表示中断发生了 */
    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */


    return IRQ_RETVAL(IRQ_HANDLED);
}


static int buttons_open(struct inode *inode, struct file *file)
{
    int i_ret;
	/* 注册一个名为S2的外部中断,上升沿和下降沿触发,中断执行函数为buttons_irq */
    i_ret = request_irq(IRQ_EINT0,  buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S2", (void *)&t_s3c2440_devid[0]);	
    i_ret = request_irq(IRQ_EINT2,  buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S3", (void *)&t_s3c2440_devid[1]);	
    i_ret = request_irq(IRQ_EINT11, buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S4", (void *)&t_s3c2440_devid[2]);	
    i_ret = request_irq(IRQ_EINT19, buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S5", (void *)&t_s3c2440_devid[3]);	

    return 0;
}

ssize_t buttons_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    int i_ret;
    if (size != 1)
        return -EINVAL;

    /* 如果没有按键动作, 休眠 */
    wait_event_interruptible(button_waitq, ev_press);

    /* 如果有按键动作, 返回键值 */
    i_ret = copy_to_user(buf, &key_val, 1);
    ev_press = 0;

    return 1;
}

static int buttons_drv_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT0, (void *)&t_s3c2440_devid[0]);	//释放中断
    free_irq(IRQ_EINT2, (void *)&t_s3c2440_devid[1]);
    free_irq(IRQ_EINT11, (void *)&t_s3c2440_devid[2]);
    free_irq(IRQ_EINT19, (void *)&t_s3c2440_devid[3]);
    return 0;
}

static unsigned buttons_drv_poll(struct file *file, poll_table *wait)
{
    unsigned ui_remark = 0;

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

	if (ev_press)
		ui_remark |= POLLIN | POLLRDNORM;

	return ui_remark;
    
}


static struct file_operations buttons_fops=
{
    .owner = THIS_MODULE,
    .open = buttons_open,
    .read = buttons_read,
    .release =  buttons_drv_close,
    .poll = buttons_drv_poll,
};

static int buttons_init(void)
{
    major = register_chrdev(0, "buttons", &buttons_fops);	//注册一个字符设备

    buttons_class = class_create(THIS_MODULE, "buttons");	//创建一个类

    device_create(buttons_class, NULL, MKDEV(major,0), NULL, "buttons");	//在类下面创建一个设备


    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);		//寄存器指针重定位
    gpfdat = gpfcon + 1;

    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);		//寄存器指针重定位
    gpgdat = gpgcon + 1;

    return 0;
}

static void buttons_exit(void)
{
    device_destroy(buttons_class, MKDEV(major,0));

    class_destroy(buttons_class);

    unregister_chrdev(major, "buttons");

    iounmap(gpfcon);
    iounmap(gpgcon);
}

module_init(buttons_init);

module_exit(buttons_exit);

MODULE_LICENSE("GPL");

/* 测试程序 */
#include 
#include 
#include 
#include 
#include 

int main(int argc, char **argv)
{
	int fd;
	unsigned char key_val;
    struct pollfd fds[1];
    int i_ret;
	
	fd = open("/dev/buttons", O_RDWR);
	if (fd < 0)
	{
	    return 0;
		printf("can't open!\n");
	}

    fds[0].fd     = fd;
	fds[0].events = POLLIN;
    
	while (1)
	{
	    i_ret = poll(fds, 1, 5000);
		if (i_ret == 0)
		{
			printf("time out\n");
		}
		else
		{
			read(fd, &key_val, 1);
			printf("key_val = 0x%x\n", key_val);
		}
	}
	
	return 0;
}


实验结果

Linux驱动:POLL机制_第3张图片
5秒一个循环,如果5秒内有按键按下就打印键值,没有按键按下就打印time out。

你可能感兴趣的:(Linux驱动移植)