002输入子系统驱动

摘要:
输入子系统概念介绍(第13课/第1节)回顾第三个驱动程序(中断模式下的关键驱动程序)和测试程序,发现存在一些缺点:此驱动程序不能用于其他人编写的现成应用程序(如QT),因为为其他人编写应用程序肯定不会打开您的“/dev/third_creddev”。其他人打开一些现成的设备(如/dev/tty)。甚至其他人也不打开设备,而是直接调用scanf()获取键输入。因此,仅以前编写的驱动程序

输入子系统概念介绍(第十三课/第一节)

回顾第三个驱动程序(中断方式的按键驱动程序)和测试程序,发现有一些缺点:
这个驱动程序没办法用在别人写的现成的应用程序上(比如:QT),因为别人写的应用程序肯定不会来打开你这个"/dev/third_chrdev"。别人打开的是一些现成的设备(比如:/dev/tty),甚至别人都不打开设备,而是直接调用 scanf() 就可以获取按键的输入。
002输入子系统驱动第1张
所以,以前写的驱动程序只能自己使用。如果要写一个通用的驱动程序,让其它的应用程序无缝(无缝就是指不需要修改别人的应用程序)的移植到单板上,就需要使用现成的驱动程序(在内核现成的驱动程序里面把自己的东西融合进去)。这个现成的驱动程序就是输入子系统(input子系统)。
要想把自己东西融合进这个驱动程序,就得把这个输入子系统的框架弄清楚。
以前我们自己写驱动时的步骤,在input子系统里面也会有,只不过是系统已经做好了。
002输入子系统驱动第2张

1、最上层/核心层(drivers/input.c):

首先,看一个驱动程序是从"入口函数"开始查看
002输入子系统驱动第3张
002输入子系统驱动第4张
以前注册字符设备驱动的函数是自己写的,现在是内核里面已经有了
来看看 "input_fops" 这个结构,这个结构里面只有一个 open 成员。
002输入子系统驱动第5张
疑问,这不是输入子系统吗,你不是想要读按键吗,怎么会只有一个 open 函数呢?可以猜想这个 open 函数肯定做了某些工作。进入这个去看看
002输入子系统驱动第6张
定义一个"input_handler"的结构体指针并指向以打开文件的次设备在"input_table"这个数组中找到一项
002输入子系统驱动第7张
然后把(handler)里面的(fops)结构指针赋值给(new_fops)
002输入子系统驱动第8张
把(new_fops)赋值给此函数参数的(file)中的(f_op)结构
002输入子系统驱动第9张
调用(file_operation)结构中的 open 成员
002输入子系统驱动第10张
这样下来最后用到的是(struct input_handler)类型的指针(handler)中的(fops),input.c是一个中转的作用。最终还是会用到(input_table[])这个数组。
以后app来读的时候最终就会调用(file->f_op_read),但是这个(input_table[])是如何定义的,又是怎样被构造的?
002输入子系统驱动第11张
被static修饰的全局变量只能在该文件被使用,搜索一下
002输入子系统驱动第12张
那这个(input_register_handler)函数又是被谁来调用的呢?
002输入子系统驱动第13张
002输入子系统驱动第14张
这些handler向上注册
002输入子系统驱动第15张

接下来看看这个读的过程

002输入子系统驱动第16张
查看这个结构的原型
002输入子系统驱动第17张
它是一个(input_handler)类型的结构体,我们来看看的它的定义
002输入子系统驱动第18张
以前这个file_operation使我们自己构造的,现在是系统已经做好了。所以(64>>5)也就是64除以32等于2,放在(input_table[2])里
002输入子系统驱动第19张
002输入子系统驱动第20张

这些handler的会放入input.c中的input_table中,软件方面会向核心层注册handler,硬件方面会向核心层注册device。
002输入子系统驱动第21张
一边是(handler)软件处理者,一边是(device)设备,那么两边应该如何建立连接呢?
(input_handler)中有一个(id_table),表示能支持哪些设备。
002输入子系统驱动第22张
当我们注册(handler)和(device)时,这两者就会两两比较,看(handler)能否支持这个设备,若支持则会调用(input_handler)结构中的(connet)函数建立连接
002输入子系统驱动第23张

看看谁会调用这(input_register_device),搜索一下(有各种按键,各种鼠标等等)
002输入子系统驱动第24张
分析一下(input_register_device)会做哪些事情
002输入子系统驱动第25张
放入链表
002输入子系统驱动第26张
对于每一个(input_handler),都调用(input_attach_handler)这个函数,它会根据input_handler的id_table判断能否支持这个设备
002输入子系统驱动第27张

