Linux Input子系统3

Linux Input子系统3(基于Linux6.6)---输入核心层介绍

 


在 Linux 内核中,输入核心层(或称为输入子系统的核心)是 input 子系统 的关键组成部分,负责管理各种输入设备、处理输入事件,并提供与用户空间的接口。输入核心层是输入子系统的基础,它承载了输入设备的注册、事件的处理、以及与用户空间的交互等核心功能。

输入核心层的主要职责包括:

  • 管理输入设备:处理输入设备的注册与注销,维护输入设备的生命周期。
  • 事件的捕捉与传递:处理来自设备的输入事件,确保事件的正确传递到相应的用户空间应用程序。
  • 输入事件类型的支持:支持多种输入事件类型(如键盘按键、鼠标移动、触摸屏坐标等)。
  • 提供与用户空间的接口:通过设备文件接口(如 /dev/input/eventX),允许用户空间应用读取输入事件。

一、input子系统的实现

注册input子系统本身很简单只需要在sysfs中注册号相应的设备类和主设备号即可。

 drivers/input/input.c

static char *input_devnode(const struct device *dev, umode_t *mode)
{
	return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev));
}

struct class input_class = {
	.name		= "input",
	.devnode	= input_devnode,
};
EXPORT_SYMBOL_GPL(input_class);
 
static int __init input_init(void)
{
	int err;
 
	err = class_register(&input_class);    /* 在sysfs中的class类下注册一个input的类 */
	if (err) {
		printk(KERN_ERR "input: unable to register input_dev class\n");
		return err;
	}
 
	err = input_proc_init();    /* 初始化proc文件系统 */
	if (err)
		goto fail1;
 
	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);    /* 注册字符设备驱动 */
	if (err) {
		printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}
 
	return 0;
 
 fail2:	input_proc_exit();
 fail1:	class_unregister(&input_class);
	return err;
}

取消注册就更简单来,和注册相反即可。

drivers/input/input.c

static void __exit input_exit(void)
{
	input_proc_exit();
	unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
				 INPUT_MAX_CHAR_DEVICES);
	class_unregister(&input_class);
}

二、设备驱动层提供的接口函数

2.1、初始化

设备描述结构体变量,同时初始化一些通用性数据。

drivers/input/input.c 

/**
 * input_allocate_device - allocate memory for new input device
 *
 * Returns prepared struct input_dev or %NULL.
 *
 * NOTE: Use input_free_device() to free devices that have not been
 * registered; input_unregister_device() should be used for already
 * registered devices.
 */
struct input_dev *input_allocate_device(void)
{
	static atomic_t input_no = ATOMIC_INIT(-1);
	struct input_dev *dev;
 
	dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);    /* 申请内存 */
	if (dev) {        
		dev->dev.type = &input_dev_type;       /* 绑定设备信息 */
		dev->dev.class = &input_class;         /* 绑定在sysfs所属的类,后面的register里面的add_device后才能出现在sysfs下的input文件夹下 */
		device_initialize(&dev->dev);          /* 设备初始化 */
		mutex_init(&dev->mutex);
		spin_lock_init(&dev->event_lock);
		INIT_LIST_HEAD(&dev->h_list);         /* 初始化链表 */
		INIT_LIST_HEAD(&dev->node);
 
		__module_get(THIS_MODULE);
	}
 
	return dev;
}
EXPORT_SYMBOL(input_allocate_device);

2.2、设置dev设备的能力

只有注册前设置来它拥有的能力,将来该设备注册后,发送的信息才能被上报。

注意:该函数每次只能设置一个能力。

drivers/input/input.c 

/* 根据type类型,设置code到相应的typebit */
/**
 * input_set_capability - mark device as capable of a certain event
 * @dev: device that is capable of emitting or accepting event
 * @type: type of the event (EV_KEY, EV_REL, etc...)
 * @code: event code
 *
 * In addition to setting up corresponding bit in appropriate capability
 * bitmap the function also adjusts dev->evbit.
 */
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
	if (type < EV_CNT && input_max_code[type] &&
	    code > input_max_code[type]) {
		pr_err("%s: invalid code %u for type %u\n", __func__, code,
		       type);
		dump_stack();
		return;
	}

	switch (type) {
	case EV_KEY:
		__set_bit(code, dev->keybit);
		break;

	case EV_REL:
		__set_bit(code, dev->relbit);
		break;

	case EV_ABS:
		input_alloc_absinfo(dev);
		__set_bit(code, dev->absbit);
		break;

	case EV_MSC:
		__set_bit(code, dev->mscbit);
		break;

	case EV_SW:
		__set_bit(code, dev->swbit);
		break;

	case EV_LED:
		__set_bit(code, dev->ledbit);
		break;

	case EV_SND:
		__set_bit(code, dev->sndbit);
		break;

	case EV_FF:
		__set_bit(code, dev->ffbit);
		break;

	case EV_PWR:
		/* do nothing */
		break;

	default:
		pr_err("%s: unknown type %u (code %u)\n", __func__, type, code);
		dump_stack();
		return;
	}

	__set_bit(type, dev->evbit);
}
EXPORT_SYMBOL(input_set_capability);

