接前一篇文章《Linux内核中ideapad-laptop.c文件全解析5》,地址为:
Linux内核中ideapad-laptop.c文件全解析5_蓝天居士的博客-CSDN博客
上一篇详细分析了ideapad_debugfs_init,本篇详细分析ideapad_input_init。
ideapad_input_init在同文件(drivers/platform/x86/ideapad-laptop.c)中实现,代码如下:
- static int ideapad_input_init(struct ideapad_private *priv)
- {
- struct input_dev *inputdev;
- int err;
-
- inputdev = input_allocate_device();
- if (!inputdev)
- return -ENOMEM;
-
- inputdev->name = "Ideapad extra buttons";
- inputdev->phys = "ideapad/input0";
- inputdev->id.bustype = BUS_HOST;
- inputdev->dev.parent = &priv->platform_device->dev;
-
- err = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
- if (err) {
- dev_err(&priv->platform_device->dev,
- "Could not set up input device keymap: %d\n", err);
- goto err_free_dev;
- }
-
- err = input_register_device(inputdev);
- if (err) {
- dev_err(&priv->platform_device->dev,
- "Could not register input device: %d\n", err);
- goto err_free_dev;
- }
-
- priv->inputdev = inputdev;
-
- return 0;
-
- err_free_dev:
- input_free_device(inputdev);
-
- return err;
- }
-
- static void ideapad_input_exit(struct ideapad_private *priv)
- {
- input_unregister_device(priv->inputdev);
- priv->inputdev = NULL;
- }
input_allocate_device函数在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(*dev), GFP_KERNEL);
- if (dev) {
- dev->dev.type = &input_dev_type;
- dev->dev.class = &input_class;
- device_initialize(&dev->dev);
- mutex_init(&dev->mutex);
- spin_lock_init(&dev->event_lock);
- timer_setup(&dev->timer, NULL, 0);
- INIT_LIST_HEAD(&dev->h_list);
- INIT_LIST_HEAD(&dev->node);
-
- dev_set_name(&dev->dev, "input%lu",
- (unsigned long)atomic_inc_return(&input_no));
-
- __module_get(THIS_MODULE);
- }
-
- return dev;
- }
- EXPORT_SYMBOL(input_allocate_device);
由input_allocate_device函数,就进入了Linux输入子系统(Linux Input Subsystem)。
以下内容引自input 子系统架构总结_lbmygf的博客-CSDN博客
#############################################################################
Linux 的输入子系统不仅支持鼠标、键盘等常规输入设备,而且还支持蜂鸣器、触摸屏等设备。
输入子系统又叫input子系统。其构建非常灵活,只需要调用一些简单的函数,就可以将一个输入设备的功能呈现给应用程序。

#############################################################################
input_allocate_device函数位于核心层,其作用是动态分配一个input_dev结构体实例并初始化。该结构体是一个输入设备结构体,包含了输入设备的一些相关信息,如:设备支持的按键码、设备的名称、设备支持的事件等。函数返回一个指向函数中动态分配的input_dev结构实例的指针。
sparse_keymap_setup函数在drivers/input/sparse-keymap.c中,代码如下:
- /**
- * sparse_keymap_setup - set up sparse keymap for an input device
- * @dev: Input device
- * @keymap: Keymap in form of array of &key_entry structures ending
- * with %KE_END type entry
- * @setup: Function that can be used to adjust keymap entries
- * depending on device's needs, may be %NULL
- *
- * The function calculates size and allocates copy of the original
- * keymap after which sets up input device event bits appropriately.
- * The allocated copy of the keymap is automatically freed when it
- * is no longer needed.
- */
- int sparse_keymap_setup(struct input_dev *dev,
- const struct key_entry *keymap,
- int (*setup)(struct input_dev *, struct key_entry *))
- {
- size_t map_size = 1; /* to account for the last KE_END entry */
- const struct key_entry *e;
- struct key_entry *map, *entry;
- int i;
- int error;
-
- for (e = keymap; e->type != KE_END; e++)
- map_size++;
-
- map = devm_kmemdup(&dev->dev, keymap, map_size * sizeof(*map),
- GFP_KERNEL);
- if (!map)
- return -ENOMEM;
-
- for (i = 0; i < map_size; i++) {
- entry = &map[i];
-
- if (setup) {
- error = setup(dev, entry);
- if (error)
- return error;
- }
-
- switch (entry->type) {
- case KE_KEY:
- __set_bit(EV_KEY, dev->evbit);
- __set_bit(entry->keycode, dev->keybit);
- break;
-
- case KE_SW:
- case KE_VSW:
- __set_bit(EV_SW, dev->evbit);
- __set_bit(entry->sw.code, dev->swbit);
- break;
- }
- }
-
- if (test_bit(EV_KEY, dev->evbit)) {
- __set_bit(KEY_UNKNOWN, dev->keybit);
- __set_bit(EV_MSC, dev->evbit);
- __set_bit(MSC_SCAN, dev->mscbit);
- }
-
- dev->keycode = map;
- dev->keycodemax = map_size;
- dev->getkeycode = sparse_keymap_getkeycode;
- dev->setkeycode = sparse_keymap_setkeycode;
-
- return 0;
- }
- EXPORT_SYMBOL(sparse_keymap_setup);
如函数说明,sparse_keymap_setup函数建立了输入设备的稀疏键映射。
ideapad_input_init函数中调用sparse_keymap_setup时,第2个参数const struct key_entry *keymap传入的实参是ideapad_keymap。ideapad_keymap数组在同文件(drivers/platform/x86/ideapad-laptop.c)中初始化,如下代码:
- static const struct key_entry ideapad_keymap[] = {
- { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
- { KE_KEY, 7, { KEY_CAMERA } },
- { KE_KEY, 8, { KEY_MICMUTE } },
- { KE_KEY, 11, { KEY_F16 } },
- { KE_KEY, 13, { KEY_WLAN } },
- { KE_KEY, 16, { KEY_PROG1 } },
- { KE_KEY, 17, { KEY_PROG2 } },
- { KE_KEY, 64, { KEY_PROG3 } },
- { KE_KEY, 65, { KEY_PROG4 } },
- { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
- { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
- { KE_KEY, 128, { KEY_ESC } },
- { KE_END },
- };
input_register_device函数是input设备注册的接口,在drivers/input/input.c中,代码如下:
- /**
- * input_register_device - register device with input core
- * @dev: device to be registered
- *
- * This function registers device with input core. The device must be
- * allocated with input_allocate_device() and all it's capabilities
- * set up before registering.
- * If function fails the device must be freed with input_free_device().
- * Once device has been successfully registered it can be unregistered
- * with input_unregister_device(); input_free_device() should not be
- * called in this case.
- *
- * Note that this function is also used to register managed input devices
- * (ones allocated with devm_input_allocate_device()). Such managed input
- * devices need not be explicitly unregistered or freed, their tear down
- * is controlled by the devres infrastructure. It is also worth noting
- * that tear down of managed input devices is internally a 2-step process:
- * registered managed input device is first unregistered, but stays in
- * memory and can still handle input_event() calls (although events will
- * not be delivered anywhere). The freeing of managed input device will
- * happen later, when devres stack is unwound to the point where device
- * allocation was made.
- */
- 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);
输入子系统部分的代码要完全分析清楚,需要一个系列才可以。由于这并不是我们的重点,所以只是点到为止,把涉及到的接口及代码列在这里,并不深入展开了。
至此,ideapad_input_init函数也就算是分析完了。