Linux驱动开发11之input子系统驱动

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangdapao12138/article/details/82055019

1.为什么要学习input子系统

这里说要说明下:input子系统是独立于platform平台总线的。既然和总线无关,那么就是一类设备的驱动模型,就像misc设备驱动模型一样,所以必须要学习!

2.什么是input子系统

输入的一类设备。用于各种不同输入类设备的管理。linux系统中键盘、鼠标、触摸屏等输入设备的支持都通过、或越来越倾向于标准的input输入子系统。因为input子系统已经完成了字符驱动的文件操作接口,所以编写驱动的核心工作是完成input系统留出的接口,工作量不大。但如果你想更灵活的应用它,就需要好好的分析下input子系统了。

3.怎样验证一个input子系统完成注册并成功驱动

答案:可以使用应用层API调用编写简单的调试代码。举例如下:

 (1)应用层操作驱动有2条路:/dev目录下的设备文件,/sys目录下的属性文件

(2)input子系统用的/dev目录下的设备文件,具体一般都是在 /dev/input/eventn

(3)用cat命令来确认某个设备文件名对应哪个具体设备。我在自己的ubuntu中实测的键盘是event1,而鼠标是event3,我们使用应用层的代码来测试input子系统的文件。

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <linux/input.h>

#include <string.h>

#define DEVICE_KEY          "/dev/input/event1"

#define DEVICE_MOUSE        "/dev/input/event3"

#define X210_KEY            "/dev/input/event1"

 

int main(void)

{

    int fd = -1, ret = -1;

    struct input_event ev;

   

    // 1步:打开设备文件

    fd = open(X210_KEY, O_RDONLY);

    if (fd < 0)

    {

        perror("open");

        return -1;

    }

   

    while (1)

    {

        // 2步:读取一个event事件包

        memset(&ev, 0, sizeof(struct input_event));

        ret = read(fd, &ev, sizeof(struct input_event));

        if (ret != sizeof(struct input_event))

        {

            perror("read");

            close(fd);

            return -1;

        }

       

        // 3步:解析event包,才知道发生了什么样的输入事件

        printf("-------------------------\n");

        printf("type: %hd\n", ev.type);

        printf("code: %hd\n", ev.code);

        printf("value: %d\n", ev.value);

        printf("\n");

    }

   

   

    // 4步:关闭设备

    close(fd);

   

    return 0;

}

4.input设备文件从哪里开始注册和挂接的?

5.Input子系统的分层

一个输入事件,如鼠标移动、键盘按下等通过Driver->Inputcore->Event handler->userspace的顺序到达用户控件的应用程序,具体的流程可以用下图:

linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。

对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。即将底层的硬件输入转化为统一事件形式,向输入核心(Input Core)汇报

对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。承上启下。为驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/Proc下产生相应的设备信息

对于事件处理层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。和用户空间交互。(Linux中在用户空间将所有的设备都当初文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)

6.输入核心层的input.c

6.1 事件:struct input_event,这里的事件是我们对外设的操作,比如按键一次a可能产生数个input_event数据。

00026: struct input_event {
00027: struct timeval time;     //按键时间
00028: __u16 type;           //事件类型
00029: __u16 code;          //要模拟成什么按键
00030: __s32 value;          //是按下还是释放
00031: };

Type:指事件类型,常见的事件类型有:

Code:指事件的代码,也就是事件类型中的细分

每个类型都有众多细分事件,比如同步事件有三个,键盘事件有255个等。

Value:指事件的值。如果事件的类型代码是EV_KEY,当按键按下时值为1,松开时值为0;如果事件的类型代码是EV_REL,value的正数值和负数值分别代表两个不同方向的值.

 

6.2.input类设备驱动开发方法

(1)输入事件驱动层和输入核心层不需要动,只需要编写设备驱动层

(2)设备驱动层编写的接口和调用模式已定义好,驱动工程师的核心工作量是对具体输入设备硬件的操作和性能调优。

(3)input子系统不算复杂,学习时要注意“标准模式”四个字。

 7.输入核心层源码分析

