1.什么是input子系统

1.1、何为输入设备
1.2、linux中输入设备的编程模型
(1)命令行界面的输入类设备应用接口
(2)GUI界面带来的麻烦、不同的输入类设备也会带来麻烦
(3)struct input_event
1.3、input子系统简介
(1)linux的input子系统解决了什么问题
(2)input子系统分4个部分:应用层 + input event + input core + 硬件驱动
(3)input子系统如何工作
(4)事件驱动型GUI框架,如QT、VC等。

2.input设备应用层编程实践1

2.1、确定设备文件名
(1)应用层操作驱动有2条路:/dev目录下的设备文件,/sys目录下的属性文件
(2)input子系统用的/dev目录下的设备文件,具体一般都是在 /dev/input/eventn
(3)用cat命令来确认某个设备文件名对应哪个具体设备。我在自己的ubuntu中实测的键盘是event1,而鼠标是event3.

2.2、标准接口打开并读取文件
2.3、解析struct input_event

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/input.h>
#include <time.h>
#include <pthread.h>

#define DEVICE_KEY "/dev/input/event1"
#define DEVICE_MOUSE "/dev/input/event3"

struct input_event env1;
struct input_event env2;

int fd1 = -1;
int fd2 = -1;
int ret = -1;

pthread_t tid1;
pthread_t tid2;

void *pth_fn1(void *args)
{
for(;;)
{
ret = read(fd1, &env1, sizeof(struct input_event));
if(ret > 0)
{
printf("ctime: %s -------------------------------", ctime((time_t *)&env1.time.tv_sec));
printf("type = %hd code = %hd value = %d \n", env1.type, env1.code, env1.value);
fflush(stdout);
memset(&env1, 0, sizeof(struct input_event));
}
}
}

void *pth_fn2(void *args)
{
for(;;)
{
ret = read(fd2, &env2, sizeof(struct input_event));
if(ret > 0)
{
printf("ctime: %s -------------------------------", ctime((time_t *)&env2.time.tv_sec));
printf("type = %hd code = %hd value = %d \n", env2.type, env2.code, env2.value);
fflush(stdout);
memset(&env2, 0, sizeof(struct input_event));
}
}
}


int main(void)
{
void *ret1 = NULL;
void *ret2 = NULL;
fd1 = open(DEVICE_KEY, O_RDONLY);
if(fd1 < 0){
perror("open1");
goto err1;
}


fd2 = open(DEVICE_MOUSE, O_RDONLY);
if(fd2 < 0){
perror("open2");
goto err2;
}

memset(&env1, 0, sizeof(struct input_event));
memset(&env2, 0, sizeof(struct input_event));

pthread_create(&tid1, NULL, pth_fn1, NULL);
pthread_create(&tid2, NULL, pth_fn2, NULL);

pthread_join(tid1, &ret1); //wait thread return
pthread_join(tid2, &ret2); //wait thread return

return 0;
err3:
close(fd2);
err2:
close(fd1);
err1:
return -1;

}

4.input子系统架构总览1

4.1、input子系统分为三层
(1)最上层:输入事件驱动层,用户通过该层得到数据
(2)中间层:输入核心层,管理和匹配
(3)最下层:输入设备驱动层,处理底层硬件的信息
 

我们自己编写的驱动就是处于最下层(驱动层),应用程序通过最上层得到事件

8.input子系统基础之按键_#include
8.input子系统基础之按键_#include_02

所有的输入设备的主设备号都是13,其通过次设备来将输入设备进行分类,如下图:

8.input子系统基础之按键_#include_03

4.2、input类设备驱动开发方法
(1)输入事件驱动层和输入核心层不需要动,只需要编写设备驱动层
(2)设备驱动层编写的接口和调用模式已定义好,驱动工程师的核心工作量是对具体输入设备硬件的操作和性能调优。
(3)input子系统不算复杂,学习时要注意“标准模式”四个字。

