• Linux驱动开发一.字符设备框架1.模块加载和卸载


    从这一章开始,我们开始学习对Linux进行驱动开发。首先我们以字符设备驱动作为入门开始,以一个虚拟设备为例,做一个Linux驱动开发,并写一个APP来测试驱动工作是否正常。

    字符设备

    字符设备是我们最常用到的设备,字符设备是指在I/O传输过程中以字符为单位进行传输的设备,例如键盘,打印机等。在UNIX系统中,字符设备以特别文件方式在文件目录树中占据位置并拥有相应的结点。作为嵌入式设备来说,IO操作(点灯),I2C等通讯、LCD显示,都是操作的字符设备。系统在操作这些设备时,是按照一个字节一个字节的顺序进行读写操作的。具体的操作流程如下:

     整个流程是这样的:

    我们一直在说在Linux中,一切皆为文件,任何成功加载的设备都会在/dev文件下生成一个对应的文件(如下图)

    这些文件都对应了不同的硬件。比如有个led的驱动(/dev/led),那么这个led文件就是对应led灯的驱动文件。在我们想对这个led进行点亮操作都时候就需要通过一个应用程序来对这个驱动文件进行操作。这个过程就是前两个方框里的过程。应用程序通过open()函数打开文件/dev/led这个文件;而我们如果要点亮LED,也就是要向/dev/led这个文件写入对应的数据,这时候就要使用read()函数了。要注意的是应用程序是运行在用户空间中,而linux驱动属于内核的一部分。这里需要额外的操作来实现对内核的操作,即系统调用。通过系统调用,用户空间可以陷入到内核空间中,再通过驱动程序调用底层设备。等不需要该硬件,就反向操作就可以了。

    整个流程大致就是上面说的那样。说起来比较空泛,我们通过一个虚拟的硬件来演示一下流程。

    字符设备驱动框架

    驱动加载

    要调试驱动首先要清楚驱动加载的方式。驱动的加载有两种方式,第一种就是直接编译到内核中,这样在linux启动的时候内核就会直接运行驱动程序;第二种就是编译为模块(.ko文件),在内核启动以后通过命令加载模块。调试的时候一般采用第二种,因为第一种在每次修改程序后都要编译内核,还要重启系统。太麻烦了。在驱动开发完成调试没问题了就可以讲驱动编译进内核里。

    既然要加载模块,就要用到两个函数

    module_init(dev_init);      //模块加载函数注册
    module_exit(dev_exit);      //模块卸载注册函数

    确切来说这两个函数是个注册函数。当我们通过命令“insmod"加载模块的时候,就会调用通过module_init注册的函数(dev_init),而在通过rmmod卸载模块时候就会调用dev_exit函数。

    编写驱动

    我们新建一个文件testDev.c

    /**
     * @file testDev.c
     * @author your name (you@domain.com)
     * @brief 
     * @version 0.1
     * @date 2022-03-23
     * 
     * @copyright Copyright (c) 2022
     * 
     */
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    
    static int __init dev_init(void)
    {   
        printk("device init!\r\n");
        return 0;
    }
    
    
    static int __exit dev_exit(void)
    {
        printk("device exit\r\n");
        return 0;
    }
    module_init(dev_init);      //模块加载
    module_exit(dev_exit);      //模块卸载
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("ZeqiZ");

    文件中我们就定义了两个函数dev_init和dev_exit,在函数中用到了一个printk,这个函数和printf差不多,但是是内核中运行的。

    两个函数通过注册函数进行注册(关于头部信息的导入问题可以看这里)。

    同路径下新建一个makefile文件

    KERNELDIR := /home/qi/IMX6UL/linux/linux/
    CURRENT_PATH := $(shell pwd)
    obj-m := testDev.o
    
    build: kernel_modules
    
    kernel_modules:
        $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
    clean:
        $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

    要注意第一行:因为我们是要通过内核运行驱动,所以这里要指定内核的路径。因为内核还没有下载到emmc中,就可以指定一个本地的内核路径。make以后会生成一个.ko文件

     通过file命令查看一下,可以看出来这个模块是在ARM架构下运行的。没问题了记得修改文件权限,直接777就行了。

    这个驱动框架就完成了。

    驱动框架测试

    modprobe命令使用

    前提条件:开发板通过Uboot启动内核,根目录系统通过nfs挂载。串口调试。

    模块的挂载要用modprobe命令来实现,这里我们不使用前面说的insmod是因为该命令不能解决模块中的依赖关系,比如A.ko依赖了B.ko,我们必须先手动insmod B.ko以后再insmod A.ko。而modprobe就可以解决这个问题。在终端中输入modprobe会提示下面的问题

     提示没有路径,直接mkdir创建

    继续报错,还是没有路径,继续创建

    OK了,上面的流程可以看出来,因为我用的内核是4.1.15,那么modprobe是直接在/lib/modules/4.1.15中进行模块加载的 。把编译好的testDev.ko复制到这个路径下

    qi@qi-Ubuntu:~/IMX6UL/linux_drivers/01_chrdevbase$ sudo cp testDev.ko /home/qi/IMX6UL/rootfs/lib/modules/4.1.15/

    注意路径,不同的根文件系统挂载的路径也不同。复制时一定要加上sudo,完成后可以查看一下

     没问题,加载一下

     这里会报个错,因为一个新的模块在加载时需要一个modules.dep文件,这个文件是不能通过手动输入的,要通过下面的命令:

    /lib/modules/4.1.15 # depmod

    注意一定要在我们新建立的文件路径下!

     这个时候测试一下模块的加载和卸载

    打印出了我们要求的值,原始框架的加载和卸载就完成了。 

  • 相关阅读:
    基于visual Studio2013解决面试题之1307二分查找
    基于visual Studio2013解决面试题之1306奇偶位数交换
    基于visual Studio2013解决面试题之1305字符串所有子集
    cocos2d-x游戏开发系列教程-坦克大战游戏之虚拟手柄控制坦克移动
    cocos2d-x游戏开发系列教程-坦克大战游戏之虚拟手柄的显示
    cocos2d-x游戏开发系列教程-坦克大战游戏之坦克的显示
    基于visual Studio2013解决面试题之1204大数组查找
    6.6 判断字符串是不是字母类型的
    6.4 从字符串中删除不需要的字符
    6.3 计算字符在字符串中出现的次数
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/15857917.html
Copyright © 2020-2023  润新知