linux驱动input子系统学习六(数据传输流程)

上一篇博客中曾使用input_report_key函数来上报键值和按键状态,现在我们就分析一下它的上报流程。

可以看系统提供了很多来上报各种信息的函数(适用,键盘,鼠标,触摸屏等各种上报信息)

它们都是掉用同一的接口input_enent来实现的,所以input_event才是我们分析的重点。


static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_KEY, code, !!value);    /* 注意这里value是两次取反,即输入给input_event的只可能是0或1 */
}

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

看一下input_event的实现

/**
 * 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().
 *
 * NOTE: input_event() may be safely used right after input device was
 * allocated with input_allocate_device(), even before it is registered
 * with input_register_device(), but the event will not reach any of the
 * input handlers. Such early invocation of input_event() may be used
 * to 'seed' initial state of a switch or initial position of absolute
 * axis, etc.
 */
void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
{
	unsigned long flags;

        /*调用 is_event_supported()函数检查输入设备是否支持该事件*/
	if (is_event_supported(type, dev->evbit, EV_MAX)) {

                /* 调用 spin_lock_irqsave()函数对将事件锁锁定 */
		spin_lock_irqsave(&dev->event_lock, flags);
                /* add_input_randomness()函数对事件发送没有一点用处,只是用来对随机数熵池增加一些贡献,因为按键输入是一种随机事件,所以对熵池是有贡献的 */  
		add_input_randomness(type, code, value);
                /* 函数来继续输入子系统的相关模块发送数据。该函数较为复杂,下面单独进行分析 */
		input_handle_event(dev, type, code, value);
		spin_unlock_irqrestore(&dev->event_lock, flags);
	}
}

上面用到的两个工具函数