2.3、具体设备注册

drivers/input/input.c 

int input_register_device(struct input_dev *dev)
{
	struct input_devres *devres = NULL;
	struct input_handler *handler;
	unsigned int packet_size;
	const char *path;
	int error;

	if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
		dev_err(&dev->dev,
			"Absolute device without dev->absinfo, refusing to register\n");
		return -EINVAL;
	}

	if (dev->devres_managed) {
		devres = devres_alloc(devm_input_device_unregister,
				      sizeof(*devres), GFP_KERNEL);
		if (!devres)
			return -ENOMEM;

		devres->input = dev;
	}

	/* Every input device generates EV_SYN/SYN_REPORT events. */
	__set_bit(EV_SYN, dev->evbit);

	/* KEY_RESERVED is not supposed to be transmitted to userspace. */
	__clear_bit(KEY_RESERVED, dev->keybit);

	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
	input_cleanse_bitmasks(dev);

	packet_size = input_estimate_events_per_packet(dev);
	if (dev->hint_events_per_packet < packet_size)
		dev->hint_events_per_packet = packet_size;

	dev->max_vals = dev->hint_events_per_packet + 2;
	dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
	if (!dev->vals) {
		error = -ENOMEM;
		goto err_devres_free;
	}

	/*
	 * If delay and period are pre-set by the driver, then autorepeating
	 * is handled by the driver itself and we don't do it in input.c.
	 */
	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
		input_enable_softrepeat(dev, 250, 33);

	if (!dev->getkeycode)
		dev->getkeycode = input_default_getkeycode;

	if (!dev->setkeycode)
		dev->setkeycode = input_default_setkeycode;

	if (dev->poller)
		input_dev_poller_finalize(dev->poller);

	error = device_add(&dev->dev);
	if (error)
		goto err_free_vals;

	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
	pr_info("%s as %s\n",
		dev->name ? dev->name : "Unspecified device",
		path ? path : "N/A");
	kfree(path);

	error = mutex_lock_interruptible(&input_mutex);
	if (error)
		goto err_device_del;

	list_add_tail(&dev->node, &input_dev_list);

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

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);

	if (dev->devres_managed) {
		dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
			__func__, dev_name(&dev->dev));
		devres_add(dev->dev.parent, devres);
	}
	return 0;

err_device_del:
	device_del(&dev->dev);
err_free_vals:
	kfree(dev->vals);
	dev->vals = NULL;
err_devres_free:
	devres_free(devres);
	return error;
}
EXPORT_SYMBOL(input_register_device);

2.4、handler和dev做匹配

如果匹配上,则把两者绑定。

drivers/input/input.c 

  
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);    /* 匹配handler和dev */
	if (!id)
		return -ENODEV;
 
	error = handler->connect(handler, dev, id);    /* 做具体的绑定handler和dev工作,由具体的事驱动层实现 */
	if (error && error != -ENODEV)
		printk(KERN_ERR
			"input: failed to attach handler %s to device %s, "
			"error: %d\n",
			handler->name, kobject_name(&dev->dev.kobj), error);
 
	return error;
}

2.5、匹配handler和dev

每一个事件驱动层在实现的时候都要实现一个struct input_device_id的表(数组),用来表明该事件驱动可以支持的设备。

 include/linux/mod_devicetable.h

 struct input_device_id {
 
	kernel_ulong_t flags;    /* flags表明下面的四个要不要匹配,已经用相关宏定义好 */
 
	__u16 bustype;
	__u16 vendor;
	__u16 product;
	__u16 version;
 
	kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
	kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
	kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
	kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
	kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
	kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
	kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
	kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
	kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
 
	kernel_ulong_t driver_info;
};

如果填充时在handler里面的在flags标志里面置位了下面的某一个,则匹配时就会检查响应的(总线类型,厂商,设备号,设备版本)。如果没有置位相应位,则表明匹配时不检查响应的标志位。

include/linux/mod_devicetable.h 

#define INPUT_DEVICE_ID_MATCH_BUS	1
#define INPUT_DEVICE_ID_MATCH_VENDOR	2
#define INPUT_DEVICE_ID_MATCH_PRODUCT	4
#define INPUT_DEVICE_ID_MATCH_VERSION	8

同时也可以置位需要匹配类型,

 #define INPUT_DEVICE_ID_MATCH_EVBIT	0x0010
#define INPUT_DEVICE_ID_MATCH_KEYBIT	0x0020
#define INPUT_DEVICE_ID_MATCH_RELBIT	0x0040
#define INPUT_DEVICE_ID_MATCH_ABSBIT	0x0080
#define INPUT_DEVICE_ID_MATCH_MSCIT	0x0100
#define INPUT_DEVICE_ID_MATCH_LEDBIT	0x0200
#define INPUT_DEVICE_ID_MATCH_SNDBIT	0x0400
#define INPUT_DEVICE_ID_MATCH_FFBIT	0x0800
#define INPUT_DEVICE_ID_MATCH_SWBIT	0x1000

下面就是真正的通过input_device_id来匹配

drivers/input/input.c

  
static const struct input_device_id *input_match_device(struct input_handler *handler,
							struct input_dev *dev)
{
	const struct input_device_id *id;
	int i;
   /* input_device_id是一个数组,从前向后依次遍历id_table中和dev里面的id和xxbit中完全匹配的 */
	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;
} 

bool input_match_device_id(const struct input_dev *dev,
			   const struct input_device_id *id)
{
 
 
/* 正如前面描述的handler中id_table置位了相应标志位才做匹配检查 */
	if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
		if (id->bustype != dev->id.bustype)
			return false;

	if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
		if (id->vendor != dev->id.vendor)
			return false;

	if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
		if (id->product != dev->id.product)
			return false;

	if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
		if (id->version != dev->id.version)
			return false;
        /* 同一id_table里面的flags匹配成功,才能MATCH_BIT匹配 */
	if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
	    !bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
	    !bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
	    !bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
	    !bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
	    !bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
	    !bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
	    !bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
	    !bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
	    !bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
		return false;
	}
        /* 看handler层有没有再定义macth匹配函数,如果没有,那到这里就是已经找到id了,否则,还要通过handler里面的match才能确定就是该id */
	return true;
}
EXPORT_SYMBOL(input_match_device_id);

以某一个来分析MATCH_BIT宏。

 #define MATCH_BIT(bit, max) \
		for (i = 0; i < BITS_TO_LONGS(max); i++) \
			if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
				break; \
		if (i != BITS_TO_LONGS(max)) \
			continue;

假设以keybit为例,宏替换后如下。可以发现,就是匹配handler里面的keybit值的dev里面keybit值。即每一项都要满足id_table中,该项(keybit的值)。即dev的key值可以有 1 2 3 4 5 6,handler中id_table可以只有1,2,3。这样也能匹配成功。

 for (i = 0; i < BITS_TO_LONGS(KEY_MAX); i++) 
	if ((id->keybit[i] & dev->keybit[i]) != id->keybit[i]) 
		break; 
if (i != BITS_TO_LONGS(KEY_MAX)) 
	continue;

以evdev的id_table分别举例,方便上面的理解。

首先看evdev的id_table:可以发现,它里面除了driver_info其他都是空的。即evdev的handler可以匹配所有的device

即空的flags:不用匹配flags

空的xxbit,不用匹配xxbit

 static const struct input_device_id evdev_ids[] = {
	{ .driver_info = 1 },	/* Matches all devices */
	{ },			/* Terminating zero entry */
};

2.6、unregister

它和register的顺序刚好相反,就不详细分析了

/**
 * input_unregister_device - unregister previously registered device
 * @dev: device to be unregistered
 *
 * This function unregisters an input device. Once device is unregistered
 * the caller should not try to access it as it may get freed at any moment.
 */
void input_unregister_device(struct input_dev *dev)
{
	if (dev->devres_managed) {
		WARN_ON(devres_destroy(dev->dev.parent,
					devm_input_device_unregister,
					devm_input_device_match,
					dev));
		__input_unregister_device(dev);
		/*
		 * We do not do input_put_device() here because it will be done
		 * when 2nd devres fires up.
		 */
	} else {
		__input_unregister_device(dev);
		input_put_device(dev);
	}
}
EXPORT_SYMBOL(input_unregister_device);

至此,和核心层中input子系统和device相关的已经分析完毕。

三、事件驱动层提供的接口函数

接下来我们分析handler部分

3.1、handler注册

 drivers/input/input.c

  
/**
 * input_register_handler - register a new input handler
 * @handler: handler to be registered
 *
 * This function registers a new input handler (interface) for input
 * devices in the system and attaches it to all input devices that
 * are compatible with the handler.
 */
int input_register_handler(struct input_handler *handler)
{
	struct input_dev *dev;
	int retval;
 
	retval = mutex_lock_interruptible(&input_mutex);
	if (retval)
		return retval;
 
	INIT_LIST_HEAD(&handler->h_list);    /* 初始化自身链表 */
 
	if (handler->fops != NULL) {
		if (input_table[handler->minor >> 5]) {    /* 每种handler的minor以32的倍数为base ,这里是检查当前handler类是不是已经注册过了,比如evdev的base是64,那么他就在input_table[2]中*/
			retval = -EBUSY;
			goto out;
		}
		input_table[handler->minor >> 5] = handler;    /* 上面检查没被注册过,测在这里注册进input_table中 */
	}
 
	list_add_tail(&handler->node, &input_handler_list); /* 把当前handler加入到input核心增维护的两条重要链表之一的handler链表中去 */
         /* 用当前的handler遍历核心层维护的两条重要链表之一的dev链表,并尝试匹配 */
	list_for_each_entry(dev, &input_dev_list, node)
		input_attach_handler(dev, handler);   
 
	input_wakeup_procfs_readers();        /* 更新proce文件系统 */
 
 out:
	mutex_unlock(&input_mutex);
	return retval;
}

匹配算法和device那边的完全一样。

这里要说明的是为什么即在注册handler的时候匹配一遍,又要在注册device的时候匹配一次。

这是因为,通常系统不能确定是handler先注册,还是dev先注册。

假设只在注册dev的时候匹配handler(注册handler去掉匹配那几句代码  ),结果,devi先执行的,因为handler还没被注册,所以dev匹配失败。当注册handler的时候,因为没有了再次匹配,所以两者无法再接续到一块了。所以要在handler和dev注册都匹配一次,当然肯定是后面注册的才能匹配成。

3.2、handler解注册

 drivers/input/input.c

  
/**
 * input_unregister_handler - unregisters an input handler
 * @handler: handler to be unregistered
 *
 * This function disconnects a handler from its input devices and
 * removes it from lists of known handlers.
 */
void input_unregister_handler(struct input_handler *handler)
{
	struct input_handle *handle, *next;
 
	mutex_lock(&input_mutex);
 
	list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
		handler->disconnect(handle);    
	WARN_ON(!list_empty(&handler->h_list));
 
	list_del_init(&handler->node);
 
	if (handler->fops != NULL)
		input_table[handler->minor >> 5] = NULL;
 
	input_wakeup_procfs_readers();
 
	mutex_unlock(&input_mutex);
}

3.3、handle注册

还有一个注册函数就是handle的注册(与其说注册,其实就是把handler和dev的链表加入到handle的数据中)

 drivers/input/input.c

  
/**
 * input_register_handle - register a new input handle
 * @handle: handle to register
 *
 * This function puts a new input handle onto device's
 * and handler's lists so that events can flow through
 * it once it is opened using input_open_device().
 *
 * This function is supposed to be called from handler's
 * connect() method.
 */
int input_register_handle(struct input_handle *handle)
{
	struct input_handler *handler = handle->handler;
	struct input_dev *dev = handle->dev;
	int error;
 
	/*
	 * We take dev->mutex here to prevent race with
	 * input_release_device().
	 */
	error = mutex_lock_interruptible(&dev->mutex);
	if (error)
		return error;
 
	/*
	 * Filters go to the head of the list, normal handlers
	 * to the tail.
	 */
	if (handler->filter)
		list_add_rcu(&handle->d_node, &dev->h_list);
	else
		list_add_tail_rcu(&handle->d_node, &dev->h_list);   /* 把dev加入到handle */
 
	mutex_unlock(&dev->mutex);
 
	/*
	 * Since we are supposed to be called from ->connect()
	 * which is mutually exclusive with ->disconnect()
	 * we can't be racing with input_unregister_handle()
	 * and so separate lock is not needed here.
	 */
	list_add_tail_rcu(&handle->h_node, &handler->h_list);    /* 把handler加入到handle */
 
	if (handler->start)
		handler->start(handle);
 
	return 0;
}

4.handle解注册

drivers/input/input.c

 /**
 * input_unregister_handle - unregister an input handle
 * @handle: handle to unregister
 *
 * This function removes input handle from device's
 * and handler's lists.
 *
 * This function is supposed to be called from handler's
 * disconnect() method.
 */
void input_unregister_handle(struct input_handle *handle)
{
	struct input_dev *dev = handle->dev;
 
	list_del_rcu(&handle->h_node);
 
	/*
	 * Take dev->mutex to prevent race with input_release_device().
	 */
	mutex_lock(&dev->mutex);
	list_del_rcu(&handle->d_node);
	mutex_unlock(&dev->mutex);
 
	synchronize_rcu();
}

RCU(Read-Copy-Update)是一种优化的数据结构更新机制,广泛用于 Linux 内核中,特别是在需要高并发读取和低开销写入的场景下。RCU 的核心思想是将读操作与写操作分离开来,允许多个读操作并行进行,而写操作则通过创建新的数据副本、替换旧数据的方式进行,从而减少锁的竞争和延迟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值