关闭

Linux Input子系统浅析(一)-- MTK6735M(Android6.0)平台内核实现

896人阅读 评论(1) 收藏 举报
分类:
 Linux输入设备总类繁杂,常见的包括有按键、键盘、触摸屏、鼠标、摇杆等等,他们本身就是字符设备,
而linux内核将这些设备的共同性抽象出来,简化驱动开发建立了一个input子系统。  驱动层和硬件相关,
直接捕捉和获取硬件设备的数据信息等(包括触摸屏被按下、按下位置、鼠标移动、键盘按下等等),然
后将数据信息报告到核心层。核心层负责连接驱动层和事件处理层,设备驱动(device driver)和处理程序
(handler)的注册需要通过核心层来完成,核心层接收来自驱动层的数据信息,并将数据信息选择对应的
handler去处理,最终handler将数据复制到用户空间。

1.先了解三个定义在/linux/input.h下重要的结构体input_dev、input_handler、input_handle
file:kernel-3.18/include/linux/input.h
struct input_dev {
	const char *name;  //input 设备名称
	...
	struct input_id id;  //与input_handler匹配用的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 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;
	
	...
	
	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; //当前占有该设备的handle


	struct device dev;


	struct list_head	h_list;   //该链表头用于链接该设备所关联的input_handle
	struct list_head	node;      //该链表头用于将设备链接到input_dev_list


	...
};
struct input_handler {


	...
	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	void (*events)(struct input_handle *handle,
		       const struct input_value *vals, unsigned int count);
	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);
	void (*disconnect)(struct input_handle *handle);




	const struct input_device_id *id_table; //与input_dev匹配用的id


	struct list_head	h_list;      //用于链接和该handler相关的handle   
	struct list_head	node;        //用于将该handler链入input_handler_list
	...
};
struct input_handle {




	int open;
	const char *name;


	struct input_dev *dev;  		 //指向所属的input_dev
	struct input_handler *handler;   //指向所属的input_handler


	struct list_head	d_node;      //用于链入所指向的input_dev的handle链表
	struct list_head	h_node;		 //用于链入所指向的input_handler的handle链表
};
三个结构体可以这样理解,input_dev可以作为输入设备的描述,input_handler可作为事件的处理函数(类似如中断处理)
,input_handle作为 input_dev和input_handler挂接的桥梁。因为一个device可以对应多个handler,而一个handler也可
处理多个input_device。就如一个触摸屏设备可以对应event handler也可以对应tseve handler,所以input_handle必不可
少。
2. input_dev和input_handler的匹配
file:kernel-3.18/drivers/input/input.c
int input_register_device(struct input_dev *dev)
{
	struct input_devres *devres = NULL;
	struct input_handler *handler;




	/* Every input device generates EV_SYN/SYN_REPORT events. */
	__set_bit(EV_SYN, dev->evbit);       //设置支持同步事件,input设备全部默认支持同步事件


	error = device_add(&dev->dev);
	...


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


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


	input_wakeup_procfs_readers();


	...
}


static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
	...
	id = input_match_device(handler, dev);


	error = handler->connect(handler, dev, id);
	...
}


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


	for (id = handler->id_table; id->flags || id->driver_info; id++) {


		if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
			if (id->bustype != dev->id.bustype)
				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;


		if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
			continue;


		if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
			continue;


		if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
			continue;


		if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
			continue;


		if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
			continue;


		if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
			continue;


		if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
			continue;


		if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
			continue;


		if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
			continue;
		//将设备id与handler的id进行匹配,成功直接返回ID
		if (!handler->match || handler->match(handler, dev))  
			return id;  
	}


	return NULL;
}
file:kernel-3.18/drivers/input/evdev.c
static struct input_handler evdev_handler = {
	.event		= evdev_event,
	.events		= evdev_events,
	.connect	= evdev_connect,
	.disconnect	= evdev_disconnect,
	.legacy_minors	= true,
	.minor		= EVDEV_MINOR_BASE,
	.name		= "evdev",
	.id_table	= evdev_ids,
};
从上面input_handler的初始化可以看出handler->match为null,所以上面的input_match_device如果前面的条件
都满足的话直接返回ID。接下来在观察handler->connect即evdev_connect函数:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	struct evdev *evdev;    //定义一个evdev结构体指针


	minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
	
	INIT_LIST_HEAD(&evdev->client_list);
	dev_no = minor;
	
	/* Normalize device number if it falls into legacy range */
	if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
		dev_no -= EVDEV_MINOR_BASE;
	dev_set_name(&evdev->dev, "event%d", dev_no);  //设置设备名为eventxx


	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	//这步最关键将handler赋值为evdev->handle.handler,完成input_dev和hander的挂接
	evdev->handle.handler = handler;  
	evdev->handle.private = evdev;


	evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
	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);


	cdev_init(&evdev->cdev, &evdev_fops); //绑定file操作函数
	evdev->cdev.kobj.parent = &evdev->dev.kobj;
	error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);


	error = device_add(&evdev->dev);//注册一个设备到内核
	
	...
	return 0;
}
3.上报键值的实现
   在input子系统中上报键值一般使用input_report_xxx接口,先观察一个常用的input_report_key