/* 测试addr数组中第nr位是不是为1,是则返回1,不是则返回0 */
static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
{
	return ((1UL << (nr % BITS_PER_LONG)) &
		(((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
}

static inline int is_event_supported(unsigned int code,
				     unsigned long *bm, unsigned int max)
{
	return code <= max && test_bit(code, bm);
}

按键上报的处理函数。


#define INPUT_IGNORE_EVENT	0
#define INPUT_PASS_TO_HANDLERS	1
#define INPUT_PASS_TO_DEVICE	2
#define INPUT_PASS_TO_ALL	(INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)

static void input_handle_event(struct input_dev *dev,
			       unsigned int type, unsigned int code, int value)
{
    /* 定义了一个 disposition 变量,该变量表示使用什么样的方式处理事件。此处初始化为 INPUT_IGNORE_EVENT,表示如果后面没有对该变量重新赋值,则忽略这个事件 */ 
	int disposition = INPUT_IGNORE_EVENT;

	switch (type) {

	case EV_SYN:
		switch (code) {
		case SYN_CONFIG:
			disposition = INPUT_PASS_TO_ALL;
			break;

		case SYN_REPORT:
			if (!dev->sync) {
				dev->sync = 1;
				disposition = INPUT_PASS_TO_HANDLERS;
			}
			break;
		case SYN_MT_REPORT:
			dev->sync = 0;
			disposition = INPUT_PASS_TO_HANDLERS;
			break;
		}
		break;

	case EV_KEY:
            /* 函数判断是否支持该按键,同时判断按键状态是否改变过 */
		if (is_event_supported(code, dev->keybit, KEY_MAX) &&
		    !!test_bit(code, dev->key) != value) {    /* 判断value是不是改变过,比如某个键长按,则它在按下过程一致是1 */
                        /* EV_KEY正常是不会有0和1之外的value值的 */
			if (value != 2) {
				__change_bit(code, dev->key);        /* 把key中code位取反 */
				if (value) 
                                        /* 如果刚按下键,则开始重复上报数据(具体报不报还要看dev的配置) */
					input_start_autorepeat(dev, code);
				else
                                        /* 松开按键则停止重复上报 */
					input_stop_autorepeat(dev);     
			}

			disposition = INPUT_PASS_TO_HANDLERS;    /* 更新disposition */
		}
		break;

	case EV_SW:
		if (is_event_supported(code, dev->swbit, SW_MAX) &&
		    !!test_bit(code, dev->sw) != value) {

			__change_bit(code, dev->sw);
			disposition = INPUT_PASS_TO_HANDLERS;
		}
		break;

	case EV_ABS:
		if (is_event_supported(code, dev->absbit, ABS_MAX)) {

			if (test_bit(code, input_abs_bypass)) {
				disposition = INPUT_PASS_TO_HANDLERS;
				break;
			}

			value = input_defuzz_abs_event(value,
					dev->abs[code], dev->absfuzz[code]);

			if (dev->abs[code] != value) {
				dev->abs[code] = value;
				disposition = INPUT_PASS_TO_HANDLERS;
			}
		}
		break;

	case EV_REL:
		if (is_event_supported(code, dev->relbit, REL_MAX) && value)
			disposition = INPUT_PASS_TO_HANDLERS;

		break;

	case EV_MSC:
		if (is_event_supported(code, dev->mscbit, MSC_MAX))
			disposition = INPUT_PASS_TO_ALL;

		break;

	case EV_LED:
		if (is_event_supported(code, dev->ledbit, LED_MAX) &&
		    !!test_bit(code, dev->led) != value) {

			__change_bit(code, dev->led);
			disposition = INPUT_PASS_TO_ALL;
		}
		break;

	case EV_SND:
		if (is_event_supported(code, dev->sndbit, SND_MAX)) {

			if (!!test_bit(code, dev->snd) != !!value)
				__change_bit(code, dev->snd);
			disposition = INPUT_PASS_TO_ALL;
		}
		break;

	case EV_REP:
		if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
			dev->rep[code] = value;
			disposition = INPUT_PASS_TO_ALL;
		}
		break;

	case EV_FF:
		if (value >= 0)
			disposition = INPUT_PASS_TO_ALL;
		break;

	case EV_PWR:
		disposition = INPUT_PASS_TO_ALL;
		break;
	}
    
        /* 处理 EV_SYN 事件,这里并不对其进行关心。*/   */
	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
		dev->sync = 0;

        /* 一些特殊事件需要对dev也上报(比如led点灯等),通常我们的dev里面不会设置event事件 */
	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
		dev->event(dev, type, code, value);
        /* 真正的向上层handler上报事件 */
	if (disposition & INPUT_PASS_TO_HANDLERS)
		input_pass_event(dev, type, code, value);
}

上报事件的处理(这里主要是区分是固定给某个handler上报,还是查询目前打开着event的就上报)

/*
 * Pass event first through all filters and then, if event has not been
 * filtered out, through all open handles. This function is called with
 * dev->event_lock held and interrupts disabled.
 */
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();
    /*  grab 是强制为 input device 绑定的 handler,如果存在就直接调用绑定的handler里面的event即可 */
	handle = rcu_dereference(dev->grab);
	if (handle)
		handle->handler->event(handle, type, code, value);
	else {
		bool filtered = false;
        /* 如果没有为device绑定具体的handle,则遍历这个dev上的所有handle,同时会向应用层已经 open打开过的发送具体的事件信息,
        handle里面的open是做打开次数统计的,非0即有应用程序打开。这里要注意的是向所有的打开的都发送事件 */
		list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
			if (!handle->open)
				continue;
            
			handler = handle->handler;
			if (!handler->filter) {            /* 看handler要不要进行再次筛选,通常evdev和mousedev等都是不需要进行筛选了 */
				if (filtered)
					break;
                                /* 真正的调用handler里面的event向应用程序上报数据 */
				handler->event(handle, type, code, value);    

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

	rcu_read_unlock();
}

具体的handler(以evdev为例)层的上报我在input子系统学习四已经分析了。这里我直接留拷贝过来了。完整的过程还是要这几篇都看的。


/*
 * Pass incoming event to all connected clients.
 */
static void evdev_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)
{
	struct evdev *evdev = handle->private;  /*  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();
     
     /* 如果该evdev有个专用的client,那么就将事件发给它如果该evdev不存在专用的client,那个就把该事件发送给evdev上client_list链表上所有的client */  
	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();
        /* 唤醒等到队列上的进程,注意这里是唤醒,肯定有别的地方定义(connect)和让它睡眠(read),注意我会标记的 */
	wake_up_interruptible(&evdev->wait);
}

使用异步通知通知上层。


static void evdev_pass_event(struct evdev_client *client,
			     struct input_event *event)
{
	/*
	 * Interrupts are disabled, just acquire the lock
	 */
	spin_lock(&client->buffer_lock);
	wake_lock_timeout(&client->wake_lock, 5 * HZ);   /* 设置一个5s的超时锁,5s后自动解锁,超时后内核可以进入休眠模式(即5s没输入则进入休眠) */
	client->buffer[client->head++] = *event;    /* 把键值写入client缓冲队列中,方便app层读(app层可能一次读多个输入信息) */
	client->head &= EVDEV_BUFFER_SIZE - 1;
	spin_unlock(&client->buffer_lock);
 
	if (event->type == EV_SYN)
		kill_fasync(&client->fasync, SIGIO, POLL_IN);    /* 发送一个异步通知,通知该打开该client的应用程序,执行信号处理函数*/

上面就是所有的从按键发生到按键事件发生给应用程序的整个流程。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/81272843