#define EV_SYN      0x00  //表示支持所有事件
#define EV_KEY 0x01 //键盘或者按键,表示一个键码
#define EV_REL 0x02 //鼠标设备,表示一个相对的光标位置结果
#define EV_ABS 0x03 //手写板产生的值,其是一个绝对整数值
#define EV_MSC 0x04 //其他类型
#define EV_SW 0x05 //转换
#define EV_LED 0x11 //led
#define EV_SND 0x12 //声音设备
#define EV_REP 0x14 //允许重复输入按键类型
#define EV_FF 0x15
#define EV_PWR 0x16 //电源管理
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)

6.input设备注册

设备结构体:

struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;

unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//用来表示按键有哪些值
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev,
unsigned int scancode, unsigned int keycode);
int (*getkeycode)(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode);

struct ff_device *ff;

unsigned int repeat_key;
struct timer_list timer;

int sync;

int abs[ABS_CNT];
int rep[REP_MAX + 1];

unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];

int absmax[ABS_CNT];
int absmin[ABS_CNT];
int absfuzz[ABS_CNT];
int absflat[ABS_CNT];
int absres[ABS_CNT];

int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

struct input_handle *grab;

spinlock_t event_lock;
struct mutex mutex;

unsigned int users;
bool going_away;

struct device dev;

struct list_head h_list;
struct list_head node;
}

设备id

struct input_device_id {

kernel_ulong_t flags;//表示信息

__u16 bustype;//总线类型
__u16 vendor; //制造商ID
__u16 product; //产品ID
__u16 version; //版本号

kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];

kernel_ulong_t driver_info; //额外信息
};

1.自动分配设备空间
 

struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;

dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); //分配一个结构体
if (dev) {
dev->dev.type = &input_dev_type;//初始化设备的类型
dev->dev.class = &input_class;//设置为输入设备类
device_initialize(&dev->dev);//初始化device结构
mutex_init(&dev->mutex); //初始化互斥锁
spin_lock_init(&dev->event_lock);//初始化自旋锁
INIT_LIST_HEAD(&dev->h_list);//初始化链表
INIT_LIST_HEAD(&dev->node);//初始化链表

__module_get(THIS_MODULE);//模块引用计数加1
}

return dev;
}

(2)input_set_capability   //设置为的值

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
switch (type) {
case EV_KEY://键盘或按键
__set_bit(code, dev->keybit);
break;
case EV_REL://鼠标设备,表示一个相对光标位置结果
__set_bit(code, dev->relbit);
break;
case EV_ABS://手写板产生的值,是一个绝对整数
__set_bit(code, dev->absbit);
break;
case EV_MSC://其他类型
__set_bit(code, dev->mscbit);
break;
case EV_SW:
__set_bit(code, dev->swbit);
break;
case EV_LED:
__set_bit(code, dev->ledbit);
break;
case EV_SND:
__set_bit(code, dev->sndbit);
break;
case EV_FF:
__set_bit(code, dev->ffbit);
break;
case EV_PWR:
/* do nothing */
break;
default:
printk(KERN_ERR
"input_set_capability: unknown type %u (code %u)\n",
type, code);
dump_stack();
return;
}

__set_bit(type, dev->evbit);
}

2.注册设备函数

int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);//注意这里的static
struct input_handler *handler;
const char *path;
int error;

/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);//所有的设备都要定义这个

/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);

/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);//确保dev->evbit中没有提到的位掩码是干净的

/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);//设置函数
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}

if (!dev->getkeycode)//如果处理KEY的函数没有定义就用默认的
dev->getkeycode = input_default_getkeycode;

if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;

dev_set_name(&dev->dev, "input%ld",//设置名字,input_no为stattic,因此会累加
(unsigned long) atomic_inc_return(&input_no) - 1);

error = device_add(&dev->dev);//添加设备
if (error)
return error;

path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",//打印设备路径,输出调试信息
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);

error = mutex_lock_interruptible(&input_mutex);
if (error) {
device_del(&dev->dev);
return error;
}

list_add_tail(&dev->node, &input_dev_list);//添加设备到链表

list_for_each_entry(handler, &input_handler_list, node)//从所有handler中找
input_attach_handler(dev, handler);//挂接handler和设备

