参考:【Linux驱动编程】通过一个虚拟input设备熟悉input设备驱动开发步骤
参考Github:Prry/linux-drivers
修改后代码:
本部分介绍参考Github中的代码使用。该代码虚拟了一个键盘,通过向某文件写入内容,可以模拟按键按下与松开。
进入linux-drivers/input
目录,运行如下命令
su # 输入密码,此步必须完成,否则后续写文件时会出现即使使用sudo也无法
make
insmod devinput.ko
此时,执行以下命令,便可模拟按下并松开一次0(一次不行多试几次)
echo 1 > /sys/bus/platform/devices/dev_input/input_bin && echo 0 > /sys/bus/platform/devices/dev_input/input_bin
或单独执行以下命令模拟按下(可以看到一直有0打出来)
echo 1 > /sys/bus/platform/devices/dev_input/input_bin
虽然很感谢Prry老师的开源,但是其代码有太多冗余,并不是本demo的最简实现方式,而且命名混乱,没有注释,可读性差,结构也不合理,容易导致初学者误解,所以我将代码进行了整理,把实现最简化,辅以注释,代码在Piamen/virtual-keyboard-linux-driver开源,以便各位读者理解。使用方法几乎不变,不过需要将/sys/bus/platform/devices/dev_input/input_bin
替换为/sys/kernel/virtual_keyboard/io_bin
。
对于一个驱动程序,我觉得应该关注的是其初始化函数和各类回调函数。
哪个函数是初始化函数呢?根据以下代码,可以看出初始化函数为virtual_keyboard_drv_init
module_init(virtual_keyboard_drv_init);
module_exit(virtual_keyboard_drv_exit);
以上两句代码表明,virtual_keyboard_drv_init
将会在执行insmod
的时候被调用。以下是其内容,可以看到注册了一个输入设备,创建了一个sysfs的文件。和我们的功能需求一一对应。具体每行函数的功能,不是本篇关注内容,也建议读者不去细读,读者应关注整个程序的结构和运行的方式。
// called on insmod
static int __init virtual_keyboard_drv_init(void)
{
printk(KERN_INFO "*********virtual_keyboard_drv_init\n");
int ret = -1;
/* input device (keyboard) register begin */
kb_dev = input_allocate_device();
kb_dev->name = "virtual_keyboard";
kb_dev->evbit[0] = BIT_MASK(EV_KEY)/* | BIT_MASK(EV_REP)*/; /* 不创建重复事件 */
input_set_capability(kb_dev, EV_KEY, KEY_0); /* 设置键值 */
ret = input_register_device(kb_dev);
if (ret != 0)
{
printk("input device register failed.\r\n");
return ret;
}
/* input device (keyboard) register end */
/* sysfs_create_file begin */
kobj = kobject_create_and_add("virtual_keyboard", kernel_kobj);
sysfs_create_file(kobj, &dev_attr_io_bin.attr); // file path is /sys/kernel/{kobj->name}/{dev_attr_io_bin.attr.name}
/* sysfs_create_file end */
return 0;
}
但是我们之前实现的echo是怎么处理的呢?读者可以看这行代码。
static DEVICE_ATTR(io_bin, 0660, io_bin_cat_callback, io_bin_echo_callback); // used to handle io of the sysfs file
这行代码是一种宏定义的使用手法,其等效于以下代码,也就是一个设备的定义。
struct device_attribute dev_attr_io_bin = __ATTR(io_bin, 0660, io_bin_cat_callback, io_bin_echo_callback)
因为我们在sysfs_create_file
时,使用到了dev_attr_io_bin.attr
,所以之前创建的sysfs文件会被绑定到这个dev_attr_io_bin
上。
而dev_attr_io_bin
又在定义时绑定了io_bin_cat_callback, io_bin_echo_callback
两个回调函数。
所以在echo xxx > /sys/kernel/virtual_keyboard/io_bin
和cat > /sys/kernel/virtual_keyboard/io_bin
时,系统会分别调用io_bin_cat_callback, io_bin_echo_callback
这两个函数。
这两个函数不做具体讲解,注释中皆有说明。
之前读Prry前辈代码,花了一下午的时间才搞懂基本逻辑,误以为驱动代码可读性差,但是代码经过自己整理之后发现其实很简单,只是因为他选用了不合适的技术,没有注释,命名混乱,才导致代码如此难懂。我越来越明白代码之中的学问之深,希望我能一直保持思考,写易读易用的代码。