接前一篇文章《Linux内核中ideapad-laptop.c文件全解析5》,地址为:
Linux内核中ideapad-laptop.c文件全解析5_蓝天居士的博客-CSDN博客
上一篇详细分析了ideapad_debugfs_init,本篇详细分析ideapad_input_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函数也就算是分析完了。