再来分析一下(input_register_handler)
002输入子系统驱动第28张
首先先放在数组里
002输入子系统驱动第29张
然后同样也放入链表里
002输入子系统驱动第30张
紧接着遍历 input_dev_list(全局链表,连接所有的 input_dev) 上的dev,并调用 input_attach_handler来进行输入设备和软件处理的关联。
002输入子系统驱动第31张
所以不管是先注册右边的(handler)还是左边的(device),最终都会调用(input_attach_handler)来进行匹配
现在我们来看看(input_attach_handler)这个函数到底做了什么?
002输入子系统驱动第32张

以(evdev.c)为例子,看看是如何建立连接的(看 connet 函数),可能不同的(handler)有自己不同的方式
分析(evdev_connet)函数
002输入子系统驱动第33张
分配一个(evdev)结构体,但里面包含(input_handle)结构体
002输入子系统驱动第34张
看看这个(evdev)结构体
002输入子系统驱动第35张
002输入子系统驱动第36张
分配(input_handle)后,下面进行设置
002输入子系统驱动第37张
然后注册这个(input_handle)
002输入子系统驱动第38张
看看这个(handle)它是如何注册的
002输入子系统驱动第39张
002输入子系统驱动第40张

app:read

应用程序来读,最终会调用(input_handler->fops->read)
002输入子系统驱动第41张
怎么读按键?
002输入子系统驱动第42张
既然有休眠,那么谁来唤醒它呢?它在(evdev->wait)上休眠,所以在该文件中搜索这个关键字就能找到
002输入子系统驱动第43张
这个(evdev_event)事件处理函数是怎样被触发的,也就是被谁调用的?
猜测:应该是硬件相关的代码,在(input_dev)那一层被触发
回过头看看硬件那一层,以(gpio_keys.c)它作为例子,分析
002输入子系统驱动第44张
002输入子系统驱动第45张
接着分析(input_event)函数
002输入子系统驱动第46张
所以,这个事件处理函数就是被(input_dev)这一层调用的。

写符合输入子系统框架的程序(第十三课/第二节)

首先要明白处理函数那一层系统已经做好了,所以我们只要写左边的硬件层,然后注册,就可以调用与右边层建立连接,进而可以调用右边的函数
看看我们以前自己写驱动程序的步骤:
002输入子系统驱动第47张

在上一节我们已经弄清楚这个子系统的框架了,那么怎么写符合输入子系统框架的驱动程序?

  1. 分配一个input_dev结构体
  2. 设置
  3. 注册
  4. 硬件相关的代码,比如在中断服务程序里上报事件

以(gpio_keys.c)为例看看别人是怎么写的,然后模仿
首先看看别人是如何分配一个input_dev结构体
002输入子系统驱动第48张
设置这个结构体,先看看这个结构体需要设置些什么
002输入子系统驱动第49张
设置产生哪类事件,哪类事件下的哪些事件
002输入子系统驱动第50张
注册
002输入子系统驱动第51张

下面我们自己来写驱动程序

1.分配一个input_dev结构体
002输入子系统驱动第52张
002输入子系统驱动第53张
2.1 设置能产生哪类事件
002输入子系统驱动第54张
设置能产生按键类的哪些事件,以前写的按键值只有我们自己知道是什么意思,现在我们要写一个通用的驱动程序
002输入子系统驱动第55张

002输入子系统驱动第56张
3.注册,注册后就会放入input_dev的链表中,然后会遍历input_handler链表通过handler的id_table一个一个的进行匹配比较,匹配成功后会建立连接(调用connet函数创建一个input_handle结构体)
002输入子系统驱动第57张
4.硬件相关的操作(注册中断,添加定时器防抖)
002输入子系统驱动第58张

002输入子系统驱动第59张
看看这个中断函数(button_irq)
002输入子系统驱动第60张
按下按键10ms后执行的函数
以前有按键按下时,会根据按键值进行处理,现在只需要上报事件即可,只需调用到(input.c)中的(input_event)函数,最终就会调用handler里的event函数
002输入子系统驱动第61张
这个上报事件最终是执行handler里的event函数,这个event函数会把传入的参数记录下来,最后唤醒中断
002输入子系统驱动第62张
在出口函数里面释放申请的资源
002输入子系统驱动第63张