input_wakeup_procfs_readers();

mutex_unlock(&input_mutex);

return 0;
}

3.匹配inpu_dev和handler

input-attach-handler()函数用来匹配input dev和handler,只有匹配成功,才能进行下,一步的关联操作。

input match device (函数用来与input dev和handler进行匹配。handler的id table表中定义了其支持的input dev设备。

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;

id = input_match_device(handler, dev);
if (!id)
return -ENODEV;

error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
printk(KERN_ERR
"input: failed to attach handler %s to device %s, "
"error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);

return error;
}

static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
int i;

for (id = handler->id_table; id->flags || id->driver_info; id++) {
//重所有handler中匹配
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)//如果定义了,表示需要匹配flags
if (id->bustype != dev->id.bustype)//表示不能和已经定义的handler匹配
continue;

if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
continue;

if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
continue;

if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
continue;

MATCH_BIT(evbit, EV_MAX);//匹配
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
MATCH_BIT(swbit, SW_MAX);

if (!handler->match || handler->match(handler, dev))
return id;
}

return NULL;
}

7.handler 和  handle 注册

1.   注册handler

input_handler结构体:

struct input_handler {

void *private;//表示特定驱动的数据
//处理发送给设备的事件
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
//连接handler和input_dev
void (*disconnect)(struct input_handle *handle);//断开handler和input_dev
void (*start)(struct input_handle *handle);

const struct file_operations *fops;//handler实现的操作文件集
int minor;//次设备号
const char *name;//handler的名字

const struct input_device_id *id_table;//表示驱动能够处理的表

struct list_head h_list;//连接到于这个handler相联系的下一个handler
struct list_head node;//链接到全局的handler链表中,所有的handler都在上面
};

函数:

int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;


retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;

INIT_LIST_HEAD(&handler->h_list);

if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) { //除以32
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;//如果没有注册就注册
}

list_add_tail(&handler->node, &input_handler_list);

list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);


input_wakeup_procfs_readers();

out:
mutex_unlock(&input_mutex);
return retval;
}

2.   注册handle

input_handle是用来连接input_dev和input_handler的一个中间结构体。

事件通过, input_handle从input_dev发送到input handler,

或者从input_handler发送到input_dev进行处理。

在使用input handle之前,需要对其进行注册,注册函数是input register-handle().


 

结构体:

struct input_handle {

void *private;//handler的私有数据

int open;//表示handle是否正在被使用,当使用时,会将事件分发给设备处理
const char *name;//handle名字

struct input_dev *dev;//handle依附的设备
struct input_handler *handler;//handle依附的handler

struct list_head d_node;//放到 设备 相关联的handle链表中
struct list_head h_node;//放到 handler 相关联的handle链表中
};

注册函数:由handler的evdev_connect来调用这个挂接函数

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{ //...........
error = input_register_handle(&evdev->handle);
}
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler; //取出handler
struct input_dev *dev = handle->dev; //取出设备
int error;

/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;

/*
* Filters go to the head of the list, normal handlers
* to the tail.
*/
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);//挂接设备
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);

mutex_unlock(&dev->mutex);

/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
list_add_tail_rcu(&handle->h_node, &handler->h_list);//挂接handler

if (handler->start)
handler->start(handle);

return 0;
}

handle,handler和device的关系————————————

8.input子系统基础之按键_3c_04

  1. 结点1,2,3表示input_dev设备,其通过input_dev->node变量连接到全局输入设备链表input_dev list中。
  2. 结点4、5、6表示input_handler处理器,其通过input_handler->noce连接到全局handler处理器链表input_handler list中。
  3. 结点7是一个input_handle的结构体,其用来连接input_dev和input_handler, input_handle的dev成员指向了对应的input_dev设备, input_handle的handler成员指向了对应的input_handler。
  4. 另外,结点7的input_handle通过d_node连接到了结点2的input_dev上的h_list链表上。
  5. 另一方面,结点7的input_handle通过h_node连接到了结点5的input_handler的h_list链表上。