7.1.核心模块注册input_init

(1)class_register    //类的注册

(2)input_proc_init //proc文件的创建

(3)register_chrdev         //字符设备驱动,等待device的注册然后mach
01937: static int __init input_init(void)
01938: {
01939: int err;
01941: input_init_abs_bypass();
01942:
01943:
err = class_register(&input_class);  //调用class_register()函数先注册了一个名为input的类。所有input device都属于这个类。在sysfs中表现就是,所有input device所代表的目录都位于/dev/class/input下面。input_class类的定义只是一个名字。
01944: if (err) {
01945: printk(KERN_ERR "input: unable to register input_dev class\n");
01946: return err;
01947: }
01948:
01949:
err = input_proc_init();   //调用input_proc_init()/proc下面建立相关的交互文件。
01950: if (err)
01951: goto ¯fail1;
01952:
01953:
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);   //调用register_chrdev()注册了主设备号为INPUT_MAJOR(13)。次设备号为0255的字符设备。它的操作指针为input_fops。在这里,可以看到所有主设备号13的字符设备的操作最终都会转入到input_fops中。例如/dev/input/event0~/ dev/input/event4的主设备号为13,对其的操作会落在input_fops中。使用:cat /proc/devices可以看到


01954: if (err) {
01955: printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR
01956: goto ¯fail2;
01957: }
01958:
01959:
return 0;
01960:
01961:
fail2: input_proc_exit();
01962: fail1: class_unregister(&input_class);
01963: return err;
01964: } ? end input_init ?

01463: struct class input_class = {
01464: .name = "input",
01465: .devnode = input_devnode,
01466: };

01924: static const struct file_operations input_fops = {
01925: .owner = THIS_MODULE,
01926: .open = input_open_file,
01927: };

7.2、设备驱动层的接口函数

(1)input_allocate_device     //设备驱动层的必要数据结构的初始化,内存空间的申请等

(2)input_set_capability                  //设备的能力

(3)input_register_device     //input类型的变量注册进去

01611: int input_register_device(struct input_dev *dev)
01612: {
01613: static atomic_t input_no = ATOMIC_INIT(0);     // //这个原子变量,代表总共注册的input设备,每注册一个加1,因为是静态变量,所以每次调用都不会清零的
01614: struct input_handler *handler;
01615: const char *path;
01616: int error;
01618: /*
01618: Every input device generates EV_SYN/SYN_REPORT events. */
01619: __set_bit(EV_SYN, dev->evbit);    //调用__set_bit()函数设置input_dev所支持的事件类型。事件类型由input_devevbit成员来表示,在这里将其EV_SYN置位,表示设备支持所有的事件。注意,一个设备可以支持一种或者多种事件类型。/解析说的很明白,因为每个input device 都会产生EV_SYN/SYN_REPORT 时间,所以就放在一起去设置了。set_bit()告诉input输入子系统支持哪些事件,哪些按键。
01620:
01621:
/*
01621: KEY_RESERVED is not supposed to be transmitted to userspace. */
01622: __clear_bit(KEY_RESERVED, dev->keybit);
01623:
01624:
/*
01624: Make sure that bitmasks not mentioned in dev->evbit are clean.
01625: input_cleanse_bitmasks(dev);
01626:
01627:
/*
01628: * If delay and period are pre-set by the driver, then auto
01628: repeating
01629: * is handled by the driver itself and we don't do it in in
01629: put.c.
01630: */
01631: init_timer(&dev->timer); // // 这个内核定时器是为了重复按键而设置的
01632: if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
01632: {
01633: dev->timer.data = (long) dev;
01634: dev->timer.function = input_repeat_key;
01635: dev->rep[REP_DELAY] = 250;
01636: dev->rep[REP_PERIOD] = 33; // //如果没有定义有关重复按键的相关值,就用内核默认的
01637: }
01639: if (!dev->getkeycode)
01640: dev->getkeycode = input_default_getkeycode;    
01642: if (!dev->setkeycode)
01643: dev->setkeycode = input_default_setkeycode;
01644: // //以上设置的默认函数由input核心提供
01645: dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

// //设置input_devdevice的名字,这个名字会在/class/input中出现
01648: error = device_add(&dev->dev);     // //device加入到linux设备模型中去
01649: if (error)
01650: return error;
01652: path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL
01652: );
01653: printk(KERN_INFO "input: %s as %s\n",dev->name ? dev->name : "Unspecified device",path ? path : "N/A");
01655: kfree(path);       // //这个得到路径名称,并打印出来
01657: error = mutex_lock_interruptible(&input_mutex);
01658: if (error) {
01659: device_del(&dev->dev);
01660: return error;
01661: }
01663: list_add_tail(&dev->node, &input_dev_list);//将新分配的input设备连接到input_dev_list链表上
01665: list_for_each_entry(handler, &input_handler_list, node)
01666: input_attach_handler(dev, handler);    

