Input Core 初探

来源:互联网 发布:夏洛特.兰普林 知乎 编辑:程序博客网 时间:2024/05/11 05:22

本文基于Kernel3.0.8


1.几个数据结构

 

struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};


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


unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];


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 hint_events_per_packet;


unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;


int (*setkeycode)(struct input_dev *dev,
 const struct input_keymap_entry *ke,
 unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev,
 struct input_keymap_entry *ke);


struct ff_device *ff;


unsigned int repeat_key;
struct timer_list timer;


int rep[REP_CNT];


struct input_mt_slot *mt;
int mtsize;
int slot;
int trkid;


struct input_absinfo *absinfo;


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 (*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 __rcu *grab;


spinlock_t event_lock;
struct mutex mutex;


unsigned int users;
bool going_away;


bool sync;


struct device dev;


struct list_headh_list;
struct list_headnode;
}

这个数据结构太复杂了,又长又长的。


一般上报event事件的函数如下:

触摸事件,使用上报绝对值函数,X/Y 两个值

input_report_abs(inputdev, ABS_X, 100);

input_report_abs(inputdev, ABS_Y, 200);

按键事件,使用key event函数,

input_report_key(inputdev, BTN_TOUCH, 1);


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_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}


/**
 * input_event() - report new input event
 * @dev: device that generated the event
 * @type: type of the event
 * @code: event code
 * @value: value of the event
 *
 * This function should be used by drivers implementing various input
 * devices to report input events. See also input_inject_event().
 */
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;


if (is_event_supported(type, dev->evbit, EV_MAX)) {


spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}


input_handle_evet 调用input_pass_event

static void input_pass_event(struct input_dev *dev,
    unsigned int type, unsigned int code, int value)
{
struct input_handler *handler;
struct input_handle *handle;


rcu_read_lock();


handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else {
bool filtered = false;


list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)
continue;


handler = handle->handler;
if (!handler->filter) {
if (filtered)
break;


handler->event(handle, type, code, value);


} else if (handler->filter(handle, type, code, value))
filtered = true;
}
}


rcu_read_unlock();
}

这个函数里的handler 就是input dev设备注册的时候所关联的 handler,它是处理输入事件的关键,

下面看到的代码里 有个 evdev_handler ,这两个是一回事。


下面说下 evdev & input core & input_dev

Kernel 中会调用 evdev_read 此函数从client->buffer中(evdev_client *client = file->private_data;)copy对应的input_event到用户提供的buffer。这些input_event数据如何从input_handle_event到client->buffer中的呢? 它是在evdev_event中实现的。

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,
};

在evdev_init中调用input_register_handler(&evdev_handler),这个函数注册了evdev_handlerl并把它添加到input_handler_list中。

 在input_register_device中,通过input_attach_handler(dev, handler),寻找input_handler_list中哪一个input_handler与需要注册的设备相匹配,找到匹配的,则调用此input_handler的connect把input_handler与注册的input_device关联起来,这样新注册的input_device就有对应的处理方法了。

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)
pr_err("failed to attach handler %s to device %s, error: %d\n",
      handler->name, kobject_name(&dev->dev.kobj), error);


return error;
}

如果匹配上了,就调用evdev_handler->connect函数(evdev_connect),它把此evdev_handler保存在新设备的handle->handler中(evdev->handle.handler = handler;)。这样在input_pass_event就可以调用evdev->handle.handler.event了。

  input_handle把input_device和input_handler关联起来,其数据结构如下:

struct input_handle {


void *private;


int open;
const char *name;


struct input_dev *dev;
struct input_handler *handler;


struct list_headd_node;
struct list_headh_node;
};

evdev为事件字符设备( Event char devices)提供了访问原始输入设备事件的方法,它是负责做具体事的,对于事件字符设备,当我们调用open, read, write最终都会调用它的函数,即evdev_fops中的对应函数。有了evdev和input core,设备驱动程序就很简单了.

1)调用input_allocate_device 创建一个input_dev对象
2)设备input_dev的各种属性以告诉input core你将提供哪些事件
3)调用input_register_device把input_dev注册到input core

来看下 input_register_device 这个函数,

在input_register_device中,它将在input_handler_list 寻找与input_dev匹配的input_handler,然后调用input_handler的connect函数,即evdev_connect。在evdev_connect中,它将创建evdev对象,并以evdev->minor作为索引把它放在evdev_table数组中。evdev数据结构如下:

struct evdev {
int open;
int minor;   //在evdev_table中的索引
struct input_handle handle;  //连接input_dev和input_handler
wait_queue_head_t wait;
struct evdev_client __rcu *grab;   // 用户每调用一次open,将创建一个evdev_client
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
bool exist;
};


struct evdev_client {
unsigned int head;
unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct wake_lock wake_lock;
char name[28];
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
unsigned int bufsize;
struct input_event buffer[];  //存放所有这个设备产生的input_event,由evdev_event写入
};


在evdev_connect,它设置设备名,初始化input_handle中各个数据成员(关键是其input_dev和input_handler),然后再调用input_register_handle把evdev中的input_handle添加到input_dev的h_lis链表中,并且把此input_handle添加到input_handler的h_list链表中。从此它们的三角关系建立完成。注:当用户每打开一次它就要创建一个evdev_client,并加入到client_list链表中,当input_dev产生事件时,evdev_event函数将把此input_event放入evdev->client_list链表中的每个evdev_client的buffer中。它们的关系如下图所示:




Kernel中数据读取流程
     在kernel中调用evdev_read来读取数据,其函数原型如下:
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
    其读取流程为:
    1)从前面得知在打开设备文件时,创建了一个evdev_client,并把此client保存在file的private_data中,所以现在从file->private_data中取出evdev_client
    2)从前面得知此evdev_client中保存有在evdev_connect时创建的evdev,从而可以得到对应的evdev对象
    3)从client->buffer中读取事件,并copy到用户提供的buffer中。前面讲过,client->buffer中的事件是当input_dev报告事件给Input core时,由evdev_event把input_event事件放入client->buffer中的。

0 0
原创粉丝点击