通过这种关系,将input_dev和input_handler联系了起来。

8.input子系统基础之按键_#define_05

从本质上讲,input_dev与handler是多对多的关系,从上图可以看出来,一个input_dev可以对应多个handler,一个handler也可以对应多个input_dev。因为在匹配的时候,

一个input_dev会与所有的handler都进行匹配的,并不是匹配成功一次就退出。

从图中可以看出来,一个handle就是用来记录系统中一对匹配成功的handler和device,我们可以从这个handle出发得到handler的信息,还可以得到device的信息。所以正因为有这样的

功能,所以可以由handler经过handle最终获取到device的信息,同理也可以从device从发经过handle最终获取到handler的信息。这种运用方法将会在后面的分析中看到。

总结:#####################################

核心层(其实就是驱动框架)提供的服务有哪些:

(1)创建设备类、注册字符设备

(2)向设备驱动层提供注册接口

(3)提供上层handler和下层device之间的匹配函数

(4)上层和下层通过handle来匹配

 

8.evdev输入事件驱动分析

evdev输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底,层驱动的大多数事件,并使用相应的逻辑对其进行处理。evdev输入事件驱动从底层接收事件信息,将其反映到sys文件系统中,用户程序通过对svs文件系统的操作,就能够达到处理事件的能力。

1.evdev的初始化-----------------------------------------------

static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}

2.evdev成员解析-------------------------------------------------------------------------------

static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};

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

第06行,定义了minor为EVDEV MINOR BASE (64) 。因为一个handler可以处理32个设备,所以evdev handler所能处理的设备文件范围为(13.64)~(13,64+32) ,其中13是所有输入设备的主设备号。

evdev. ids没有定义flags,也没有定义匹配属性值。

这个evdev ids的意思就是: evdev handler可以匹配所有input dev设备,也就是所有的input dev发出的事件,都可以由evdev handler来处理。另外,从前面的分析可以知道,匹配成功之后会调用, handler->connect()函数,对该函数的介绍如下:

8.input子系统基础之按键_#define_06

2.1   evdev_connect函数*************

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;

for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])//找到可以用的次设备号
break;

if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;

INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);

dev_set_name(&evdev->dev, "event%d", minor);//定义在/dev/input/evevt(minor)文件名
evdev->exist = 1;
evdev->minor = minor;

evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;

evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);//主设备号 = 13
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);

error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;

error = evdev_install_chrdev(evdev);//添加到iput设备数组中
if (error)
goto err_unregister_handle;

error = device_add(&evdev->dev);//添加设备
if (error)
goto err_cleanup_evdev;

return 0;

err_cleanup_evdev:
evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
return error;
}

第04~06行,声明了一些必要的局部变量。
第07~13行, for循环中的EVDEV MINORS定义为32,表示evdev handler所表示的32个设备文件。evdev talbe是一个struct evdev类型的数组, struct evdev是模块使用的封装结构,与具体的输入设备有关。
第08行,这一段代码的在 evdev talbe找到为空的那一项,当找到为空的一项,便结束for循环。这时, minor就是数组中第一项为空的序号。
第10到13行,如果没有空闲的表项,则退出。
第14~16行,分配一个struct evdev的空间,如果分配失败,则退出。
第17~20行,对分配的evdev结构进行初始化,主要对链表、互斥锁和等待队列做必要的初始化。在evdev中,封装了一个handle结构,这个结构与handler是不同的。可以把handle看成是handler和input_device的信息集合体,这个结构用来联系匹配成功的handler和input_device.
第21行,对evdev命一个名字,这个设备的名字形如eventx,例如eventl. event2和event3等。最大有32个设备,这个设备将在/dev/input/目录下显示。
第23-27行,对evdev进行必要的初始化。其中,主要对handle进行初始化,这此初始化的目的是使input dev和input handler联系起来。
第28-33行,在设备驱动模型中注册一个evdev->dev的设备,并初始化一个 evdev->dev的设备。这里,使evdev->dev所属的类指向input class。这样在/sysfs中创建的设备目录就会在/sys/class/input/下显示。
第34行,调用input register-handle()函数注册一个input handle结构体。
第37行,注册handle,如果成功,那么调用evdev install chrdev将evdev table的minor项指向evdev。
第40行,将evdev->device注册到sysfs文件系统中。
第41~50行,进行一些必要的错误处理。