file:kernel-3.18/drivers/input/input.h
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_KEY, code, !!value);
}
file:kernel-3.18/drivers/input/input.c
void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
{
	input_handle_event(dev, type, code, value);
}
static void input_handle_event(struct input_dev *dev,
			       unsigned int type, unsigned int code, int value)
{
	int disposition;


	disposition = input_get_disposition(dev, type, code, &value);


	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
		dev->event(dev, type, code, value);


	if (!dev->vals)
		return;


	if (disposition & INPUT_PASS_TO_HANDLERS) {
		struct input_value *v;


		if (disposition & INPUT_SLOT) {
			v = &dev->vals[dev->num_vals++];
			v->type = EV_ABS;
			v->code = ABS_MT_SLOT;
			v->value = dev->mt->slot;
		}


		v = &dev->vals[dev->num_vals++];
		v->type = type;
		v->code = code;
		v->value = value;
	}


	if (disposition & INPUT_FLUSH) {
		if (dev->num_vals >= 2)
			input_pass_values(dev, dev->vals, dev->num_vals);
		dev->num_vals = 0;
	} else if (dev->num_vals >= dev->max_vals - 2) {
		dev->vals[dev->num_vals++] = input_value_sync;
		input_pass_values(dev, dev->vals, dev->num_vals);
		dev->num_vals = 0;
	}


}
static void input_pass_values(struct input_dev *dev,
			      struct input_value *vals, unsigned int count)
{
	struct input_handle *handle;
	struct input_value *v;


	if (!count)
		return;


	rcu_read_lock();


	handle = rcu_dereference(dev->grab);
	if (handle) {
		count = input_to_handler(handle, vals, count);
	} else {
		list_for_each_entry_rcu(handle, &dev->h_list, d_node)
			if (handle->open)
				count = input_to_handler(handle, vals, count);
	}


	rcu_read_unlock();


	add_input_randomness(vals->type, vals->code, vals->value);


	/* trigger auto repeat for key events */
	for (v = vals; v != vals + count; v++) {
		if (v->type == EV_KEY && v->value != 2) {
			if (v->value)
				input_start_autorepeat(dev, v->code);
			else
				input_stop_autorepeat(dev);
		}
	}
}
static unsigned int input_to_handler(struct input_handle *handle,
			struct input_value *vals, unsigned int count)
{
	struct input_handler *handler = handle->handler;
	struct input_value *end = vals;
	struct input_value *v;


	for (v = vals; v != vals + count; v++) {
		if (handler->filter &&
		    handler->filter(handle, v->type, v->code, v->value))
			continue;
		if (end != v)
			*end = *v;
		end++;
	}


	count = end - vals;
	if (!count)
		return 0;


	if (handler->events)
		handler->events(handle, vals, count);
	else if (handler->event)
		for (v = vals; v != end; v++)
			handler->event(handle, v->type, v->code, v->value);


