使用文档
设备树修改
新增一个 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;
}