2.2   evdev_event函数***********将传入事件传递给所有连接的客户端。

static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
struct timespec ts;

ktime_get_ts(&ts);//获得当前时间
event.time.tv_sec = ts.tv_sec;//秒
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;//纳秒
event.type = type;//获得重驱动传来的类型
event.code = code;
event.value = value;

rcu_read_lock();//加读锁

client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);

rcu_read_unlock();

wake_up_interruptible(&evdev->wait);//等待直到中断
}

2.3  fops

用于

static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush
};

对主设备号为INPUT_MAJOR的设备结点进行操作,会将操作集转换成handler的操作集。

在evdev handler中定义了一个fops集合,被赋值为evdev_fops的指针。

evdev_fops结构体是一个file_operations的类型。当用户层调用类似代码 "open("/dev/input/event1", O RDONLY)函数打开设备结点时,会调用evdev_fops中的 evdev_read()函数。

 

3.evdev设备的打开----------------------------------------------------------------

3.1evdev_open函数

evdev_fops结构体是一个file_operations的类型。当用户层调用类似代码 "open("/dev/input/event1", O RDONLY)函数打开设备结点时,会调用evdev_fops中的 evdev_read()函数,该函数的代码如下:

client会用来装载设备传过来的信息,然后在等待应用层读取

static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE;//得到在evdev_table[]中的序号
int error;

if (i >= EVDEV_MINORS)
return -ENODEV;

error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
evdev = evdev_table[i];//得到evdev
if (evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);

if (!evdev)
return -ENODEV;
//初始化一个client,用来关联evdev
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}

spin_lock_init(&client->buffer_lock);
snprintf(client->name, sizeof(client->name), "%s-%d",
dev_name(&evdev->dev), task_tgid_vnr(current));
wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
client->evdev = evdev;
evdev_attach_client(evdev, client);//挂接到client_list上

error = evdev_open_device(evdev);//打开输入设备
if (error)
goto err_free_client;

file->private_data = client;//挂接到文件的私有数据上
nonseekable_open(inode, file);

return 0;
}

3.2evdev_open_device函数

evdev_open_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据。evdev_open_device()函数先获得互斥锁,然后检查设备是否存在,并判断设备是否已经被打开。如果没有打开,则调用input_open_device()函数打开设备。evdev_open_device()函数的代码如下:

static int evdev_open_device(struct evdev *evdev)
{
int retval;

retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;

if (!evdev->exist)//判断是否存在
retval = -ENODEV;
else if (!evdev->open++) {//如果是第一次打开,就进行下一步
retval = input_open_device(&evdev->handle);//执行核心的打开函数
if (retval)
evdev->open--;
}

mutex_unlock(&evdev->mutex);
return retval;
}

3.2input_open_device函数

在这个函数中,递增handle的打开计数。如果是第一次打开,则调用input dev的open()函数。

int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int retval;

retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval;

if (dev->going_away) {
retval = -ENODEV;
goto out;
}

handle->open++;//handle的打开加一

if (!dev->users++ && dev->open)
retval = dev->open(dev);//执行驱动的打开函数

if (retval) {
dev->users--;
if (!--handle->open) {//减少打开次数
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_rcu();
}
}

out:
mutex_unlock(&dev->mutex);
return retval;
}

事件如何从驱动传递到应用层---------------------------------------------------

参考了Linux典藏大典驱动开发  P323

注意:只有设备文件被打开时,才会传送事件

通过在应用的   中断函数  中调用   input_report_key()  来开始传递

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

input子系统下层通过调用input_event函数项核心层上报数据

/*************** input .c  ****************/

input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value) //327

     input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)

        input_pass_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)

            handle = rcu_dereference(dev->grab);//得到handle,grab 就是handler

            handle->handler->event(handle, type, code, value);            //  最终会调用到handler 中的event函数

