Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> MTK6735M(Android6.0)-Input子系統實現

MTK6735M(Android6.0)-Input子系統實現

編輯:關於Android編程

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將內核空間數據傳遞到用戶空間。
 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved