input子系統(二)

很早之前接觸的input的子系統,總結一下其input數據是如何同步的:
input設備驅動在probe中會註冊input device,最終會調用以下函數:

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

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

    handle->dev = dev;
    handle->handler = handler;
    handle->name = "evbug";

    error = input_register_handle(handle);//實現註冊,將handle分配結構體並且賦值到RCU同步機制列表
    if (error)
        goto err_free_handle;

    error = input_open_device(handle);//使能設備驅動,即打開中斷,喚醒驅動檢測事件
    if (error)
        goto err_unregister_handle;

    printk(KERN_DEBUG pr_fmt("Connected device: %s (%s at %s)\n"),
           dev_name(&dev->dev),
           dev->name ?: "unknown",
           dev->phys ?: "unknown");

    return 0;

 err_unregister_handle:
    input_unregister_handle(handle);
 err_free_handle:
    kfree(handle);
    return error;
}
數據最後上傳到這裏:
static void evdev_events(struct input_handle *handle,
			 const struct input_value *vals, unsigned int count)
{
	struct evdev *evdev = handle->private;
	struct evdev_client *client;
	ktime_t time_mono, time_real;

	time_mono = ktime_get();
	time_real = ktime_mono_to_real(time_mono);

	rcu_read_lock();

	client = rcu_dereference(evdev->grab);

	if (client)
		evdev_pass_values(client, vals, count, time_mono, time_real);
	else
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_values(client, vals, count,
					  time_mono, time_real);

	rcu_read_unlock();
}

最終數據放在了client結構體中,client結構體其實就是從handle來的:

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);
}
以下的handle.private 被賦值了evdev,evdev的結構體也是這時候被內核分配的內存。後面在傳遞數據時候會取回這邊的evdev結構體賦值input數據到input_event 結構體,該部分的數據會被上層調用。
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	struct evdev *evdev;
	int minor;
	int dev_no;
	int error;
。。。。。。

	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler;
	evdev->handle.private = evdev;
。。。。
}
全程過程中使用RCU的數據同步機制,上層的用戶可以有N個用戶去讀取,而內核態可以去不斷的去寫數據互不干擾。關於RCU機制見之前發的文檔。

在evdev.c中定義了一整套的操作函數:

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,
    .llseek        = no_llseek,
};
並且在connect的時候完成了註冊行爲,個人覺得頂層android那邊的open/read/wirte就是調用這邊的接口去獲取事件的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章