十六、(正点原子)Linux自带按键驱动程序

一、Linux自带按键驱动程序源码简析

        Linux 内核也自带了 KEY 驱动,如果要使用内核自带的 KEY 驱动的话需要配置 Linux 内核,不过 Linux 内核一般默认已经使能了 KEY 驱动,但是我们还是要检查一下。按照如下路径找到相应的配置选项:

-> Device Drivers
    -> Input device support
        -> Generic input layer (needed for keyboard, mouse, ...) (INPUT [=y])
            -> Keyboards (INPUT_KEYBOARD [=y])
                ->GPIO Buttons

        选中以后就会在.config 文件中出现“CONFIG_KEYBOARD_GPIO=y”这一行, Linux 内核就会根据这一行来将 KEY 驱动文件编译进 Linux 内核。
         Linux 内核自带的 KEY 驱动文件为drivers/input/keyboard/gpio_keys.cgpio_keys.c 采用了 platform 驱动框架,在 KEY 驱动上使用了 input 子系统实现。

/* 部分代码 */

static const struct of_device_id gpio_keys_of_match[] = {
	{ .compatible = "gpio-keys", },
	{ },
};

static struct platform_driver gpio_keys_device_driver = {
	.probe		= gpio_keys_probe,
	.remove		= gpio_keys_remove,
	.driver		= {
		.name	= "gpio-keys",
		.pm	= &gpio_keys_pm_ops,
		.of_match_table = of_match_ptr(gpio_keys_of_match),
	}
};

static int __init gpio_keys_init(void)
{
	return platform_driver_register(&gpio_keys_device_driver);
}

static void __exit gpio_keys_exit(void)
{
	platform_driver_unregister(&gpio_keys_device_driver);
}

late_initcall(gpio_keys_init);
module_exit(gpio_keys_exit);

        可以看出,这就是一个标准的 platform 驱动框架,如果要使用设备树来描述 KEY 设备信息的话,设备节点的 compatible 属性值要设置为“gpio-keys”。当设备和驱动匹配以后gpio_keys_probe 函数就会执行, gpio_keys_probe 函数内容如下:

static int gpio_keys_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
	struct gpio_keys_drvdata *ddata;
	struct input_dev *input;
	size_t size;
	int i, error;
	int wakeup = 0;

	if (!pdata) {
		pdata = gpio_keys_get_devtree_pdata(dev);
		if (IS_ERR(pdata))
			return PTR_ERR(pdata);
	}
    。。。

	input = devm_input_allocate_device(dev);
	if (!input) {
		dev_err(dev, "failed to allocate input device\n");
		return -ENOMEM;
	}

	ddata->pdata = pdata;
	ddata->input = input;
	mutex_init(&ddata->disable_lock);

	platform_set_drvdata(pdev, ddata);
	input_set_drvdata(input, ddata);

	input->name = pdata->name ? : pdev->name;
	input->phys = "gpio-keys/input0";
	input->dev.parent = &pdev->dev;
	input->open = gpio_keys_open;
	input->close = gpio_keys_close;

	input->id.bustype = BUS_HOST;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;

	/* Enable auto repeat feature of Linux input subsystem */
	if (pdata->rep)
		__set_bit(EV_REP, input->evbit);

	for (i = 0; i < pdata->nbuttons; i++) {
		const struct gpio_keys_button *button = &pdata->buttons[i];
		struct gpio_button_data *bdata = &ddata->data[i];

		error = gpio_keys_setup_key(pdev, input, bdata, button);
		if (error)
			return error;

		if (button->wakeup)
			wakeup = 1;
	}

	error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
	if (error) {
		dev_err(dev, "Unable to export keys/switches, error: %d\n",
			error);
		return error;
	}

	error = input_register_device(input);
	if (error) {
		dev_err(dev, "Unable to register input device, error: %d\n",
			error);
		goto err_remove_group;
	}
    。。。
}

        首先调用 gpio_keys_get_devtree_pdata 函数从设备树中获取到 KEY 相关的设备节点信息,使用 devm_input_allocate_device 函数申请 input_dev,然后初始化 input_dev,设置 input_dev事件,这里设置了 EV_REP 事件,调用 gpio_keys_setup_key 函数继续设置 KEY,此函数会设置input_devEV_KEY 事件code事件码(也就是 KEY 模拟为哪个按键),然后调用input_register_device 函数向 Linux 系统注册 input_dev

        gpio_keys_setup_key 函数调用 input_set_capability 函数设置 EV_KEY 事件以及 KEY 的按键类型,也就是 KEY 作为哪个按键?我们会在设备树里面设置指定的 KEY 作为哪个按键内容如下:

