• 正文
    • 修改设备树
    • 驱动源码my_keyboard.c编写
    • 完整的驱动my_keyboard.c示例源码
    • 编译
    • 测试
  • 相关推荐
申请入驻 产业图谱

飞凌嵌入式ElfBoard ELF 1板卡-input子系统之基于input子系统的按键驱动

04/15 11:00
464
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

例程代码路径:ELF 1开发板资料包3-例程源码3-2 驱动例程源码8_input子系统keyboard

下面以控制开发板上的K2为例进行讲解。

修改设备树

(一)查看原理图引脚复用表格,可以得到K2由GPIO5_2控制,所以我们需要配置GPIO5_2引脚为输入,而且能够在用户空间能够获取按键事件。

(二)在设备树arch/arm/boot/dts/imx6ull-elf1-emmc.dts中添加keyboard节点和引脚复用,并检查设备树中是否有其它的地方也用到了此引脚,如果用到了就需要将其屏蔽掉,避免复用冲突。

添加节点:

keyboard {

compatible = "keyboard";

pinctrl-names = "default";

pinctrl-0 = <&pinctrl_keyboard>;

gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;

};

添加后效果如下:

添加复用,因为GPIO5_IO2为SNVS引脚,所以需要在iomux_snvs中添加:

pinctrl_keyboard: keyboardgrp {

fsl,pins = <

MX6ULL_PAD_SNVS_TAMPER2__GPIO5_IO02     0x17059

>;

};

添加后效果如下:

(三)编译设备树

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/linux-imx-imx_4.1.15_2.0.0_ga$ make dtbs

编译生成的设备树文件为imx6ull-elf1-emmc.dtb,参考《01-0 ELF1、ELF1S开发板_快速启动手册_V1》4.4节单独更新设备树。

驱动源码my_keyboard.c编写

(一)头文件引用

include <linux/init.h>

#include <linux/module.h>       // 包含模块相关函数的头文件

#include <linux/fs.h>           // 包含文件系统相关函数的头文件

#include <linux/platform_device.h>

#include <linux/miscdevice.h>

#include <linux/device.h>       //包含设备节点相关的头文件

#include <linux/gpio.h>         //包含gpio操作函数的相关头文件

#include <linux/of_gpio.h>

#include <linux/of.h>

#include <linux/input.h>

#include <linux/interrupt.h>

(二)创建相关宏定义

struct device_node *node;   //设备树节点

int gpio;   //gpio编号

struct keyboard {

struct input_dev *input_dev;

int irq;

};

struct input_dev *input_dev;

(三)定义platform_driver类型结构体

static struct platform_driver my_platform_driver = {

.driver = {

.name = "my_keyboard_driver",

.owner = THIS_MODULE,

.of_match_table = of_platform_match,

},

.probe = keyboard_probe,

.remove = my_platform_remove,

};

(四)定义of_platform_match,用来与设备树中的compatible匹配,匹配成功后才会进入到probe函数中

static const struct of_device_id of_platform_match[] = {

{ .compatible = "keyboard", },

{},

};

(五)probe函数的实现

static int keyboard_probe(struct platform_device *pdev)

{

struct keyboard *keyboard;

int ret;

node = of_find_node_by_name(NULL,"keyboard");

gpio = of_get_named_gpio(node, "gpios", 0);

//为keyboard结构体申请内存

keyboard = devm_kzalloc(&pdev->dev,sizeof(*keyboard),GFP_KERNEL);

if(!keyboard)

return -ENOMEM;

// 分配input设备结构

input_dev = input_allocate_device();

if(!input_dev)

return -ENOMEM;

keyboard->input_dev = input_dev;

gpio_free(gpio);

//申请GPIO编号

if (gpio_request(gpio, "keyboard")) {

printk("request %s gpio%d faile n", "keyboard",gpio);

return -1;

}

//设置为输入

gpio_direction_input(gpio);

keyboard->irq = gpio_to_irq(gpio);

// 设置支持按键事件

set_bit(EV_KEY, input_dev->evbit);

// 设置支持按键类型为KEY_ENTER

set_bit(KEY_ENTER, input_dev->keybit);

// 注册输入设备

ret = input_register_device(input_dev);

if(ret)

return ret;

//申请中断

ret = devm_request_irq(&pdev->dev, keyboard->irq, keyboard_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "keyboard_irq", keyboard);

if (ret) {

input_unregister_device(input_dev);

return ret;

}

// 将输入设备与平台设备关联

platform_set_drvdata(pdev, input_dev);

printk(KERN_INFO "my_platform_probe: Platform device probedn");

return 0;

}

