查看: 3947|回复: 0

[评测分享] 【ALINX AXU2CGB试用】GPIO 按键视频 + linux 驱动源码分析

[复制链接]
  • TA的每日心情
    开心
    7 天前
  • 签到天数: 597 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2021-5-26 11:02:36 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 robe.zhang 于 2021-5-29 14:28 编辑

    【ALINXAXU2CGB试用】GPIO 按键视频 + linux 驱动源码分析

    以下是笔者最终的测试电路:包含了以往两篇文章提到的 EMIO GPIO 按键,AXI GPIO按键
    key1,key2 用的是 EMIO GPIO 按键
    key3,key4 用的是 AXI GPIO 按键
    1.png

    设备树也做了调整,包含了以往两篇文章提到的三种GPIO按键配置:
    Key1 配置为中断方式 EMIO GPIO按键
    Key2 配置为轮询方式 EMIO GPIO按键
    Key3,key4 配置为轮询方式AXI GPIO 按键
    2.png

    重新编译后重启生效,运行视频:
    https://v.youku.com/v_show/id_XNTE2MTM1NDc0MA==.html
    按键1,对应键盘 r 键
    按键2,对应键盘 o 键
    按键3,对应键盘 t 键
    按键4,对应键盘 enter 键
    笔者按 1,2,2,3,4,输入ubuntu 用户名:root,并回车
    再次按 1,2,2,3,4,输入ubuntu 密码:root,并回车
    正常登录进系统了。
    按键支持 repeat,按住按键不送,会重复输入键值

    Linux 源码分析:
    驱动代码位于内核 drivers\input 目录中:
    核心代码位于一下文件中:
    input.c                        #input 子系统核心代码
    input-compat.c           # 32位兼容代码
    input-mt.c                  #多点触控代码
    input-poller.c              # 轮询代码
    ff-core.c                       #力反馈代码

    看 input.c 代码:
    1.png
    这个是 input 子系统核心层,在驱动初始化之前已经运行了。
    2491行:注册 input class类
    2.png
    3.png
    2497行:初始化 proc 文件系统
    4.png
    Input devices 对应的fops
    5.png
    Open 函数,使用了一个 seq_operations,使用序列文件的方法操作文件
    6.png
    7.png
    Input handlers 对应的fops,也是用序列文件方法操作
    8.png
    9.png
    10.png
    以上是文件系统的内容,再具体点是 procfs 文件系统的内容,自己详细了解一下

    2501行:注册字符设备,一个循环注册字符设备
    11.png
    注册时候找到 chrdevs 中的第 13项,就是input 系统的对应的字符设备,填充 structchar_device_struct 结构体,保存在 chrdevs 中
    12.png
    13.png
    Chrdevs 是个机构体数组,第 13项用于 input 子系统,next 成员下保存每一个 input 字符设备指针
    14.png
    从这里能看出来,所有的字符设备,主设备号相同就全部存在于 Chrdevs 的第n项(n是主设备号),不同此设备号的字符设备结构体指针保存在 next 指针中,形成一个链表。
    Input 设备注册之前,chrdevs[13]结构体要构建好,以便后续注册 input 设备。此处就是构建chrdev[13] 结构体,此代码先于设备驱动运行。

    核心层还提供了一下 API
    EXPORT_SYMBOL(input_free_minor);
    EXPORT_SYMBOL(input_get_new_minor);        # 申请释放 minor 次设备号
    EXPORT_SYMBOL(input_register_handle);
    EXPORT_SYMBOL(input_unregister_handle);    # 注册,注销 handle
    15.png
    注册handle结尾,运行handler->start

    EXPORT_SYMBOL(input_register_handler);
    EXPORT_SYMBOL(input_unregister_handler);          # 注册,注销handler
    EXPORT_SYMBOL(input_handler_for_each_handle);

    EXPORT_SYMBOL(input_register_device);
    EXPORT_SYMBOL(input_unregister_device);            # 注册,注销input device
    16.png
    17.png
    注册input device 函数
    2180行,计算 packet 大小
    2199,2202 行,设置获取键值,设置键值的函数
    2207行,添加 dev 设备
    2224行,attach handler
    input device 注册函数就这么多内容

    EXPORT_SYMBOL(input_enable_softrepeat);            # 使能重复输入

    EXPORT_SYMBOL(input_set_capability);           # 设置位
    EXPORT_SYMBOL(input_set_timestamp);
    EXPORT_SYMBOL(input_set_capability);           # 设置,获取时间戳

    EXPORT_SYMBOL(devm_input_allocate_device);
    EXPORT_SYMBOL(input_free_device);              # 分配释放input_dev 结构体

    devm_input_device_match 函数好简单,直接比较两个指针是不是相等
    18.png
    分配 input device,dev类是刚才的 input_class,类型是
    19.png
    EXPORT_SYMBOL(input_open_device);
    EXPORT_SYMBOL(input_close_device);
    EXPORT_SYMBOL(input_flush_device);             # 打开,关闭,冲刷input_device
    有 open 运行 open函数,有 poller运行 poller并开启轮询工作队列
    20.png
    21.png
    Close ,flush 更简单
    22.png
    23.png
    EXPORT_SYMBOL(input_release_device);          # 释放 input_device
    EXPORT_SYMBOL(input_grab_device);
    EXPORT_SYMBOL(input_set_abs_params);
    EXPORT_SYMBOL(input_alloc_absinfo);            # abs 设备的设置,比如触控板等

    EXPORT_SYMBOL(input_inject_event);
    EXPORT_SYMBOL(input_event);                # 发送事件
    两个发送事件函数都调用了 input_handle_event
    24.png
    25.png
    添加时间戳,调用 input_pass_values 发送
    26.png
    调用 input_to_handler
    27.png
    看 drivers\input\evdev.c 中的events 函数的实现
    28.png
    29.png
    30.png
    最终是传递事件,发送信号,唤醒进程去处理的。异步处理

    核心层也实现了 autorepeat,使用时钟实现的
    31.png
    input_register_device 函数中 2196行,input_enable_softrepeat 初始化的
    32.png
    33.png
    上报事件,设置定时器,过一会继续上报事件,实现 softrepeat
    34.png
    核心层就这么多,类似一个库,做了一些 input 基础的初始化,提供了一些接口给其他驱动调用

    看gpio-keys驱动,这个是中断方式的gpio-keys驱动 :驱动代码位于 drivers\input\keyboard\gpio_keys.c文件中
    35.png
    774 行:如果 pdata 不存在就去解析设备树
    780行,分配 ddata
    787行,分配 keymap
    793行,分配 input dev,下面是初始化input
    825 行,是个循环,设置 gpio 按键,主要是分析子节点,设置gpio,中断,delay work
    851行,注册 input dev
    858行,休眠唤醒初始化
    36.png
    37.png
    38.png
    39.png
    40.png
    41.png
    按键工作在中断模式,来了中断就上报事件,延迟一会就释放按键,释放也会上报
    42.png
    还有一个轮询模式的Gpio 按键驱动:代码位于drivers\input\keyboard\gpio_keys_polled.c 文件中
    43.png
    44.png
    45.png
    46.png
    代码基本类似,360行,input_register_polled_device注册 polled_device 函数中 307 初始化 delay work 为 input_polled_device_work
    47.png
    input_polled_device_work 调用了 dev->poll后继续加入工作队列,继续轮询
    48.png
    dev->poll 在驱动 probe时候被初始化为 gpio_keys_polled_poll,
    49.png
    gpio_keys_polled_poll 的功能就是轮询所有按键,上报事件
    50.png
    轮询方式的 gpio 驱动工作方式就是轮训一编所有按键,加入工作队列,延迟一会,再次轮询再次延迟,不停循环

    input_register_polled_device 函数第 314行,input_open_polled_device
    51.png
    一旦打开设备,就开始上报事件,加入工作队列,开始轮询了
    52.png
    关闭设备,就取消工作队列,不再轮询
    53.png
    按键驱动,核心是input子系统,也涉及到了gpio,中断,timer,work,文件系统等等,看驱动需要点内核基础,操作系统基础,需要一定的内核代码阅读量,内核各种api都要混个脸熟,需要一定的积累,需要分析大的框架和项目代码的能力,要求还是挺高的
    有人总想投机,手把手教,包教包会,不存在的,没有速成法哈。驱动是内核级别的编程,在子系统框架下编程,学内核学框架就会了。Input 子系统看懂,是用好 gpio 按键驱动的基本功之一。


    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-11-27 00:24 , Processed in 0.122103 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.