static int gpio_keys_setup_key(struct platform_device *pdev,
				struct input_dev *input,
				struct gpio_button_data *bdata,
				const struct gpio_keys_button *button)
{
    。。。

	input_set_capability(input, button->type ?: EV_KEY, button->code);

    。。。
	return 0;
}

        一切都准备就绪以后剩下的就是等待按键按下,然后向 Linux 内核上报事件,事件上报是在 gpio_keys_irq_isr 函数中完成的,此函数内容如下:

static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
{
	struct gpio_button_data *bdata = dev_id;
	const struct gpio_keys_button *button = bdata->button;
	struct input_dev *input = bdata->input;
	unsigned long flags;

	BUG_ON(irq != bdata->irq);

	spin_lock_irqsave(&bdata->lock, flags);

	if (!bdata->key_pressed) {
		if (bdata->button->wakeup)
			pm_wakeup_event(bdata->input->dev.parent, 0);

        /* 上报事件 */
		input_event(input, EV_KEY, button->code, 1);
		input_sync(input);    //同步

		if (!bdata->release_delay) {
			input_event(input, EV_KEY, button->code, 0);
			input_sync(input);
			goto out;
		}

		bdata->key_pressed = true;
	}

	if (bdata->release_delay)
		mod_timer(&bdata->release_timer,
			jiffies + msecs_to_jiffies(bdata->release_delay));
out:
	spin_unlock_irqrestore(&bdata->lock, flags);
	return IRQ_HANDLED;
}

二、Linux自带按键驱动程序使用 

        要 使 用 Linux 内 核 自 带 的 按 键 驱 动 程 序 很 简 单 , 只 需 要 根 据
Documentation/devicetree/bindings/input/gpio-keys.txt 这个文件在设备树中添加指定的设备节点即可

节点要求如下:

        ①、节点名字为“gpio-keys”。

        ②、gpio-keys 节点的 compatible 属性值一定要设置为“gpio-keys”。

        ③、所有的 KEY 都是 gpio-keys 的子节点,每个子节点可以用如下属性描述自己:
                gpios: KEY 所连接的 GPIO 信息。

                interrupts: KEY 所使用 GPIO 中断信息,不是必须的,可以不写。

                label: KEY 名字

                linux,code: KEY 要模拟的按键。

        ④、如果按键要支持连按的话要加入 autorepeat

打开 imx6ull-alientek-emmc.dts,根据上面的要求创建对应的设备节点,设备节点内容如下所示:

        注: 设置 KEY 所使用的 IO 为 GPIO1_IO18,一定要检查一下设备树看看此 GPIO 有没有被用到其他外设上,如果有的话要删除掉相关代码!防止出现GPIO复用冲突。

 

        重新编译设备树,然后用新编译出来的 imx6ull-alientek-emmc.dtb 启动 Linux 系统,系统启动以后查看/dev/input 目录

        event1这个文件就是 KEY 对应的设备文件,使用hexdump 命令来输出/dev/input/event1 文件原始数据信息,输入如下命令:

hexdump  /dev/input/event1

按下按键,终端如下图: 

         到这里我们使用Linux自带的按键驱动就已经成功了。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Tofu_Cabbage

你的打赏是我的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值