linux input 驱动

发布于:2024-05-22 ⋅ 阅读:(186) ⋅ 点赞:(0)

使用文档

设备树修改

新增一个 LED 节点 arch/arm/boot/dts/arm/vexpress-v2p-ca9.dts

	my_pl_led {
		compatible = "arm, cortex-a9-led";
		status = "okay";
	};

设备树编译

make dtbs

日志

  DTC     arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb

编译驱动

../my_module_build.sh 0016_input

查看模块 ko 文件

ls /lib/modules/6.5.7+/
my_app my_platform_driver.ko

查看输入设备

ls /dev/input/
event0  event1

platform 驱动模块安装

modprobe my_platform_driver

日志

platform driver probe
input: keyinput as /devices/virtual/input/input3
k: platform driver module init!
drm-clcd-pl111 10020000.clcd: DVI muxed to daughterboard 1 (core tile) CLCD
drm-clcd-pl111 10020000.clcd: initializing Versatile Express PL111

列出所有输入设备

cat /proc/bus/input/devices
I: Bus=0011 Vendor=0001 Product=0002 Version=ab83
N: Name="AT Raw Set 2 keyboard"
P: Phys=10006000.kmi/input0
S: Sysfs=/devices/platform/bus@40000000/bus@40000000:motherboard-bus@40000000/bus@40000000:motherboard-bus@40000000:iofpga@7,00000000/10006000.kmi/serio0/input/input0
U: Uniq=
H: Handlers=sysrq kbd leds event0 
B: PROP=0
B: EV=120013
B: KEY=4 2000000 3803078 f800d001 feffffdf ffefffff ffffffff fffffffe
B: MSC=10
B: LED=7

I: Bus=0011 Vendor=0002 Product=0006 Version=0000
N: Name="ImExPS/2 Generic Explorer Mouse"
P: Phys=10007000.kmi/input0
S: Sysfs=/devices/platform/bus@40000000/bus@40000000:motherboard-bus@40000000/bus@40000000:motherboard-bus@40000000:iofpga@7,00000000/10007000.kmi/serio1/input/input2
U: Uniq=
H: Handlers=event1 
B: PROP=1
B: EV=7
B: KEY=1f0000 0 0 0 0 0 0 0 0
B: REL=143

I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="keyinput"
P: Phys=
S: Sysfs=/devices/virtual/input/input3
U: Uniq=
H: Handlers=kbd event2 
B: PROP=0
B: EV=100003
B: KEY=800

再次查看输入设备

ls /dev/input/
event0  event1 event2

所以这里新注册的输入设备为 event2

运行 APP 程序

/lib/modules/6.5.7+/my_app /dev/input/event2

运行日志如下

k: key down
k: key up
u: key 11 press
u: key 11 release

k: key down
k: key up
u: key 11 press
u: key 11 release

k: key down
k: key up
u: key 11 press
u: key 11 release

退出

Ctrl + a
x

驱动源码

#include "linux/export.h"
#include "linux/ioport.h"
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/input.h>

#define PLATFORM_DEVICE_NAME "my_pl_led" /* 匹配设备树节点的名字 */
#define KEYINPUT_NAME "keyinput"         /* 名字,cat /proc/bus/input/devices 显示的名字 */

/* keyinput设备结构体 */
struct keyinput_dev {
    unsigned int timer_cnt;
    struct timer_list timer;    /* 定义一个定时器*/
    struct input_dev *inputdev; /* input结构体 */
};

static struct keyinput_dev keyinputdev; /* key input设备 */

static void kernel_timer_expires(struct timer_list *t);

static void kernel_timer_expires(struct timer_list *t)
{
    if (t == &(keyinputdev.timer)) {
        keyinputdev.timer_cnt++;
        mod_timer(&(keyinputdev.timer), jiffies + 1 * HZ);

        if (keyinputdev.timer_cnt % 10 == 0) {
                printk("k: key down\r\n"); /* 按下按键 */
                input_report_key(keyinputdev.inputdev, KEY_0, 1);
                input_sync(keyinputdev.inputdev);

                printk("k: key up\r\n"); /* 抬起按键 */
                input_report_key(keyinputdev.inputdev, KEY_0, 0);
                input_sync(keyinputdev.inputdev);
        }
    }
}