//遍历input_handler_list链表,配对 input_dev input_handler  ,源码中list_for_each_entry(handler, &input_handler_list, node) 后面是没有分号的,这是一个宏函数,展开后是一个for循环结构,对每一次循环,执行input_attach_handler(dev,handler). //input_attach_handler 这个函数是配对的关键,下面将详细分析
01668: input_wakeup_procfs_readers();   // // proc文件系统有关,暂时不考虑
01670: mutex_unlock(&input_mutex);
01672: return 0;
01673: } ? end input_register_device ?

7.3.handlerdevice的匹配

(1)input_attach_handler

00774: static int input_attach_handler(struct input_dev
00774: *dev, struct input_handler *handler)
00775: {
00776: const struct input_device_id *id;
00777: int error;
00779: id = input_match_device(handler, dev); //返回匹配的id,类型是struct input_device_id
00780: if (!id)
00781: return -ENODEV;
00783: error = handler->connect(handler, dev, id); //配对成功调用handlerconnect函数,这个函数在事件处理器中定义,主要生成一个input_handle结构,并初始化,还生成一个事件处理器相关的设备结构
00784: if (error && error != -ENODEV)
00785: printk(KERN_ERR "input: failed to attach handler %s to device %s, ""error: %d\n",handler->name, kobject_name(&dev->dev.kobj), error);
00790: return error;
00791: }

00733: static const struct input_device_id *  input_match_device(struct input_handler *handler struct input_dev *dev)
00735: {
00736: const struct input_device_id *id;
00737: int i;
00739: for (id = handler->id_table; id->flags || id->driver_info; id++) {
00741:           if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) //匹配总线id
00742:                  if (id->bustype != dev->id.bustype)
00743:           continue;
00745:           if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) //匹配生产商id
00746:                  if (id->vendor != dev->id.vendor)
00747:                  continue;
00749:           if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) //匹配产品id 
00750:                  if (id->product != dev->id.product)
00751:                  continue;
00752:
00753:          
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) //匹配版本 
00754:                  if (id->version != dev->id.version)
00755:                  continue;

//匹配idevbitinput_devevbit的各个位,如果不匹配则continue,数组中下一个设备
00757: MATCH_BIT(evbit, EV_MAX);
00758: MATCH_BIT(keybit, KEY_MAX);
00759: MATCH_BIT(relbit, REL_MAX);
00760: MATCH_BIT(absbit, ABS_MAX);
00761: MATCH_BIT(mscbit, MSC_MAX);
00762: MATCH_BIT(ledbit, LED_MAX);
00763: MATCH_BIT(sndbit, SND_MAX);
00764: MATCH_BIT(ffbit, FF_MAX);
00765: MATCH_BIT(swbit, SW_MAX);
00766:
00767:
if (!handler->match || handler->match(handler,
00767: dev))
00768: return id;
00769: } ? end for id=handler->id_table;... ?
00770:
00771:
return NULL;
00772: } ? end input_match_device ?