可以看到probe函数中进行了一些列的初始化操作:

(1)获取设备树中的gpio引脚编号;

(2)分配input设备结构;

(3)申请GPIO编号并配置为输入模式;

(4)设置按键事件和类型;

(5)注册输入设备;

(6)申请中断;

(7)将输入设备与平台设备关联。

(六)中断服务函数实现

static irqreturn_t keyboard_irq_handler(int irq, void *dev_id)

{

struct keyboard *keyboard = dev_id;

int value = gpio_get_value(gpio);

input_report_key(keyboard->input_dev, KEY_ENTER, !value);

input_sync(keyboard->input_dev);

return IRQ_HANDLED;

}

中断服务函数中读取了引脚状态,然后通过input_report_key()和input_sync()函数将事件上报给用户空间。

(1)input_report_key()原型:

void input_report_key(struct input_dev *dev, unsigned int code, int value);

参数说明:

dev:指向 struct input_dev 的指针,表示目标输入设备。

code:无符号整数,表示按键码(键值)。

value:整数,表示按键状态。0表示按键释放,1表示按键按下,2表示按键重复。

input_report_key() 函数将按键事件报告给输入设备 dev,并指定按键码和按键状态。该函数会更新 input_dev 结构体中相应按键的状态信息,并触发输入子系统将事件传递到用户空间。

(2)input_sync()原型:

void input_sync(struct input_dev *dev);

参数说明:

dev:指向 struct input_dev的指针,表示目标输入设备。

input_sync()函数用于通知输入子系统当前输入设备的事件已经全部报告完毕,需要立即将事件传递到用户空间。在调用 input_report_*() 系列函数报告完输入事件后,应当调用input_sync()函数以确保事件被及时处理。

完整的驱动my_keyboard.c示例源码

#include <linux/init.h>

#include <linux/module.h>       // 包含模块相关函数的头文件

#include <linux/fs.h>           // 包含文件系统相关函数的头文件

#include <linux/platform_device.h>

#include <linux/miscdevice.h>

#include <linux/device.h>       //包含设备节点相关的头文件

#include <linux/gpio.h>         //包含gpio操作函数的相关头文件

#include <linux/of_gpio.h>

#include <linux/of.h>

#include <linux/input.h>

#include <linux/interrupt.h>

struct device_node *node;   //设备树节点

int gpio;   //gpio编号

struct keyboard {

struct input_dev *input_dev;

int irq;

};

struct input_dev *input_dev;

static irqreturn_t keyboard_irq_handler(int irq, void *dev_id)

{

struct keyboard *keyboard = dev_id;

int value = gpio_get_value(gpio);

printk("------------------n");

input_report_key(keyboard->input_dev, KEY_ENTER, !value);

input_sync(keyboard->input_dev);

return IRQ_HANDLED;

}

static int keyboard_probe(struct platform_device *pdev)

{

struct keyboard *keyboard;

int ret;

node = of_find_node_by_name(NULL,"keyboard");

&nbspgpio = of_get_named_gpio(node, "gpios", 0);

&nbspkeyboard = devm_kzalloc(&pdev->dev,sizeof(*keyboard),GFP_KERNEL);

&nbspif(!keyboard)

&nbsp&nbspreturn -ENOMEM;

&nbspinput_dev = input_allocate_device();

&nbspif(!input_dev)

&nbsp&nbspreturn -ENOMEM;

&nbspkeyboard->input_dev = input_dev;

&nbspgpio_free(gpio);

&nbspprintk("-------------gpio=%d---------n",gpio);

&nbspif (gpio_request(gpio, "keyboard")) {

printk("request %s gpio%d faile n", "keyboard",gpio);

return -1;

}

&nbsp//设置为输入

&nbspgpio_direction_input(gpio);

&nbspkeyboard->irq = gpio_to_irq(gpio);

&nbsp// 设置支持按键事件

&nbspset_bit(EV_KEY, input_dev->evbit);

&nbsp// 设置支持按键类型为KEY_ENTER

&nbspset_bit(KEY_ENTER, input_dev->keybit);

&nbsp

&nbsp// 注册输入设备

&nbspret = input_register_device(input_dev);

&nbspif(ret)

&nbsp&nbspreturn ret;

&nbspret = devm_request_irq(&pdev->dev, keyboard->irq, keyboard_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "keyboard_irq", keyboard);

&nbspif (ret) {

&nbsp&nbspinput_unregister_device(input_dev);

&nbsp&nbspreturn ret;

&nbsp}

&nbsp// 将输入设备与平台设备关联

&nbspplatform_set_drvdata(pdev, input_dev);

printk(KERN_INFO "my_platform_probe: Platform device probedn");

return 0;

}

