1. button 阻塞输入
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#define DEVICE_NAME "button"
dev_t dev;
struct cdev *cdev;
struct class *button_class;
static wait_queue_head_t button_press_wait;
static int button_press;
static unsigned int g_value;
struct button_desc {
unsigned int pin;
unsigned int val;
unsigned int irq;
};
static struct button_desc button_desc_arr[3] = {
{ .pin = S3C2410_GPF0, .val = 0x01, .irq = IRQ_EINT0 },
{ .pin = S3C2410_GPF2, .val = 0x02, .irq = IRQ_EINT2 },
{ .pin = S3C2410_GPG3, .val = 0x03, .irq = IRQ_EINT11 },
};
static irqreturn_t
buttons_irq_handler(int irq, void *dev_id)
{
struct button_desc *bd = dev_id;
unsigned int up;
g_value = bd->val;
up = s3c2410_gpio_getpin(bd->pin);
if (up)
g_value |= 0x80;
/* 唤醒button_press_wait 上的进程 */
button_press = 1;
wake_up_interruptible(&button_press_wait);
return IRQ_HANDLED;
}
static int
button_open (struct inode *inode, struct file *fp)
{
unsigned int i;
/* 设置gpio为中断模式 */
s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_EINT0);
s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_EINT11);
/* 注册中断, IRQT_BOTHEDGE : 双边缘触发 */
for (i = 0; i < sizeof(button_desc_arr)/sizeof(*button_desc_arr); i++) {
request_irq(button_desc_arr[i].irq, buttons_irq_handler,
IRQT_BOTHEDGE, DEVICE_NAME, button_desc_arr + i);
}
return 0;
}
static ssize_t
button_read (struct file *fp, char __user *user, size_t sz, loff_t *poffst)
{
/* 若没有数据,button_press 为0,则挂起本进程到等待队列 button_press_wait */
wait_event_interruptible(button_press_wait, button_press);
/* 重置 button_press */
button_press = 0;
if (copy_to_user(user, &g_value, sz)) {
printk(DEVICE_NAME " failed to copy_to_user");
return -1;
}
return 0;
}
static int
button_close (struct inode *inode, struct file *fp)
{
unsigned int i;
for (i = 0; i < sizeof(button_desc_arr)/ sizeof(*button_desc_arr); i++) {
free_irq(button_desc_arr[i].irq, button_desc_arr + i);
}
return 0;
}
static const struct file_operations button_ops = {
.owner = THIS_MODULE,
.open = button_open,
.read = button_read,
.release = button_close,
};
static int __init
button_init(void)
{
unsigned int major;
/* 1. 字符设备驱动框架部分 */
/* 1.1 申请设备号 */
if (alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME) < 0) {
printk(DEVICE_NAME "failed to alloc dev\n");
return -1;
}
/* 1.2 构建cdev,并绑定 file_operations */
cdev = cdev_alloc();
cdev->ops = &button_ops;
cdev->owner = THIS_MODULE;
/* 1.3 将cdev加到系统的哈希表中,如此才能通过设备号找到cdev */
if (cdev_add(cdev, dev, 1) < 0) {
printk(DEVICE_NAME "failed to cdev_add\n");
return -1;
}
/* 2. 为了mdev自动创建设备节点 */
/* 2.1 创建一个/sys/class/button 类 */
button_class = class_create(THIS_MODULE, DEVICE_NAME);
/* 2.2 创建/sys/class/button/buttons */
major = MAJOR(dev);
device_create(button_class, NULL, MKDEV(major, 0), "buttons");
/* 3. 初始化等待队列,以实现阻塞操作 */
init_waitqueue_head(&button_press_wait);
return 0;
}
static void __exit
button_exit(void)
{
int major;
major = MAJOR(dev);
device_destroy(button_class, MKDEV(major, 0));
class_destroy(button_class);
cdev_del(cdev);
unregister_chrdev_region(dev, 4);
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
poll
poll 机制分析
sys_poll
do_sys_poll(ufds, nfds, &timeout_jiffies) // timeout_jiffies 是输入输出参数,输入超时值,输出剩余的时间
poll_initwait(&table);
init_poll_funcptr(&pwq->pt, __pollwait); // -> table->pt->qproc = __pollwait;
i = nfds; while(i != 0) {...} // 从用户空间中将poll event参数拷贝到内核空间
fdcount = do_poll(nfds, head, &table, timeout);
for (;;) {
if (do_pollfd(pfd, pt)) { // -> mask = file->f_op->poll(file, pwait);
count++; // 调用驱动的 poll函数,如果mask>0则 count++
pt = NULL;
}
// 如果 count > 0 ,有事件,返回
// 如果 *timeout == 0 ,超时,返回
// 有信号待处理,返回
if (count || !*timeout || signal_pending(current))
break;
// 睡眠__timeout 时间, 可以被打断
__timeout = schedule_timeout(__timeout);
}
return count; // 返回事件数量
while() {...} // 将发生的事件拷贝到用户空间
驱动的poll函数
static unsigned int evdev_poll(struct file *file, poll_table *wait)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
// 调用 poll_wait , 最终将当前进程加入 驱动构建的等待队列, 但当前不会阻塞,在do_poll中若没有事件,则调用 schedule_timeout 阻塞
poll_wait(file, &evdev->wait, wait); // > p->qproc(filp, wait_address, p);
// > __pollwait(filp, wait_address, p);
init_waitqueue_entry(&entry->wait, current);
add_wait_queue(wait_address, &entry->wait);
return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) |
(evdev->exist ? 0 : (POLLHUP | POLLERR)); // 返回 POLLIN 等事件
}
等待队列的函数
wait_queue_head_t wait;
init_waitqueue_head(&wait);
wake_up_interruptible(&wait);
驱动如何使用poll机制 ?
1) file_operations 中定义 poll ,准备等待队列 waitq
2) poll(struct file *file, poll_table *wait_table) {
poll_wait(file, &waitq, wait_table); // 将本进程加入等待队列
// 3) 判断释放有事件
return event; // 无事件为0,有事件返回 POLLIN POLLOUT 等
}
poll 和 select 在驱动层面都是调用 f_op->poll ,所以驱动实现了poll,应用层可以调用poll和 select
给button驱动加上 poll
static unsigned int
button_poll (struct file *file, struct poll_table_struct *wait)
{
// 将本进程加入 button_press_wait 等待队列,但当前不会阻塞
poll_wait(file, &button_press_wait, wait);
if (button_press) // 这里不重置 button_press 因为 数据还没有读,在read后重置 button_press
return POLLIN;
return 0;
}
标签:2440,struct,东山,button,笔记,file,press,poll,wait
From: https://www.cnblogs.com/yangxinrui/p/17141257.html