static int my_probe(struct platform_device *device)
{
    int ret = 0;

    printk("platform driver probe\r\n");

    /* 申请input_dev */
    keyinputdev.inputdev = input_allocate_device();
    keyinputdev.inputdev->name = KEYINPUT_NAME;

    // unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 事件类型的位图 */
    // unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; /* 按键值的位图 */
    // unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; /* 相对坐标的位图 */
    // unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; /* 绝对坐标的位图 */
    // unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; /* 杂项事件的位图 */
    // unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; /*LED 相关的位图 */
    // unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];/* sound 有关的位图 */
    // unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 压力反馈的位图 */
    // unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /*开关状态的位图 */
    keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);

    // #define EV_SYN 0x00 /* 同步事件 */
    // #define EV_KEY 0x01 /* 按键事件 */
    // #define EV_REL 0x02 /* 相对坐标事件 */
    // #define EV_ABS 0x03 /* 绝对坐标事件 */
    // #define EV_MSC 0x04 /* 杂项(其他)事件 */
    // #define EV_SW 0x05 /* 开关事件 */
    // #define EV_LED 0x11 /* LED */
    // #define EV_SND 0x12 /* sound(声音) */
    // #define EV_REP 0x14 /* 重复事件 */
    // #define EV_FF 0x15 /* 压力事件 */
    // #define EV_PWR 0x16 /* 电源事件 */
    // #define EV_FF_STATUS 0x17 /* 压力状态事件 */
    input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);

    /* 注册输入设备 */
    ret = input_register_device(keyinputdev.inputdev);
    if (ret) {
        printk("register input device failed!\r\n");
        return ret;
    }

    //初始化timer
    timer_setup(&(keyinputdev.timer), kernel_timer_expires, 0);

    //启动timer
    mod_timer(&(keyinputdev.timer), jiffies + 1 * HZ); // 1s timeout

    return 0;
}

static int my_remove(struct platform_device *device)
{
    printk("platform driver remove\r\n");

    /* 释放input_dev */
    input_unregister_device(keyinputdev.inputdev);
    input_free_device(keyinputdev.inputdev);

    return 0;
}

/**
 * @brief include mod_devicetable.h. 传统匹配方式ID列表
 * 
 */
static const struct platform_device_id test_id_table = {
    .name = "low pri name", /* 用于非设备树匹配的时候,如果有设备树,可以匹配 compatible 属性的名字。 */
};

/* 匹配列表 */
static const struct of_device_id led_of_match[] = {
    { .compatible =
              "arm, cortex-a9-led" }, /* 和设备树节点 compatible 匹配的名字 */
    { /* Sentinel */ },
};

static struct platform_driver platform_driver_test = {
    .probe = my_probe,
    .remove = my_remove,
    .driver = {
        .of_match_table = led_of_match, /* 设备树匹配机制,匹配设备树中的 compatible 属性,优先级高 */
        .name = "my_pl_driver", /* platform 驱动名(ls /sys/bus/platform/drivers/my_pl_driver/) */
        .owner = THIS_MODULE,
    },
    .id_table   = &test_id_table, /* 旧的匹配机制,优先级低 */
};

static int __init platform_driver_init(void)
{
    platform_driver_register(&platform_driver_test);

    printk("k: platform driver module init!\r\n");

    return 0;
}

static void __exit platform_driver_exit(void)
{
    platform_driver_unregister(&platform_driver_test);

    printk("k: platform driver module exit!\r\n");
}

module_init(platform_driver_init);
module_exit(platform_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");
MODULE_INFO(intree, "Y"); /* loading out-of-tree module taints kernel */

应用源码

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <linux/input.h>

static struct input_event inputevent;

int main(int argc, char *argv[])
{
    int fd;
    int err = 0;
    char *filename;

    filename = argv[1];

    if (argc != 2) {
        printf("Error Usage!\r\n");
        return -1;
    }

    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

    while (1) {
        err = read(fd, &inputevent, sizeof(inputevent));
        if (err > 0) { /* 读取数据成功 */
            switch (inputevent.type) {
            case EV_KEY:
                if (inputevent.code < BTN_MISC) { /* 键盘键值 */
                    printf("u: key %d %s\r\n", inputevent.code, inputevent.value ? "press" : "release");
                } else {
                    printf("u: button %d %s\r\n", inputevent.code,
                           inputevent.value ? "press" : "release");
                }
                break;

            /* 其他类型的事件,自行处理 */
            case EV_REL:
                break;
            case EV_ABS:
                break;
            case EV_MSC:
                break;
            case EV_SW:
                break;
            }
        } else {
            printf("读取数据失败\r\n");
        }
    }
    return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到