首页 > 其他分享 >韦东山2440-学习笔记-字符设备驱动

韦东山2440-学习笔记-字符设备驱动

时间:2023-02-21 22:37:57浏览次数:45  
标签:2440 struct 东山 button 笔记 file press poll wait

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

相关文章

  • 学习笔记283—CT、MRI、PET三种检查的临床比较
    CT(X线电子计算机断层扫描)主要是利用X线断层扫描,电光子探测器接收,并把信号转化为数字输入电子计算机,再由计算机转化为图像,是一种无痛苦、无损伤的辅助检查工具。 MR......
  • Django学习笔记记录(整理了B站武老师的讲课课件,供大家学习)
    day1、初识DjangoPython知识点:函数、面向对象。前端开发:HTML、CSS、JavaScript、jQuery、BootStrap。MySQL数据库。Python的Web框架:Flask,自身短小精悍+第三方组......
  • Johnson 全源最短路 学习笔记
    我居然不会这玩意,过来学一下。算法简介Johnson全源最短路用于求一个带负权的图的任意两点之间的最短路,时间复杂度为\(\Theta(nm\logm)\)。算法流程考虑到\(n\)次......
  • day11 事件相关笔记
    day11事件上事件的概述事件是指代一个东西的操作被另外一个东西监听以后的一个过程(事件),这个过程可以完成对应的操作(处理函数)事件监听器是一个标准的观察者模式(observer)......
  • 第十二天笔记 事件相关
    第十二天笔记 事件流的传播流程事件流的传播有三个阶段捕获阶段从最外层找到对应的事件执行的元素目标阶段找到这个元素执行对应的事件冒泡阶段逐层向上......
  • 【线性代数复习笔记】矩阵特征值,特征向量,相似对角化与实对称矩阵
    【线性代数复习笔记】矩阵特征值,特征向量,相似对角化与实对称矩阵线代好难-_-特征值与特征向量:1.求解特征值与特征向量:​ 先计算特征多项式f(ʎ)=|ʎI-A|,求出根,再根据......
  • 阿里云-云原生技术公开课的笔记之二 Job & CronJobs、DaemonSet
      第一个是restartPolicy,在Job里面我们可以设置Never、OnFailure、Always这三种重试策略。在希望Job需要重新运行的时候,我们可以用Never;希望在失败的时候再......
  • TypeScript 入门自学笔记 — 接口的使用(六)
    目录一.函数接口参数二.函数类型接口三.函数混合类型四.对象接口(最常用)确定属性可选属性任意属性只读属性可索引接口索引访问符类接口接口继承构造函数类型type和inter......
  • Vue 学习笔记-入门(1)
    Vue入门简述​Vue(读音/vjuː/,类似于view)是一套用于构建用户界面的渐进式JavaScript框架。[5]与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用。Vue的核......
  • PMP学习笔记《第八章 项目成本管理》
    质量管理的各种名言警句:1、等级低不一定是个问题,质量未达到要求肯定是个问题;2、PDCA循环由休哈特定义,戴明改进并完善PDCA环(14条原则)即持续改进;预防胜于检查3、朱兰:质......