input work flow

本文深入探讨Linux内核的输入子系统和Evdev驱动的工作原理,详细解析了事件处理流程,包括事件收集、环形队列管理、事件上报等关键步骤,以及如何将事件传递给相应的input设备。文章还介绍了如何在驱动中注册事件类型和扫描码,以及如何在input系统中清除未使用的事件类型和扫描码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<pre name="code" class="plain">diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index f4897c8..5fb50a3 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -61,10 +61,12 @@ struct evdev_client {
 static void __pass_event(struct evdev_client *client,
 			 const struct input_event *event)
 {
+//:将事件放到一个环形队列中,每个input设备会对应一个固定大小的环形缓冲区
 	client->buffer[client->head++] = *event;
 	client->head &= client->bufsize - 1;
 
 	if (unlikely(client->head == client->tail)) {
+//:如果环形缓冲区满了,就在最后追加一个值为SYN_DROPPED的EV_SYN事件,用于告诉应用层:这里出了问题。
 		/*
 		 * This effectively "drops" all unconsumed events, leaving
 		 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
@@ -1113,6 +1115,7 @@ static struct input_handler evdev_handler = {
 
 static int __init evdev_init(void)
 {
+//:evdev用于生成每个input设备的事件操作节点(eventX)
 	return input_register_handler(&evdev_handler);
 }
 
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 6c58ff0..cfc0ea9 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -116,7 +116,7 @@ static unsigned int input_to_handler(struct input_handle *handle,
 	count = end - vals;
 	if (!count)
 		return 0;
-
+//显然这里的调用会把事件发送到evdev.c中,但是,在input.c中我却无法找到何时这部分代码会被执行!?????
 	if (handler->events)
 		handler->events(handle, vals, count);
 	else if (handler->event)
@@ -282,7 +282,7 @@ static int input_get_disposition(struct input_dev *dev,
 			break;
 		}
 		break;
-
+//17:本次上报的是事件类型是:EV_KEY
 	case EV_KEY:
 		if (is_event_supported(code, dev->keybit, KEY_MAX)) {
 
@@ -291,9 +291,10 @@ static int input_get_disposition(struct input_dev *dev,
 				disposition = INPUT_PASS_TO_HANDLERS;
 				break;
 			}
-
+//18:和上次该扫描码的值进行对比,如果两个值不一样,才会进一步处理,这样可以过滤驱动对同一个扫描码连续上报同样的值。
+//如果前后两次的值一样,input子系统可以在这里将本次事件忽略掉。
 			if (!!test_bit(code, dev->key) != !!value) {
-
+//key成员是一个bitmap,用于记录每个扫描码上次事件上报的值和上次的值不一样,则反转bitmap中的对应bit数据
 				__change_bit(code, dev->key);
 
 				#if defined(CONFIG_SPRD_DEBUG)
@@ -384,7 +385,7 @@ static void input_handle_event(struct input_dev *dev,
 			       unsigned int type, unsigned int code, int value)
 {
 	int disposition;
-
+//16:继续……
 	disposition = input_get_disposition(dev, type, code, &value);
 
 	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
@@ -402,7 +403,8 @@ static void input_handle_event(struct input_dev *dev,
 			v->code = ABS_MT_SLOT;
 			v->value = dev->mt->slot;
 		}
-
+//19:将事件存储在input设备自身的数据缓冲队列中,到这一步,一次由驱动上报的事件(一次report)就处理结束了。
+//:剩下的就是evdev的事情了。
 		v = &dev->vals[dev->num_vals++];
 		v->type = type;
 		v->code = code;
@@ -446,6 +448,7 @@ void input_event(struct input_dev *dev,
 	if (is_event_supported(type, dev->evbit, EV_MAX)) {
 
 		spin_lock_irqsave(&dev->event_lock, flags);
+//15:继续深入......
 		input_handle_event(dev, type, code, value);
 		spin_unlock_irqrestore(&dev->event_lock, flags);
 	}
@@ -1755,7 +1758,7 @@ EXPORT_SYMBOL_GPL(input_class);
 struct input_dev *input_allocate_device(void)
 {
 	struct input_dev *dev;
-
+//2:为一个input设备对象分配内存(分配的内存清零)
 	dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
 	if (dev) {
 		dev->dev.type = &input_dev_type;
@@ -1877,6 +1880,7 @@ EXPORT_SYMBOL(input_free_device);
 void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
 {
 	switch (type) {
+//5:input设备的keybit成员也是一个bitmap,其中的每个bit代表一个特定的按键,某bit为1则表示该input设备支持该按键。
 	case EV_KEY:
 		__set_bit(code, dev->keybit);
 		break;
@@ -1923,7 +1927,7 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
 		dump_stack();
 		return;
 	}
-
+//5.1 同时,这个函数会顺便将事件类型也设置到evbit中。比如本次调用就会将EV_KEY设置到evbit中!!!
 	__set_bit(type, dev->evbit);
 }
 EXPORT_SYMBOL(input_set_capability);
@@ -1966,7 +1970,7 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
 
 	return events;
 }
-
+//11:清除的条件是evbit中没有涉及到的事件类型对应的bitmap
 #define INPUT_CLEANSE_BITMASK(dev, type, bits)				\
 	do {								\
 		if (!test_bit(EV_##type, dev->evbit))			\
@@ -1976,6 +1980,7 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
 
 static void input_cleanse_bitmasks(struct input_dev *dev)
 {
+//10:清除对应bitmap的内容
 	INPUT_CLEANSE_BITMASK(dev, KEY, key);
 	INPUT_CLEANSE_BITMASK(dev, REL, rel);
 	INPUT_CLEANSE_BITMASK(dev, ABS, abs);
@@ -2058,16 +2063,16 @@ int input_register_device(struct input_dev *dev)
 
 		devres->input = dev;
 	}
-
+//7:每个input设备都必须支持EV_SYN事件类型!所以在evbit中设置EV_SYN对应的bit
 	/* Every input device generates EV_SYN/SYN_REPORT events. */
 	__set_bit(EV_SYN, dev->evbit);
-
+//8:KEY_RESERVED按键已经不再被支持了!所以在keybit中清除KEY_RESERVED对应的bit
 	/* KEY_RESERVED is not supposed to be transmitted to userspace. */
 	__clear_bit(KEY_RESERVED, dev->keybit);
-
+//9:将evbit中未涉及到的事件类型的对应的bitmap全部清零
 	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
 	input_cleanse_bitmasks(dev);
-
+//计算该input设备每次事件上报的数据包大小,即每次事件上报中最多可能有几组值。每个数据包都以sync包结束:input_event(dev, EV_SYN, SYN_REPORT, 0);
 	packet_size = input_estimate_events_per_packet(dev);
 	if (dev->hint_events_per_packet < packet_size)
 		dev->hint_events_per_packet = packet_size;
@@ -2115,7 +2120,7 @@ int input_register_device(struct input_dev *dev)
 		goto err_device_del;
 
 	list_add_tail(&dev->node, &input_dev_list);
-
+//note:B 而这里是遍历handler链表中的每个handler,来和本input设备匹配。(请对比:note:A)
 	list_for_each_entry(handler, &input_handler_list, node)
 		input_attach_handler(dev, handler);
 
@@ -2175,6 +2180,7 @@ EXPORT_SYMBOL(input_unregister_device);
  * devices in the system and attaches it to all input devices that
  * are compatible with the handler.
  */
+//:evdev.c在初始化时会调用到这里。从而为每个input设备关联对应的event节点
 int input_register_handler(struct input_handler *handler)
 {
 	struct input_dev *dev;
@@ -2187,7 +2193,7 @@ int input_register_handler(struct input_handler *handler)
 	INIT_LIST_HEAD(&handler->h_list);
 
 	list_add_tail(&handler->node, &input_handler_list);
-
+//note:A:此处是遍历input设备链表中的每个input设备,来和本handler进行匹配(请对比:note:B)
 	list_for_each_entry(dev, &input_dev_list, node)
 		input_attach_handler(dev, handler);
 
diff --git a/drivers/input/keyboard/gpio_keys_sprd.c b/drivers/input/keyboard/gpio_keys_sprd.c
index e6c1651..72dbf10 100644
--- a/drivers/input/keyboard/gpio_keys_sprd.c
+++ b/drivers/input/keyboard/gpio_keys_sprd.c
@@ -351,6 +351,7 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
 		if (state)
 			input_event(input, type, button->code, button->value);
 	} else {
+//14:最终调用input子系统的以下函数进行事件上报
 		input_event(input, type, button->code, !!state);
 		PRINT_INFO("Key:%s ScanCode:%d value:%d\n", button->desc, button->code, !!state);
 	}
@@ -361,7 +362,7 @@ static void gpio_keys_gpio_work_func(struct work_struct *work)
 {
 	struct gpio_button_data *bdata =
 		container_of(work, struct gpio_button_data, work);
-
+//13:在底半部上报事件
 	gpio_keys_gpio_report_event(bdata);
 
 	if (bdata->button->wakeup)
@@ -387,6 +388,7 @@ static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
 		mod_timer(&bdata->timer,
 			jiffies + msecs_to_jiffies(bdata->timer_debounce));
 	else
+//12:在按键中断,调用底半部
 		schedule_work(&bdata->work);
 
 	return IRQ_HANDLED;
@@ -510,7 +512,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
 		isr = gpio_keys_irq_isr;
 		irqflags = 0;
 	}
-
+//4:设置该设备支持的按键信息,每个按键对应一个bit,如果设备支持该按键,则keybit的对应bit应该被置1
+//同时,这个函数会顺便将事件类型也设置到evbit中。比如本次调用就会将EV_KEY设置到evbit中!!!
 	input_set_capability(input, button->type ?: EV_KEY, button->code);
 
 	/*
@@ -733,6 +736,7 @@ static int gpio_keys_probe(struct platform_device *pdev)
 	ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
 			pdata->nbuttons * sizeof(struct gpio_button_data),
 			GFP_KERNEL);
+//1:申请分配一个input设备,返回申请到的input设备的指针
 	input = input_allocate_device();
 	if (!ddata || !input) {
 		dev_err(dev, "failed to allocate state\n");
@@ -745,6 +749,7 @@ static int gpio_keys_probe(struct platform_device *pdev)
 	mutex_init(&ddata->disable_lock);
 
 	platform_set_drvdata(pdev, ddata);
+//1.1:对该input对象的部分成员进行赋值
 	input_set_drvdata(input, ddata);
 #ifndef CONFIG_OF
 	input->name = pdata->name ? pdata->name : pdev->name;
@@ -763,6 +768,8 @@ static int gpio_keys_probe(struct platform_device *pdev)
 
 	/* Enable auto repeat feature of Linux input subsystem */
 	if (pdata->rep)
+//3:input设备的evbit成员是一个bitmap,其中的每个bit代表一个事件类型,某bit为1则表示该input设备支持该事件类型。
+//本驱动是一个物理按键驱动,理论上应该设置EV_KEY对应的bit,这里没有看到,相信后续流程中一定会有的!(是的,请深入//4看看!)
 		__set_bit(EV_REP, input->evbit);
 
 	for (i = 0; i < pdata->nbuttons; i++) {
@@ -783,7 +790,7 @@ static int gpio_keys_probe(struct platform_device *pdev)
 			error);
 		goto fail2;
 	}
-
+//6:注册input设备
 	error = input_register_device(input);
 	if (error) {
 		dev_err(dev, "Unable to register input device, error: %d\n",
diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index be3a228..48dce88 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -7,6 +7,7 @@
 #define BIT_MASK(nr)		(1UL << ((nr) % BITS_PER_LONG))
 #define BIT_WORD(nr)		((nr) / BITS_PER_LONG)
 #define BITS_PER_BYTE		8
+//note:
 #define BITS_TO_LONGS(nr)	DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
 #endif
 
diff --git a/include/linux/input.h b/include/linux/input.h
index 128e0a5..b9da50c 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -128,11 +128,13 @@ struct input_dev {
 	struct input_id id;
 
 	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
-
-	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
-	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
+//note:用一个长整型的数组表示一个bitmap,这是很巧妙的,将来的使用方式将是用数组名,也就是用指针方式去操作其中的每个bit,请参考__set_bit(EV_REP, input->evbit)的实现
+	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//bitmap,每一bit代表一个事件类型,对应的bit为1表示该input设备支持该事件类型
+//evbit不同于以下的这些bitmap,evbit中的每个bit代表一个事件类型,而以下的每个bitmap代表某个事件类型所对应的所有的扫描码scancode
+//如:keybit中的每个bit表示一个按键的扫描码,bit[0]对应扫描码为0的按键,bit[255]对应扫描码为255的按键,等等。
+	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//bitmap,每一bit代表一个按键,对应的bit为1表示该input设备支持该按键
 	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
-	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
+	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//bitmap,每一bit代表一个绝对轴。
 	unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
 	unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
 	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
@@ -161,7 +163,9 @@ struct input_dev {
 	struct input_mt *mt;
 
 	struct input_absinfo *absinfo;
-
+//note:the name of the array will be used as a pointer!
+//这几个bitmap用于记录设备扫描码的相关状态,如key中记录的是每个按键的键值(按下1,抬起0),input子系统会通过该bitmap记录每次事件中
+//上报的对应按键的键值,所以,该bitmap中的对应bit代表的是上次事件上报中对应按键的键值。
 	unsigned long key[BITS_TO_LONGS(KEY_CNT)];
 	unsigned long led[BITS_TO_LONGS(LED_CNT)];
 	unsigned long snd[BITS_TO_LONGS(SND_CNT)];
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index eabc56d..5a9b67a 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -51,6 +51,7 @@
 #define round_down(x, y) ((x) & ~__round_mask(x, y))
 
 #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
+//note:整除,向上取整。
 #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
 #define DIV_ROUND_UP_ULL(ll,d) \
 	({ unsigned long long _tmp = (ll)+(d)-1; do_div(_tmp, d); _tmp; })
diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h
index cd80183..d3452b0 100644
--- a/tools/virtio/linux/virtio.h
+++ b/tools/virtio/linux/virtio.h
@@ -24,6 +24,7 @@ static inline void clear_bit(int nr, volatile unsigned long *addr)
 
 static inline int test_bit(int nr, const volatile unsigned long *addr)
 {
+//note:
         return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
 }
 /* end of stubs */




                
好的,以下是一个使用Keras框架搭建卷积神经网络对猫狗图片进行分类的示例代码,并使用上述数据集目录: ```python import os import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense # 训练、验证、测试数据集的目录 train_dir = 'D:\\python work\\datasets\\train1\\train' validation_dir = 'D:\\python work\\datasets\\train1\\validation' test_dir = 'D:\\python work\\datasets\\train1\\test' # 猫训练图片所在目录 train_cats_dir = os.path.join(train_dir, 'cats') # 狗训练图片所在目录 train_dogs_dir = os.path.join(train_dir, 'dogs') # 猫验证图片所在目录 validation_cats_dir = os.path.join(validation_dir, 'cats') # 狗验证数据集所在目录 validation_dogs_dir = os.path.join(validation_dir, 'dogs') # 猫测试数据集所在目录 test_cats_dir = os.path.join(test_dir, 'cats') # 狗测试数据集所在目录 test_dogs_dir = os.path.join(test_dir, 'dogs') # 数据集的大小和批次大小 img_width, img_height = 150, 150 batch_size = 32 # 使用ImageDataGenerator来进行数据增强和预处理 train_datagen = ImageDataGenerator( rescale=1./255, rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True ) validation_datagen = ImageDataGenerator(rescale=1./255) test_datagen = ImageDataGenerator(rescale=1./255) # 生成训练、验证和测试数据集 train_generator = train_datagen.flow_from_directory( train_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary' ) validation_generator = validation_datagen.flow_from_directory( validation_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary' ) test_generator = test_datagen.flow_from_directory( test_dir, target_size=(img_width, img_height), batch_size=batch_size, class_mode='binary' ) # 构建卷积神经网络模型 model = Sequential([ Conv2D(32, (3, 3), activation='relu', input_shape=(img_width, img_height, 3)), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Conv2D(128, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Conv2D(128, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Flatten(), Dense(512, activation='relu'), Dense(1, activation='sigmoid') ]) # 编译模型 model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 训练模型 history = model.fit( train_generator, steps_per_epoch=train_generator.n // batch_size, epochs=50, validation_data=validation_generator, validation_steps=validation_generator.n // batch_size ) # 评估模型 test_loss, test_acc = model.evaluate(test_generator, steps=test_generator.n // batch_size) print('测试集上的准确率为:', test_acc) ``` 这个示例代码使用了Keras框架搭建了一个卷积神经网络模型,使用ImageDataGenerator来进行数据增强和预处理,使用训练、验证和测试数据集分别进行训练、验证和评估,并输出测试集上的准确率。其中,通过设置train_dir、validation_dir和test_dir来指定数据集的目录,通过train_cats_dir、train_dogs_dir、validation_cats_dir、validation_dogs_dir、test_cats_dir和test_dogs_dir来指定猫和狗图片所在的目录。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YasinLeeX

再来一杯西湖龙井。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值