Linux驱动开发11之input子系统驱动
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)。次设备号为0~255的字符设备。它的操作指针为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_dev的evbit成员来表示,在这里将其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_dev中device的名字,这个名字会在/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.handler和device的匹配
(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); //配对成功调用handler的connect函数,这个函数在事件处理器中定义,主要生成一个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;
//匹配id的evbit和input_dev中evbit的各个位,如果不匹配则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.字符设备的次设备号为0~255,可以有256个设备,
这里后面会看到一个handler可以connect处理32个input设备,所以input体系中,最多拥有8个handler.这个匹配过程和上一篇中的过程是一样的,最后匹配上的话会调用匹配上的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链表,那么在匹配的时候,只要有这个evdev的handler,最后都会匹配到evdev,这个具体可以去看看上篇的匹配过程。
注册的evdev_handler中connect指向的函数为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个,这就是我上面说到的,为什么只有8个handler
//这里是判断evdev的32个位置中是否有空
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下面看到:event0~31 字样字符设备文件,这就是在上面命名的
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; // 一个input的handler
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); //把这个handle的d_node 加到对应input_dev的h_list链表里面
01837: else
01838: list_add_tail_rcu(&handle->d_node, &dev->h_list); //把这个handle的h_node 加到对应input_handler的h_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_handler的h_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]这么大的数组了。
这么做一方面是为了提高兼容性,万一数量改变,还要记得修改定义的数组大小,定义成宏,定义数组的时候会根据数量自动判断应该创建多大的数组
另一方面也是提供了一种通用的解决办法,为下面的keybit,relbit等都提供了通用好用的解决方案,佩服这种思想!
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冲突!
进入目录把重复的去掉。
编译!成功之后下载,装载驱动模块。
。
验证!
将驱动编译成模块。