测试:
在装载驱动之前先看看(/dev)下的event有哪些
002输入子系统驱动第64张
装载驱动
002输入子系统驱动第65张
那么这个event是怎么来的呢?
002输入子系统驱动第66张
第一步:创建主设备号为13
002输入子系统驱动第67张
第二步:创建类
002输入子系统驱动第68张
类名是input
002输入子系统驱动第69张
第三步:在类里面创建次设备号,进而通过mdev创建设备结点
002输入子系统驱动第70张
002输入子系统驱动第71张
002输入子系统驱动第72张

调试方法一:hexdump /dev/event1 表示(open(/dev/event1),然后读取内容,紧接着用十六进制显示出来),第一排和第三排分别表示按下和松开,第二排和第四排表示上传的同步事件

002输入子系统驱动第73张
002输入子系统驱动第74张
问:这些值是怎么来的呢?答:在(evdev.c)中(evdev_read)函数会调用下面的函数
002输入子系统驱动第75张
这个函数的原型其实就是(copy_to_usr)
002输入子系统驱动第76张
拷贝的内容
002输入子系统驱动第77张
002输入子系统驱动第78张

调试方法二:cat /dev/tty1

(cat /dev/tty1)它主设备号是4,此设备号是1,它是通过(tty_io.c)里面的驱动程序来访问(keybord.c)
这个(tty_io.c)比较复杂,暂且不去分析,现在大概来分析(keybord.c)
在入口函数调用这个函数
002输入子系统驱动第79张

002输入子系统驱动第80张
002输入子系统驱动第81张
若上报事件,则会从input_dev里的h_list指向的input_handle的链表里找到一项(因为每一个connet函数会创建一个input_handle),所以会有多项(input_handle)结构。找到后调用handler里面的event函数(event.c的handler调用它的input_handler结构中的event函数,同理,keybord.c的handler则调用它的input_handler结构中的event函数)。
002输入子系统驱动第82张
使用(cat /dev/tty1)时,它不是从input子系统进入,而是通过(tty_io.c)与(keybord.c)的联系进入的

按键驱动程序(eighth_input.c)

/*参考F:编程之路编程之路(Linux)link_to_term1_and_term2kernelslinux-2.6.22.6driversinputkeyboardgpio_keys.c*/

#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>

#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>


static struct input_dev *buttons_dev;
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer;
static int irq_val;

static struct pin_desc{
        int irq;
        char *name;
        unsigned int pin;
        unsigned int key_val;
    };
static struct pin_desc pins_desc[4] = {
        {IRQ_EINT0, "S2", S3C2410_GPF0,  KEY_L},
        {IRQ_EINT2, "S3", S3C2410_GPF2,  KEY_S},
        {IRQ_EINT11,"S4", S3C2410_GPG3,  KEY_ENTER},
        {IRQ_EINT19,"S5", S3C2410_GPG11, KEY_LEFTSHIFT},
    };

static irqreturn_t button_irq(int irq, void *dev_id)
{
    irq_pd = (struct pin_desc *)dev_id;
    irq_val = irq;
    mod_timer(&buttons_timer, jiffies+HZ/100);    
    return IRQ_HANDLED;
}

static void buttons_timer_function(unsigned long data)
{
    struct pin_desc *pindesc = irq_pd;
    unsigned char pinval;

    if(!pindesc)
        return;
    //printk("irq_val = %d

", irq_val);
    pinval = s3c2410_gpio_getpin(pindesc->pin);    //读取对应的引脚的状态值
    if(pinval)
    {
        /* 最后一个参数:0-松开            1-按下 */
        input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
        input_sync(buttons_dev);    //上报同步事件
    }
    else
    {
        /* 按下 */
        input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
        input_sync(buttons_dev);
    }
}