/*********************  evdev.c  ************/

                evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value

                         //再这里面会把驱动的事件赋值给event

                  evdev_pass_event(struct evdev_client *client,struct input_event *event)  //挂接到client上

                      client->buffer[client->head++] = *event;     //  会将input输入事件数据存放在evdev_client结构体中的缓冲去中

 

如果没有为input device强制指定handler,即为grab赋值,就会遍历input_device->hlist上的handle成员。如果该handle被打开,表示该设备 ,已经被一个用户进程使用。就会调用与输入设备对应的handler的event)函数。注意,只有在handle被打开的情况下才会接收到事件,这就是说,只有设备被用户要向用户空间导出信息。事8.input子系统基础之按键_3c_07

当我们的应用层通过open打开event0这个设备节点时最终会调用到input_init函数中注册的字符设备input时注册的file_operations->open() 函数

/*************** input .c  ****************/

input_open_file(struct inode *inode, struct file *file)

    handler = input_table[iminor(inode) >> 5]  //查找对应的handler

    handler->fops->open()    //执行对应的open函数  

/*********************  evdev.c  ************/

       evdev_open(struct inode *inode, struct file *file)

          evdev = evdev_table[i];

          evdev_open_device

               input_open_device

                    input_dev->open()         //  最终就是执行input设备中的open函数

          file->private_data = evdev_client;   //把事件挂接到文件的私有数据上

 

所以当我们在应用层调用read函数时,最终会调用到handler->fops->read函数

evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)

   struct evdev_client *client = file->private_data;  //取事件

    evdev_fetch_next_event(struct evdev_client *client,struct input_event *event)

        *event = client->buffer[client->tail++]      //  将evdev_client->buffer中的数据取走

    input_event_to_user

        copy_to_user(buffer, &compat_event,sizeof(struct input_event_compat))                  //  拷贝到用户空间

/********************************************************************************************/

 

驱动发送事件的函数:

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value); //key类型
}
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_REL, code, value);
}
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_FF_STATUS, code, value);
}
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_SW, code, !!value);
}
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
static inline void input_mt_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}

9.按键驱动层源码分析

1 如何找到bsp中按键驱动源码

(1)锁定目标:板载按键驱动
(2)确认厂家提供的BSP是否已经有驱动

通过对input中的文件进行读写来判断是否存在驱动
(3)找到bsp中的驱动源码

  1. 同过对input下设备文件name 的读,来获得驱动的名字
  2. 通过查找哪些被编译来判断

2. 源码细节实现分析

结构体:

static struct platform_driver s3c_button_device_driver = {
.probe = s3c_button_probe,
.remove = s3c_button_remove,
.suspend = s3c_button_suspend,
.resume = s3c_button_resume,
.driver = {
.name = "s3c-button",
.owner = THIS_MODULE,
}
};

//平台设备
static struct platform_device s3c_device_button = {
.name = "s3c-button",
.id = -1,
};

2.1 注册和卸载函数:

static int __init s3c_button_init(void)
{
platform_device_register(&s3c_device_button);

return platform_driver_register(&s3c_button_device_driver);
}

static void __exit s3c_button_exit(void)
{
platform_driver_unregister(&s3c_button_device_driver);
platform_device_unregister(&s3c_device_button);
}

2.2 设备识别时挂接的函数:

