以我们上一节 按键中断驱动程序为例,整个简易流程图如下:
可以看到程序在进入read函数以后,如果没有读到键值,就会一直让进程休眠,等待按键中断唤醒。
如果我们在这个基础上引入POLL机制,就可以达到下面的效果:
由上面的流程图可以看到,我们引入POLL机制以后,倘若没有按键按下,那么超过多少时间之后,进程能够继续得到执行,而不是没有按键按下,就永远休眠。
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
输入参数中的 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;
}