static int buttons_init(void)
{
    int i;
    /* 1.分配一个input_dev结构体 */
    buttons_dev = input_allocate_device();

    /* 2.设置 */
    /* 2.1.能产生哪类事件 */
    set_bit(EV_KEY, buttons_dev->evbit);
    set_bit(EV_REP, buttons_dev->evbit);

    /* 2.2能产生这类操作里的哪些事件:L,S,ENTRY,LEFTSHIT */
    set_bit(pins_desc[0].key_val, buttons_dev->keybit);
    set_bit(pins_desc[1].key_val, buttons_dev->keybit);
    set_bit(pins_desc[2].key_val, buttons_dev->keybit);
    set_bit(pins_desc[3].key_val,  buttons_dev->keybit);

    /* 3.注册 */
    input_register_device(buttons_dev);

    /* 4.硬件相关的操作 */
    /* 4.1.定时器初始化(防抖动) */
    init_timer(&buttons_timer);
    buttons_timer.function = buttons_timer_function;
    add_timer(&buttons_timer);

    /* 4.2.注册中断 */
    for(i=0; i<4; i++)
    {
        request_irq(pins_desc[i].irq,   button_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
    }
    return 0;
}

static void buttons_exit(void)
{
    int i;
    for(i=0; i<4; i++)
    {
        free_irq(pins_desc[i].irq, &pins_desc[i]);
    }

    del_timer(&buttons_timer);
    input_unregister_device(buttons_dev);
    input_free_device(buttons_dev);
}

module_init(buttons_init);

module_exit(buttons_exit);

MODULE_LICENSE("GPL");

测试:
002输入子系统驱动第83张
在串口上等待输入的程序,根据我们输入内容执行的程序就是(shell)程序。
可以查看我们的(shell)程序打开了哪些文件
002输入子系统驱动第84张

以前是从串口上得到输入,现在把它的0号文件(标准输入文件改为tty1),就可以从键值得到输入。
002输入子系统驱动第85张
不足点:按下后不松开不能重复打印该键值。加上这一句就可以了。
002输入子系统驱动第86张
那么它是如何实现的呢?
首先当有按键按下并消抖后会上报事件
002输入子系统驱动第87张
根据事件类型判断是否为重复类事件
002输入子系统驱动第88张

002输入子系统驱动第89张
在注册(input_register_device)就会初始化一个定时器
002输入子系统驱动第90张
002输入子系统驱动第91张
002输入子系统驱动第92张

再次测试:
002输入子系统驱动第93张

补充:环形缓冲区(本质是一个数组)
002输入子系统驱动第94张
002输入子系统驱动第95张

<wiz_tmp_tag contenteditable="false" style="display: none;">

 
 
 
 

免责声明:文章转载自《002输入子系统驱动》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇黑客入侵的常法linux命令学习(2):wc 命令下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

linux下安装composer以及使用composer安装laravel

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nianzhi1202/article/details/72770099 一.安装composer之前首先要确定您的机器上已经安装了php,并可以通过php来执行命令。使用php –v 测试 当然要直接使用php 还需确定php命令已经设置在全局命令中,否...

关于Linux上的SSH服务无法启动,提示“/var/empty/sshd must be owned by root and not group or world-writable”错误

首先通过物理终端进入到linux上,手工检查ssh发现没运行# /etc/init.d/sshd statussshd is stopped 手动启动服务,发现报告权限错误。# /etc/init.d/sshd start Starting sshd:/var/empty/sshd must be owned by root and not group o...

linux系统日志__ratelimit: N callbacks suppressed

报错 今天线上遇到故障,php进行因为段错误退出了,系统日志中的kernel报错如下: Feb 25 22:25:11 web_server_01 kernel: __ratelimit: 250 callbacks suppressed Feb 25 22:25:11 web_server_01 kernel: php-fpm[25942]: segf...

机器学习策略(二)---误差分析、训练集与开发测试集不相配怎么办、迁移学习/多任务学习、端到端深度学习

1.误差分析 1.1 误差分析 当算法还没有到达human level时,你需要去分析算法带来的误差,并且决定接下去应该如何优化,从而减小误差。这个过程叫做误差分析。 将设在猫狗分类的任务上,若dev set上的error有10%,此时你需要找出这些错误的case,然后统计猫错分成狗,和狗错分成猫各自的比例,如果你发现:狗错分成猫的比例是5%猫错分成狗的比...

嵌入式linux系统的构建

前期工作:a.配置好tftp服务器:在嵌入式的童年中有介绍 b.开发板可以pc,linux 三者可以互相ping通 c.配置好nfs服务器:同样在嵌入式的童年中有介绍 一.嵌入式linux内核的制作(这里使用的国嵌提供的linux-ok6410内核,附带内核配置文件config-file) a.进入内核目录清除原有配置和中间文件: make clean b...

通过Bochs分析Lilo启动Linux内核的过程

1. Bochs调试 参考:http://www.cnblogs.com/long123king/p/3414884.html http://bochs.sourceforge.net/cgi-bin/topper.pl?name=New+Bochs+Documentation&url=http://bochs.sourceforge.net/do...