可以看到这么多步的目的除了初始以及添加input_dev到链表,就是为了去匹配 input_handler_list 中对应的handler ,匹配的最终是需要比对handler以及input_dev中的 id,其中input_dev 中的id类型为 input_id。这跟上面 input_handler 结构里面的 input_device_id 匹配id 变量,来确认 handler!在最开始的时候就有提到,整个input输入体系,分三个层次,现在的input核心层做的事就是:在硬件驱动层调用 input_register_device ,往内核注册驱动的同时,根据硬件的相关id去匹配 适用的事件处理层(input_handler)!这里匹配上之后就会调用对应 input_handler connect 函数。

7.4.事件驱动层的接口函数evdev.c

上面介绍了input核心的职责,其中有说道注册input设备时会去匹配已有的事件处理器handler

而这个handler也是存放在一个链表里面的,这里介绍下input子系统中的事件处理input_handler机制.

00897: static int __init evdev_init(void)
00898: {
00899: return input_register_handler(&evdev_handler);
00900: }

这个初始化就是往input核心中注册一个input_handler类型的evdev_handler,调用的是input.c提供的接口,input_handler结构前文有介绍,看下evdev_handler的赋值:

00887: static struct input_handler evdev_handler = {
00888: .event = evdev_event,
00889: .connect = evdev_connect,
00890: .disconnect = evdev_disconnect,
00891: .fops = &evdev_fops,
00892: .minor = EVDEV_MINOR_BASE,
00893: .name = "evdev",
00894: .id_table = evdev_ids,
00895: };

//赋值各个函数指针!

可以看到上面的evdev handler 就是调用这个接口注册到input核心中的,同样evdev.c同目录下也还有其它的handler,有兴趣可以看看它们的init函数,都是会调用到这个接口去注册的.

01714: int input_register_handler(struct input_handler *handler)
01715: {
01716: struct input_dev *dev;
01717: int retval;
01719: retval = mutex_lock_interruptible(&input_mutex);
01720: if (retval)
01721: return retval;
01723: INIT_LIST_HEAD(&handler->h_list);
01725: if (handler->fops != NULL) {
01726: if (input_table[handler->minor >> 5]) {
01727: retval = -EBUSY;
01728: goto ¯out;
01729: }
01730: input_table[handler->minor >> 5] = handler; //input.c定义的全局handler 数组赋值,evdev handler的次设备号为64,这里除以32,赋值在input_table[2] 
01731: }
01733: list_add_tail(&handler->node, &input_handler_list); //添加进handler 链表
01735: list_for_each_entry(dev, &input_dev_list, node) //同样遍历input_dev这个链表,依次调用下面的input_attach_handler去匹配input_dev,这个跟input_dev注册的时候的情形类似 
01736: input_attach_handler(dev, handler);
01738: input_wakeup_procfs_readers();
01740: out:
01741: mutex_unlock(&input_mutex);
01742: return retval;
01743: } ? end input_register_handler ?

这是保存注册到input核心中的handler数组,因为在之前input注册的时候注册的字符设备主设备号为13.字符设备的次设备号为0255,可以有256个设备,

这里后面会看到一个handler可以connect处理32input设备,所以input体系中,最多拥有8handler.这个匹配过程和上一篇中的过程是一样的,最后匹配上的话会调用匹配上的handler connect指针指向的函数。

00774: static int input_attach_handler(struct input_dev*dev, struct input_handler *handler)
00775: {
00776: const struct input_device_id *id;
00777: int error;
00779: id = input_match_device(handler, dev);
00780: if (!id)
00781: return -ENODEV;
00783: error = handler->connect(handler, dev, id);
00784: if (error && error != -ENODEV)
00785: printk(KERN_ERR "input: failed to attach handler %s to device %s, ""error: %d\n",
00788: handler->name, kobject_name(&dev->dev.kobj ), error);
00790: return error;
00791: }

另外可以注意的是evdev是匹配所有设备的,因为:

00880: static const struct input_device_id evdev_ids[] = {
00881: { .driver_info = 1 }, /* Matches all devices */
00882: { }, /* Terminating zero entry */
00883: };

如果没有特定的handler添加进handler链表,那么在匹配的时候,只要有这个evdevhandler,最后都会匹配到evdev,这个具体可以去看看上篇的匹配过程。

