gpio-keys 代码分析
时间: 2024-01-27 19:46:39 浏览: 126
gpio-keys 是 Linux 内核中的一个模块,用于将 GPIO 按键映射为键盘上的按键,以便用户可以使用 GPIO 按键来与系统进行交互。在本文中,我将对 gpio-keys 模块的代码进行分析。
首先,我们需要了解的是 gpio-keys 模块的注册和注销过程。在模块初始化期间,我们需要调用 `gpio_keys_probe()` 函数来注册模块,该函数会注册一个 platform 设备,并将其与 gpio_keys_driver 结构体相关联。这个结构体包含了模块的名称、ID、设备树匹配以及一些回调函数。注册完成后,内核就会调用 `gpio_keys_irq()` 函数来设置 GPIO 中断并处理按键事件。
下面是 `gpio_keys_probe()` 函数的代码:
```c
static int gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_button *button;
struct input_dev *input;
int error, i;
...
/* Allocate input device */
input = input_allocate_device();
if (!input) {
dev_err(&pdev->dev, "Failed to allocate input device\n");
error = -ENOMEM;
goto err_free_desc;
}
/* Set input device properties */
input->name = pdata->input_name ?: "gpio-keys";
input->dev.parent = &pdev->dev;
set_bit(EV_KEY, input->evbit);
for (i = 0, button = pdata->buttons; i < pdata->nbuttons;
i++, button++) {
input_set_capability(input, EV_KEY, button->code);
}
/* Register input device */
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "Failed to register input device\n");
goto err_free_dev;
}
/* Allocate and configure gpio_keys_device */
gkd = devm_kzalloc(&pdev->dev, sizeof(*gkd), GFP_KERNEL);
if (!gkd) {
error = -ENOMEM;
goto err_free_dev;
}
gkd->pdev = pdev;
gkd->input = input;
gkd->n_buttons = pdata->nbuttons;
gkd->buttons = pdata->buttons;
/* Request and configure GPIOs */
for (i = 0, button = pdata->buttons; i < pdata->nbuttons;
i++, button++) {
error = gpio_request(button->gpio, button->desc);
if (error) {
dev_err(&pdev->dev, "Failed to request gpio %d: %d\n",
button->gpio, error);
goto err_free_gpio;
}
error = gpio_direction_input(button->gpio);
if (error) {
dev_err(&pdev->dev, "Failed to configure gpio %d: %d\n",
button->gpio, error);
goto err_free_gpio;
}
}
/* Register IRQ handlers */
for (i = 0, button = pdata->buttons; i < pdata->nbuttons;
i++, button++) {
error = gpio_request(button->gpio, button->desc);
if (error) {
dev_err(&pdev->dev, "Failed to request gpio %d: %d\n",
button->gpio, error);
goto err_free_irq;
}
error = request_irq(gpio_to_irq(button->gpio), gpio_keys_irq,
button->irqflags, button->desc, gkd);
if (error) {
dev_err(&pdev->dev, "Failed to register IRQ for gpio %d: %d\n",
button->gpio, error);
goto err_free_irq;
}
}
/* Store private data */
platform_set_drvdata(pdev, gkd);
return 0;
err_free_irq:
while (--i >= 0) {
button--;
free_irq(gpio_to_irq(button->gpio), gkd);
}
goto err_free_gpio;
err_free_gpio:
while (--i >= 0) {
button--;
gpio_free(button->gpio);
}
err_free_dev:
input_free_device(input);
err_free_desc:
for (i = 0, button = pdata->buttons; i < pdata->nbuttons;
i++, button++) {
kfree(button->desc);
}
return error;
}
```
在 `gpio_keys_probe()` 函数中,我们首先为输入设备分配内存,然后设置输入设备的属性,如名称、上级设备、事件类型和按键能力。接下来,我们为每个按键分配 GPIO,并将其配置为输入模式。最后,我们为每个按键注册中断处理程序,并将私有数据存储在 platform 设备的私有数据区域中。
一旦模块已注册并初始化,内核就可以使用 `gpio_keys_irq()` 函数来处理按键事件。该函数会检查哪个按键被按下或释放,并将事件发送到输入子系统。
下面是 `gpio_keys_irq()` 函数的代码:
```c
static irqreturn_t gpio_keys_irq(int irq, void *dev_id)
{
struct gpio_keys_device *gkd = dev_id;
struct gpio_keys_button *button;
struct input_dev *input = gkd->input;
unsigned int state;
int i;
/* Check each button */
for (i = 0, button = gkd->buttons; i < gkd->n_buttons; i++, button++) {
state = gpio_get_value(button->gpio);
if (state != button->active_low)
continue;
/* Send event to input subsystem */
input_report_key(input, button->code, 1);
input_sync(input);
input_report_key(input, button->code, 0);
input_sync(input);
}
return IRQ_HANDLED;
}
```
在 `gpio_keys_irq()` 函数中,我们遍历每个按键并检查其状态。如果按键被按下,则我们发送 “按下” 事件;如果按键被释放,则我们发送 “释放” 事件。最后,我们将事件同步到输入子系统。
总的来说,gpio-keys 模块是一个非常有用的内核模块,它允许用户通过 GPIO 按键与系统进行交互。通过分析其代码,我们可以更好地了解内核模块是如何工作的,并且可以更好地理解 linux 内核的编程模式。
阅读全文