	return count;
}
file:kernel-3.18/drivers/input/evdev.c
static void evdev_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)
{
	struct input_value vals[] = { { type, code, value } };


	evdev_events(handle, vals, 1);
}
static void evdev_pass_values(struct evdev_client *client,
			const struct input_value *vals, unsigned int count,
			ktime_t mono, ktime_t real)
{
	struct evdev *evdev = client->evdev;
	const struct input_value *v;
	struct input_event event;
	bool wakeup = false;


	if (client->revoked)
		return;


	event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
				      mono : real);


	/* Interrupts are disabled, just acquire the lock. */
	spin_lock(&client->buffer_lock);


	for (v = vals; v != vals + count; v++) {
		event.type = v->type;
		event.code = v->code;
		event.value = v->value;
		__pass_event(client, &event);
		if (v->type == EV_SYN && v->code == SYN_REPORT)
			wakeup = true;
	}


	spin_unlock(&client->buffer_lock);


	if (wakeup)
		wake_up_interruptible(&evdev->wait);
}
static void __pass_event(struct evdev_client *client,
			 const struct input_event *event)
{
	client->buffer[client->head++] = *event;
	client->head &= client->bufsize - 1;


	if (unlikely(client->head == client->tail)) {
		/*
		 * This effectively "drops" all unconsumed events, leaving
		 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
		 */
		client->tail = (client->head - 2) & (client->bufsize - 1);
		
		//将需要上报的数据存放在buffer中
		client->buffer[client->tail].time = event->time;
		client->buffer[client->tail].type = EV_SYN;
		client->buffer[client->tail].code = SYN_DROPPED;
		client->buffer[client->tail].value = 0;


		client->packet_head = client->tail;
		if (client->use_wake_lock)
			wake_unlock(&client->wake_lock);
	}


	if (event->type == EV_SYN && event->code == SYN_REPORT) {
		client->packet_head = client->head;
		if (client->use_wake_lock)
			wake_lock(&client->wake_lock);
		kill_fasync(&client->fasync, SIGIO, POLL_IN);
	}
}
4.读取键值的实现
当上层进行read后,调用该设备的read函数:
static ssize_t evdev_read(struct file *file, char __user *buffer,
			  size_t count, loff_t *ppos)
{
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;
	struct input_event event;
	size_t read = 0;
	int error;


	if (count != 0 && count < input_event_size())
		return -EINVAL;


	for (;;) {
		if (!evdev->exist || client->revoked)
			return -ENODEV;


		if (client->packet_head == client->tail &&
		    (file->f_flags & O_NONBLOCK))
			return -EAGAIN;


		/*
		 * count == 0 is special - no IO is done but we check
		 * for error conditions (see above).
		 */
		if (count == 0)
			break;


		while (read + input_event_size() <= count &&
		       evdev_fetch_next_event(client, &event)) {


			if (input_event_to_user(buffer + read, &event))
				return -EFAULT;


			read += input_event_size();
		}


		if (read)
			break;


		if (!(file->f_flags & O_NONBLOCK)) {
			error = wait_event_interruptible(evdev->wait,
					client->packet_head != client->tail ||
					!evdev->exist || client->revoked);
			if (error)
				return error;
		}
	}


	return read;
}
file:kernel-3.18/drivers/input/input-compat.c
int input_event_to_user(char __user *buffer,
			const struct input_event *event)
{
	if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
		struct input_event_compat compat_event;


		compat_event.time.tv_sec = event->time.tv_sec;
		compat_event.time.tv_usec = event->time.tv_usec;
		compat_event.type = event->type;
		compat_event.code = event->code;
		compat_event.value = event->value;


		if (copy_to_user(buffer, &compat_event,
				 sizeof(struct input_event_compat)))
			return -EFAULT;


	} else {
		//最终使用copy_to_user将数据上传到用户空间
		if (copy_to_user(buffer, event, sizeof(struct input_event)))
			return -EFAULT;
	}


	return 0;
}
5.小结
通过上面4点分析,我们大致可以看出,输入子系统的本质就是利用input_dev、input_handler、input_handle
三个数据结构进行数据的转换和传递,其中input_dev用于数据的描述,input_handler用户事件的处理,input_handle
用于沟通input_dev、input_handler,当input_dev与input_handler挂接上了后,会创建相应的字符设备,并绑定相应的
读写方法,当底层有数据后,上报的数据存放在buffer数组中,当用户空间调用open 打开/dev/input/eventxx 然后调用
read 读取键值时,底层调用evdev_read并最终利用copy_to_user将内核空间数据传递到用户空间。












0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:241023次
    • 积分:3742
    • 等级:
    • 排名:第8658名
    • 原创:81篇
    • 转载:301篇
    • 译文:2篇
    • 评论:37条
    Android 系统开发
    对android系统中核心架构进行分析、总结,深入学习整套系统!