注册的evdev_handlerconnect指向的函数为evdev_connect

00807: static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
00808: const struct input_device_id *id)
00809: {
00810: struct evdev *evdev;
00811: int minor;
00812: int error;
00814: for (minor = 0; minor < EVDEV_MINORS; minor++)
00815: if (!evdev_table[minor])
00816: break;
00818: if (minor == EVDEV_MINORS) {
00819: printk(KERN_ERR "evdev: no more free evdev devices\n");
00820: return -ENFILE;
00821: }

// 可以看到这里evdev handler匹配连接好的设备都以evdev 类型存在这个evdev_table数组的,这个数组大小为32个,这就是我上面说到的,为什么只有8handler 

//这里是判断evdev32个位置中是否有空
00823: evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //为上面定义的*evdev分配内存空间
00824: if (!evdev)
00825: return -ENOMEM;
00827: INIT_LIST_HEAD(&evdev->client_list); //以下都是对这个 evdev的初始化了
00828: spin_lock_init(&evdev->client_lock);
00829: mutex_init(&evdev->mutex);
00830: init_waitqueue_head(&evdev->wait);
00832: dev_set_name(&evdev->dev, "event%d", minor); //给这个evdev命名 
00833: evdev->exist = 1;
00834: evdev->minor = minor; // minor为索引赋值
00836: evdev->handle.dev = input_get_device(dev); //evdev中的handle变量的初始化 ,后面分析这个handle ,这里面保存的就是已经匹配成功的input_dev handler 
00837: evdev->handle.name = dev_name(&evdev->dev);
00838: evdev->handle.handler = handler;
00839: evdev->handle.private = evdev;
00841: evdev->dev.devt = MKDEV(INPUT_MAJOR,  EVDEV_MINOR_BASE + minor);
00842: evdev->dev.class = &input_class;
00843: evdev->dev.parent = &dev->dev;
00844: evdev->dev.release = evdev_free;
00845: device_initialize(&evdev->dev);
00847: error = input_register_handle(&evdev->handle); //把这个evdev中初始化好的handle 注册到input核心中去,代表一个匹配成功的组合 
00848: if (error)
00849: goto ¯err_free_evdev;
00850:
00851: error = evdev_install_chrdev(evdev); //把这个初始化好的evdev添加到上面说到过的evdev_table数组,以minor索引序号
00852: if (error)
00853: goto ¯err_unregister_handle;
00855: error = device_add(&evdev->dev); //把这个device 添加到/sys/class/input/下面,所以我们可以看到/dev/input下面看到:event031 字样字符设备文件,这就是在上面命名的 
00856: if (error)
00857: goto ¯err_cleanup_evdev;
00859: return 0;
00861: err_cleanup_evdev:
00862: evdev_cleanup(evdev);
00863: err_unregister_handle:
00864: input_unregister_handle(&evdev->handle);
00865: err_free_evdev:
00866: put_device(&evdev->dev);
00867: return error;
00868: } ? end evdev_connect

00026: struct evdev {
00027: int exist;
00028: int open;    //打开标志
00029: int minor;   //次设备号
00030: struct input_handle handle;     //包含的handle
00031: wait_queue_head_t wait;   //等待队列
00032: struct evdev_client *grab;       //强制绑定的evdev_client结构 
00033: struct list_head client_list;       //evdev_client 链表,这说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问evdev设备 
00034: spinlock_t client_lock; /* protects client_list */
00035: struct mutex mutex;
00036: struct device dev;
00037: };

关于这个结构变量我的理解是抽象出来一个设备,代表一个input_dev与其匹配好的handler的组合(handle),可以看作提供给事件处理层的一个封装.

00039: struct evdev_client {
00040: struct input_event buffer[EVDEV_BUFFER_SIZE]; //input_event数据结构的数组,input_event代表一个事件,基本成员:类型(type),编码(code),值(value  
00041: int head; //buffer数组的索引头
00042: int tail; //buffer数组的索引尾 
00043: spinlock_t buffer_lock;
00043: /* protects access to buffer, head and tail */
00044: struct fasync_struct *fasync; //异步通知函数
00045: struct evdev *evdev; //包含一个evdev变量
00046: struct list_head node; //链表
00047: struct wake_lock wake_lock;
00048: char name[28];
00049: };