static int my_platform_remove(struct platform_device *pdev)

{

&nbspinput_unregister_device(input_dev);

&nbspinput_free_device(input_dev);

printk(KERN_INFO "my_platform_remove: Platform device removedn");

return 0;

}

static const struct of_device_id of_platform_match[] = {

{ .compatible = "keyboard", },

{},

};

static struct platform_driver my_platform_driver = {

.driver = {

.name = "my_keyboard_driver",

.owner = THIS_MODULE,

.of_match_table = of_platform_match,

},

.probe = keyboard_probe,

.remove = my_platform_remove,

};

module_platform_driver(my_platform_driver);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Your Name");

MODULE_DESCRIPTION("Platform Driver Example");

编译

复制7.8.3驱动中的Makefile文件,将其中的elf-aht20.o修改为my_keyboard.o,效果如下:

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/08_input子系统/keyboard$ make

将编译生成的my_keyboard.ko模块拷贝到开发板。

测试

root@ELF1:~# insmod my_keyboard.ko

-------------gpio=130---------

input: Unspecified device as /devices/virtual/input/input4

my_platform_probe: Platform device probed

加载驱动后,打印信息中可以看到,已经进入到了probe函数,输出如下命令查看对应的event事件:

root@ELF1:~# cat /proc/bus/input/devices

I: Bus=0019 Vendor=0000 Product=0000 Version=0000

N: Name="20cc000.snvs:snvs-powerkey"

P: Phys=snvs-pwrkey/input0

S: Sysfs=/devices/platform/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-powerkey/input/input0

U: Uniq=

H: Handlers=kbd event0

B: PROP=0

B: EV=3

B: KEY=100000 0 0 0

I: Bus=0019 Vendor=0000 Product=0000 Version=0000

N: Name="iMX6UL TouchScreen Controller"

P: Phys=

S: Sysfs=/devices/platform/soc/2000000.aips-bus/2040000.tsc/input/input1

U: Uniq=

H: Handlers=mouse0 event1

B: PROP=0

B: EV=b

B: KEY=400 0 0 0 0 0 0 0 0 0 0

B: ABS=3

I: Bus=0000 Vendor=0000 Product=0000 Version=0000

N: Name=""

P: Phys=

S: Sysfs=/devices/virtual/input/input2

U: Uniq=

H: Handlers=kbd event2

B: PROP=0

B: EV=3

B: KEY=10000000

可以看到生成的是event2的节点。接下来使用evtest检查这个节点,按下抬起K2按键,会上报对应的键值和事件:

root@ELF1:~# evtest /dev/input/event2

Input driver version is 1.0.1

Input device ID: bus 0x0 vendor 0x0 product 0x0 version 0x0

Input device name: "Unknown"

Supported events:

Event type 0 (EV_SYN)

Event type 1 (EV_KEY)

Event code 28 (KEY_ENTER)

Properties:

Testing ... (interrupt to exit)

------------------

Event: time 1691609102.952275, type 1 (EV_KEY), code 28 (KEY_ENTER), value 1

Event: time 1691609102.952275, -------------- SYN_REPORT ------------

------------------

------------------

Event: time 1691609103.106850, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0

Event: time 1691609103.106850, -------------- SYN_REPORT ------------

------------------

Event: time 1691609103.682438, type 1 (EV_KEY), code 28 (KEY_ENTER), value 1

Event: time 1691609103.682438, -------------- SYN_REPORT ------------

------------------

Event: time 1691609103.831401, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0

Event: time 1691609103.831401, -------------- SYN_REPORT ------------

------------------

Event: time 1691609104.223788, type 1 (EV_KEY), code 28 (KEY_ENTER), value 1

Event: time 1691609104.223788, -------------- SYN_REPORT ------------

------------------

Event: time 1691609104.430191, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0

Event: time 1691609104.430191, -------------- SYN_REPORT ------------

root@ELF1:~# rmmod my_keyboard.ko

my_platform_remove: Platform device removed

点赞
收藏
评论
分享
加入交流群
举报

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录