• Linux驱动开发入门记录——(三)虚拟输入设备


    参考:【Linux驱动编程】通过一个虚拟input设备熟悉input设备驱动开发步骤

    参考Github:Prry/linux-drivers
    修改后代码:

    代码使用

    本部分介绍参考Github中的代码使用。该代码虚拟了一个键盘,通过向某文件写入内容,可以模拟按键按下与松开。
    进入linux-drivers/input目录,运行如下命令

    su # 输入密码,此步必须完成,否则后续写文件时会出现即使使用sudo也无法
    make
    insmod devinput.ko
    
    • 1
    • 2
    • 3

    此时,执行以下命令,便可模拟按下并松开一次0(一次不行多试几次)

    echo 1 > /sys/bus/platform/devices/dev_input/input_bin &&	echo 0 > /sys/bus/platform/devices/dev_input/input_bin
    
    • 1

    或单独执行以下命令模拟按下(可以看到一直有0打出来)

    echo 1 > /sys/bus/platform/devices/dev_input/input_bin
    
    • 1

    原理详解

    虽然很感谢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);
    
    • 1
    • 2

    以上两句代码表明,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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    回调函数

    但是我们之前实现的echo是怎么处理的呢?读者可以看这行代码。

    static DEVICE_ATTR(io_bin, 0660, io_bin_cat_callback, io_bin_echo_callback); // used to handle io of the sysfs file
    
    • 1

    这行代码是一种宏定义的使用手法,其等效于以下代码,也就是一个设备的定义。

    struct device_attribute dev_attr_io_bin = __ATTR(io_bin, 0660, io_bin_cat_callback, io_bin_echo_callback)
    
    • 1

    因为我们在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前辈代码,花了一下午的时间才搞懂基本逻辑,误以为驱动代码可读性差,但是代码经过自己整理之后发现其实很简单,只是因为他选用了不合适的技术,没有注释,命名混乱,才导致代码如此难懂。我越来越明白代码之中的学问之深,希望我能一直保持思考,写易读易用的代码。

  • 相关阅读:
    【雷达】基于核聚类实现雷达信号在线分选附matlab代码
    基于单片机的电源切换控制器设计(论文+源码)
    新手炒外汇,如何防止炒外汇被坑?
    若依vue集成electron实现打包exe应用程序
    AQS源码二探-JUC系列
    【游戏技术】L4D 求生之路自主开发模式汇总
    大二《web课程设计》网页制作HTML个人主题青春网站(带psd)
    2022前端HTML5面试题
    6-9接口应用:工厂模式
    mysql存储过程
  • 原文地址:https://blog.csdn.net/Piamen/article/details/126781123