这个结构会在evdev被打开的时候 创建,这里关于evdev的初始以及在input系统中承接作用暂时介绍到这里.

7.5.输入事件驱动层源码分析

7.5.1、input_handler

01314: struct input_handle {
01316: void *private; //指向上面封装的evdev 
01318: int open;
01319: const char *name;
01321: struct input_dev *dev; //input 设备
01322: struct input_handler *handler; // 一个inputhandler
01324: struct list_head d_node; //链表结构
01325: struct list_head h_node;
01326: };

01817: int input_register_handle(struct input_handle * handle)
01818: {
01819: struct input_handler *handler = handle->handler;
01820: struct input_dev *dev = handle->dev; //取出两个成员 
01821: int error;
01823: /*
01824: * We take dev->mutex here to prevent race with
01825: * input_release_device().
01826: */
01827: error = mutex_lock_interruptible(&dev->mutex);
01828: if (error)
01829: return error;
01831: /*
01832: * Filters go to the head of the list, normal handlers
01833: * to the tail.
01834: */
01835: if (handler->filter)
01836: list_add_rcu(&handle->d_node, &dev->h_list); //把这个handled_node 加到对应input_devh_list链表里面
01837: else
01838: list_add_tail_rcu(&handle->d_node, &dev->h_list); //把这个handleh_node 加到对应input_handlerh_list链表里面
01840: mutex_unlock(&dev->mutex);
01842: /*
01843: * Since we are supposed to be called from ->connect()
01844: * which is mutually exclusive with ->disconnect()
01845: * we can't be racing with input_unregister_handle()
01846: * and so separate lock is not needed here.
01847: */
01848: list_add_tail_rcu(&handle->h_node, &handler->h_list);
01850: if (handler->start)
01851: handler->start(handle);
01853: return 0;
01854: } ? end input_register_handle ?

这个注册是把handle 本身的链表加入到它自己的input_dev 以及 input_handlerh_list链表中,这样以后就可以通过h_list遍历到这个handle,这样就实现了三者的绑定联系.

8.输入设备驱动层源码分析

8.1、先找到bsp中按键驱动源码

(1)锁定目标:板载按键驱动

(2)确认厂家提供的BSP是否已经有驱动

找到了s3c_button,然后在源码中寻找。

(3)找到bsp中的驱动源码,验证:

Value值,一个是按下是1,一个是抬起是0

8.2、按键驱动源码初步分析

(1)模块装载分析

不规范!device和driver匹配,下面可以执行probe函数。

(2)平台总线相关分析

(3)确定重点:probe函数

 

#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

sizeof(long) = 4,所以BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, 32)

BITS_TO_LONGS(nr)就是((nr) + (32 -1) / (32))

就是判断nr这个数是属于几个long类型

结合定义unsigned long evbit[BITS_TO_LONGS(EV_CNT)];

就是根据EV_CNT的个数定义一个数组,如果EV_CNT小于32,就定义成evbit[1],如果大于32就装不下了就要使用evbit[2]这么大的数组了。

这么做一方面是为了提高兼容性,万一数量改变,还要记得修改定义的数组大小,定义成宏,定义数组的时候会根据数量自动判断应该创建多大的数组

另一方面也是提供了一种通用的解决办法,为下面的keybitrelbit等都提供了通用好用的解决方案,佩服这种思想!

8.3、源码细节实现分析

(1)gpio_request

(2)input_allocate_device

(3)input_register_device

(4)timer