static int s3c_button_probe(struct platform_device *pdev)
{
int i;
int ret;

/* gph0_1 (power) */
ret = gpio_request(S5PV210_GPH0(1), "GPH0");
if(ret)
printk("button-x210: request gpio GPH0(1) fail");
s3c_gpio_setpull(S5PV210_GPH0(1), S3C_GPIO_PULL_UP);
s3c_gpio_cfgpin(S5PV210_GPH0(1), S3C_GPIO_SFN(0));
s3c_button_history[0] = gpio_get_value(S5PV210_GPH0(1));
input = input_allocate_device();//分配空间
if(!input)
return -ENOMEM;

set_bit(EV_KEY, input->evbit);//设置位图

for(i = 0; i < MAX_BUTTON_CNT; i++)
set_bit(s3c_Keycode[i], input->keybit);//设置驱动支持哪些键的值

input->name = "s3c-button";
input->phys = "s3c-button/input0";

input->id.bustype = BUS_HOST;//总线类型
input->id.vendor = 0x0001;//制造商
input->id.product = 0x0001;//产品id
input->id.version = 0x0100;//版本号

input->keycode = s3c_Keycode;//挂接key值

if(input_register_device(input) != 0)//注册设备
{
printk("s3c-button input register device fail!!\n");

input_free_device(input);
return -ENODEV;
}

/* Scan timer init */
init_timer(&timer);//初始化时钟
timer.function = s3cbutton_timer_handler;//时间到了要执行的函数

timer.expires = jiffies + (HZ/100);//设置为10毫秒检测一次按键的状态
add_timer(&timer);//添加到系统中

printk("s3c button Initialized!!\n");

return 0;
}

2.3 时间到了要执行的函数

static void s3cbutton_timer_handler(unsigned long data)
{
int flag;

/* power */
flag = gpio_get_value(S5PV210_GPH0(1));//判断历史的值,如果没变就不执行
if(flag != s3c_button_history[0])
{
if(flag)
{
input_report_key(input, s3c_Keycode[0], 0);
}
else
{
input_report_key(input, s3c_Keycode[0], 1);
}
s3c_button_history[0] = flag;//设置值
}

/* Kernel Timer restart */
mod_timer(&timer,jiffies + HZ/100);//更新时间
}

 

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

实例代码:

由于有的按键没有中断,就没有添加中断,但是有两个可以正常执行。

#include <linux/input.h> 
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>

#include <asm/irq.h>
#include <asm/io.h>
#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 KEY_NUM (3)

struct int_type{
unsigned int num;
unsigned char name[8];
unsigned int gpio;
unsigned char gpio_name[8];
};

struct work_struct work;
struct workqueue_struct *wq;

static struct input_dev *button_dev;
static int x210_Keycode[KEY_NUM] = {KEY_POWER, KEY_UP, KEY_DOWN};
/*, KEY_LEFT, KEY_RIGHT, KEY_A, KEY_B};*/

static unsigned int x210_button_history[KEY_NUM] = {0};

static struct int_type int_arry[KEY_NUM] = {
{
.num = IRQ_EINT1,
.name = {"POWER"},
.gpio = S5PV210_GPH0(1),
.gpio_name = "GPH0_1",
},{
.num = IRQ_EINT2,
.name = {"LEFT"},
.gpio = S5PV210_GPH0(2),
.gpio_name = "GPH0_2",
},{
.num = IRQ_EINT3,
.name = {"DOWN"},
.gpio = S5PV210_GPH0(3),
.gpio_name = "GPH0_3",
},
};
/*{
.num = KP_COL0,
.name = {"UP"},
.gpio = S5PV210_GPH2(0),
.gpio_name = "GPH2_0",
},{
.num = KP_COL1,
.name = {"RIGHT"},
.gpio = S5PV210_GPH2(0),
.gpio_name = "GPH2_1",
},{
.num = KP_COL2,
.name = {"MENU"},
.gpio = S5PV210_GPH2(1),
.gpio_name = "GPH2_2",
},{
.num = KP_COL3,
.name = {"BACK"},
.gpio = S5PV210_GPH2(2),
.gpio_name = "GPH2_3",
},
}*/

//bottom
static void ket_bottom(struct work_struct *work)
{
printk("This is key_bottom function\n");
}

//top
static irqreturn_t button_interrupt(int irq, void *dummy)
{
int flag;
int i;


for(i = 0; i < KEY_NUM; i++)
{
if(irq == int_arry[i].num){
s3c_gpio_cfgpin(int_arry[i].gpio, S3C_GPIO_SFN(0x0));// input模式
flag = gpio_get_value(int_arry[i].gpio);
s3c_gpio_cfgpin(int_arry[i].gpio, S3C_GPIO_SFN(0x0f));// eint2模式

input_report_key(button_dev, x210_Keycode[i], !flag);
input_sync(button_dev);
break;
}
//printk("interrupt function\n");
}

if (!work_pending(&work))
{
queue_work(wq, &work);
}

return IRQ_HANDLED;
}