00291: static int s3c_button_probe(struct
00291: platform_device *pdev)
00292: {
00293: int i;
00294: int ret;
00295:
00296:
#if defined(CONFIG_X210) && (CONFIG_X210 > 0)
00297: /* gph0_1 (power) */
00298: ret = gpio_request(S5PV210_GPH0(1), "GPH0");       //申请GPIO
00299: if(ret)
00300: printk("button-x210: request gpio GPH0(1) fail");
00301: s3c_gpio_setpull(S5PV210_GPH0(1), S3C_GPIO_PULL_UP
00301: );
00302: s3c_gpio_cfgpin(S5PV210_GPH0(1), S3C_GPIO_SFN(0));
00303: s3c_button_history[0] = gpio_get_value(S5PV210_GPH0(1));
。。。。。。。。。。。。。。。
00401: /* gph1_1 (back) */
00402: ret = gpio_request(S5PV210_GPH1(1), "GPH1");
00403: if(ret)
00404: printk("button-x210: request gpio GPH1(1) fail");
00405: s3c_gpio_setpull(S5PV210_GPH1(1), S3C_GPIO_PULL_UP);
00406: s3c_gpio_cfgpin(S5PV210_GPH1(1), S3C_GPIO_SFN(0));
00407: s3c_button_history[6] = gpio_get_value(S5PV210_GPH1(1));
00408: #endif
00410: input = input_allocate_device();
00411: if(!input)
00412: return -ENOMEM;
00414: set_bit(EV_KEY, input->evbit);

unsigned long evbit[BITS_TO_LONGS(EV_CNT)];这个数组代表input设备所支持的事件,使用位图的方法来表示。什么是位图,就是使用某一位来代表一个事件,

如果这一位被为1,那么就代表它支持这类事件。那么我们看看,input设备都支持哪些事件。

如果我们想让input设备支持按键事件,那么我们只需要让evbit数组的EV_KEY即第1位置为1就可以了。我们可以使用宏set_bit(EV_KEY, input_dev->evbit)就可以了,为什么是input_dev->evbit呢,

因为input_dev下面挂载了一个evbit的数组,表示这个设备支持的事件。


00416: for(i = 0; i < MAX_BUTTON_CNT; i++)
00417: set_bit(s3c_Keycode[i], input->keybit);
00419: input->name = "s3c-button";
00420: input->phys = "s3c-button/input0";
00422: input->id.bustype = BUS_HOST;
00423: input->id.vendor = 0x0001;
00424: input->id.product = 0x0001;
00425: input->id.version = 0x0100;
00427: input->keycode = s3c_Keycode;
00429: if(input_register_device(input) != 0)
00430: {
00431: printk("s3c-button input register device fail!!\n");
00433: input_free_device(input);
00434: return -ENODEV;
00435: }
00437: /* Scan timer init */
00438: init_timer(&timer);
00439: timer.function = s3cbutton_timer_handler;   //定时器处理函数,如中断
00441: timer.expires = jiffies + (HZ/100);          //jiffies是当前时间,HZ/100是指1/100秒。也就是按键的去抖
00442: add_timer(&timer);
00444: printk("s3c button Initialized!!\n");
00446: return 0;
00447: } ? end s3c_button_probe ?

00060: static void s3cbutton_timer_handler(unsigned
00060: long data)
00061: {
00062: int flag;
00063:
00064:
/* power */
00065: flag = gpio_get_value(S5PV210_GPH0(1));
00066: if(flag != s3c_button_history[0])
00067: {
00068: if(flag)
00069: {
00070:    input_report_key(input, s3c_Keycode[0], 0)
00070: ;
00071: }
00072: else
00073: {
00074:    input_report_key(input, s3c_Keycode[0], 1)
00074: ;
00075: }
00076:    s3c_button_history[0] = flag;
00077: }

。。。。。。。。。。。

00169: /* Kernel Timer restart */
00170: mod_timer(&timer,jiffies + HZ/100);
00171: } ? end s3cbutton_timer_handler ?

01375: static inline void input_report_key(struct
01375: input_dev *dev, unsigned int code, int value)
01376: {
01377: input_event(dev, EV_KEY, code, !!value);
01378: }

input_report_key

         input_event

                   input_handle_event

                            input_pass_event

整个方式是采用轮询的方法实现的。

9.中断方式按键驱动实战

6.1、模板

(1)input类设备驱动模式非常固定,用参考模版修改即可       

//在/kernel/documentation/input/input-programming.txt

(2)新建驱动项目并粘贴模版内容

6.2、模板驱动的解析

if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL))

{

printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);

      return -EBUSY;

}

00135: request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
00137: {
00138:    return request_threaded_irq(irq, handler, NULL, flags, name, dev);
00139: }

参数:

Irq:中断号

Handler:中断处理函数

Flags:标志位

Name:申请中断号的名字

Dev:传递参数

Gpio和中断都需要申请!

6.3、着手移植驱动

6.4、驱动移植细节

#include <linux/input.h>

#include <linux/module.h>

#include <linux/init.h>

#include <asm/irq.h>

#include <asm/io.h>

#include <linux/interrupt.h>

#include <plat/gpio-cfg.h>

#include <linux/gpio.h>

 

#include <mach/irqs.h>  //arch/arm/mach-s5pv210/include/mach/irqs.h

 

/*

 * X210:

 *

 * POWER  -> EINT1   -> GPH0_1

 * LEFT   -> EINT2   -> GPH0_2

 * DOWN   -> EINT3   -> GPH0_3

 * UP     -> KP_COL0 -> GPH2_0

 * RIGHT  -> KP_COL1 -> GPH2_1

 * MENU   -> KP_COL3 -> GPH2_3 (KEY_A)

 * BACK   -> KP_COL2 -> GPH2_2 (KEY_B)

 */

 

#define  BUTTON_IRQ     IRQ_EINT2

 

static struct input_dev *button_dev;

 

static irqreturn_t button_interrupt(int irq, void *dummy)

{

    int flag;

    s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0));

    flag = gpio_get_value(S5PV210_GPH0(2));

    s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));

   

    input_report_key(button_dev, KEY_LEFT, !flag);  //上报中断处理事件

    input_sync(button_dev); //同步

    return IRQ_HANDLED;

}

static int __init button_init(void)

{

    int error;

    int ret;

    ret = gpio_request(S5PV210_GPH0(2), "GPH0_2");

    if(ret)

    printk("keyintrupt: request gpio GPH0(2) fail");

    s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));

   

    if (request_irq(BUTTON_IRQ, button_interrupt, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING , "button-x210", NULL)) //申请了一个中断号并绑定中断处理函数,上升沿和下降沿触发

    {

        printk(KERN_ERR "keyintrupt.c: Can't allocate irq %d\n", BUTTON_IRQ);

        return -EBUSY;

    }

   

    button_dev = input_allocate_device();

   

    if (!button_dev)

    {

        printk(KERN_ERR "keyintrupt.c: Not enough memory\n");

        error = -ENOMEM;

        goto err_free_irq;

    }

    button_dev->evbit[0] = BIT_MASK(EV_KEY);

    button_dev->keybit[BIT_WORD(KEY_LEFT)] = BIT_MASK(KEY_LEFT);

    error = input_register_device(button_dev);

    if (error)

    {

        printk(KERN_ERR "keyintrupt.c: Failed to register device\n");

        goto err_free_dev;

    }

    return 0;

 

err_free_dev:

    input_free_device(button_dev);

err_free_irq:

    free_irq(BUTTON_IRQ, button_interrupt);

    return error;

}

static void __exit button_exit(void)

{

    input_unregister_device(button_dev);

    free_irq(BUTTON_IRQ, button_interrupt);

}

module_init(button_init);

module_exit(button_exit);

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("jyt<[email protected]>");

MODULE_DESCRIPTION("Keyboard driver for x210 button.");

MODULE_ALIAS("platform:s3c-button");

6.5、驱动实践

Gpio冲突!

进入目录把重复的去掉。

编译!成功之后下载,装载驱动模块。

验证!

将驱动编译成模块。

http://emb.hqyj.com/uploads/allimg/170104/143335I41-1.jpghttps://images2015.cnblogs.com/blog/944893/201611/944893-20161125165556721-1160519994.png

 

猜你喜欢

转载自blog.csdn.net/wangdapao12138/article/details/82055019