static int x210_button_probe(struct platform_device *pdev)
{
int error;
int ret = -1;
int i;

button_dev = input_allocate_device(); //分配空间
if (!button_dev) {
printk(KERN_ERR "button.c: Not enough memory\n");
error = -ENOMEM;
goto err_free_irq;
}

set_bit(EV_KEY, button_dev->evbit);//设置位图
for(i = 0; i < KEY_NUM; i++)
{
set_bit(x210_Keycode[i], button_dev->keybit);//设置驱动支持哪些键的值
}

button_dev->name = "x210-button";
button_dev->phys = "x210-button/input0";

button_dev->id.bustype = BUS_HOST;//总线类型
button_dev->id.vendor = 0x0001;//制造商
button_dev->id.product = 0x0001;//产品id
button_dev->id.version = 0x0100;//版本号

button_dev->keycode = x210_Keycode;//挂接key值

error = input_register_device(button_dev);
if (error) {
printk(KERN_ERR "button.c: Failed to register device\n");
goto err_free_dev;
}

/********* interrupt ***************/
for(i = 0; i < KEY_NUM; i++)
{
ret = gpio_request(int_arry[i].gpio, int_arry[i].gpio_name);
if (ret) {
printk(KERN_ERR "button.c: Can't requset gpio %s\n", int_arry[i].gpio_name);
//return -EBUSY;
}
s3c_gpio_cfgpin(int_arry[i].gpio, S3C_GPIO_SFN(0x0f));
x210_button_history[0] = gpio_get_value(int_arry[i].gpio);
}


wq = create_singlethread_workqueue("work_x210");
if (!wq) {
printk(KERN_ERR"Could not create workqueue\n");
goto err_free_dev;
}
flush_workqueue(wq);

INIT_WORK(&work, ket_bottom);

for(i = 0; i < KEY_NUM; i++)
{
ret = request_irq((unsigned int)int_arry[i].num, button_interrupt, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, int_arry[i].name, NULL);
if (ret) {
printk(KERN_ERR "button.c: Can't allocate irq %s\n", int_arry[i].name);
return -EBUSY;
}
}


printk("x210 button initialized!\n");
return 0;

err_free_dev:
input_free_device(button_dev);
err_free_irq:
for(i = 0; i < KEY_NUM; i++)
{
free_irq((unsigned int)int_arry[i].num, button_interrupt);
}
return error;
}

static int x210_button_remove(struct platform_device *pdev)
{
int i;
for(i = 0; i < KEY_NUM; i++)
{
free_irq((unsigned int)int_arry[i].num, NULL);
}
input_unregister_device(button_dev);
for(i = 0; i < KEY_NUM; i++)
{
free_irq((unsigned int)int_arry[i].num, button_interrupt);
}

destroy_workqueue(wq);
return 0;
}

#define x210_button_suspend NULL
#define x210_button_resume NULL

//platform_driver
static struct platform_driver x210_button_device_driver = {
.probe = x210_button_probe,
.remove = x210_button_remove,
.suspend = x210_button_suspend,
.resume = x210_button_resume,
.driver = {
.name = "x210-button",
.owner = THIS_MODULE,
}
};

//platform_device
static struct platform_device x210_device_button = {
.name = "x210-button",
.id = -1,
};

static int __init button_init(void)
{
platform_device_register(&x210_device_button);
return platform_driver_register(&x210_button_device_driver);
}

static void __exit button_exit(void)
{
platform_driver_unregister(&x210_button_device_driver);
platform_device_unregister(&x210_device_button);
}

module_init(button_init);
module_exit(button_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ljj <123456@qq.com>");
MODULE_DESCRIPTION("Key x210 driver for x210 button.");
MODULE_ALIAS("platform:x210-button");