精华内容
下载资源
问答
  • 这些驱动程序源码可以修改到内核中,也可以把他们编译成模块形势,在需要的时候动态加载。Linux内核是一个整体是结构,因此向内核添加任何东西,或者删除某些功能,都十分困难。为了解决这个问题引入了内核机制。  ...
  • 这些驱动程序源码可以修改到内核中,也可以把他们编译成模块形势,在需要的时候动态加载。Linux内核是一个整体是结构,因此向内核添加任何东西,或者删除某些功能,都十分困难。为了解决这个问题引入了内核机制。  ...
  • 驱动模块可以内核编译好后动态加载进去,也可以在编译内核的时候就直接添加。
  • 本文主要介绍在Ubuntu上为Android HAL模块访问Linux内核驱动程序,这里给大家提供方法和一个小的测试程序代码,以及常遇到的问题和解决方法,有需要的小伙伴可以参考下
  • linux内核模块加载命令

    千次阅读 2021-01-04 20:25:48
    1、lsmod 列加以挂载的内核模块; lsmod 是列出目前系统中已加载模块的名称及大小等;另外我们还可以查看 /proc/modules ,我们一样可以知道系统已经加载模块; [root@localhost beinan]# lsmod 2、modinfo ...

    一,内核相关的命令

    1、lsmod 列出已经加载的内核模块;

    lsmod 是列出目前系统中已加载的模块的名称及大小等;另外我们还可以查看 /proc/modules ,我们一样可以知道系统已经加载的模块;

    [root@localhost beinan]# lsmod
    

    2、modinfo 查看模块信息;

    modinfo 可以查看模块的信息,通过查看模块信息来判定这个模块的用途;

    [root@localhost beinan]# moinfo  模块名   
    

    举例:查看 br_netfilter模块

    [root@ct ~]# modinfo br_netfilter
    filename:       /lib/modules/3.10.0-957.el7.x86_64/kernel/net/bridge/br_netfilter.ko.xz
    description:    Linux ethernet netfilter firewall bridge
    author:         Bart De Schuymer <bdschuym@pandora.be>
    author:         Lennert Buytenhek <buytenh@gnu.org>
    license:        GPL
    retpoline:      Y
    rhelversion:    7.6
    srcversion:     C4DE536495D55C12BA6A8A8
    depends:        bridge
    intree:         Y
    vermagic:       3.10.0-957.el7.x86_64 SMP mod_unload modversions 
    signer:         CentOS Linux kernel signing key
    sig_key:        B7:0D:CF:0D:F2:D9:B7:F2:91:59:24:82:49:FD:6F:E8:7B:78:14:27
    sig_hashalgo:   sha256
    
    

    上面的例子是我们查看 br_netfilter这个模块的信息,通过查看,我们知道 br_netfilter模块是位于

    /lib/modules/3.10.0-957.el7.x86_64/kernel/net/bridge/br_netfilter.ko.xz
    

    我们再查查vfat 和ntfs 的模块信息;

    [root@localhost beinan]# modinfo vfat
    [root@localhost beinan]# modinfo ntfs 
    

    注意: 模块名是不能带有后缀的,我们通过modprobe -l 所看到的模块,都是带有.ko 或.o后缀;

    3、modprobe 挂载新模块以及新模块相依赖的模块

    modprobe 我们常用的功能就是挂载模块,在挂载某个内核模块的同时,这个模块所依赖的模块也被同时挂载;当然modprobe 也有列出内核所有模块,还有移除模块的功能;下在我们举个例子说一说咱们常用的功能和参数;

    modprobe [-v] [-V] [-C config-file] [-n] [-i] [-q] [-o <modname>] <modname> [parameters...]modprobe -r [-n] [-i] [-v] <modulename> ...modprobe -l -t <dirname> [ -a <modulename> ...]
    

    上面是modprobe 的用法,具体更为详细的帮助,我们可以查看 man modprobe ;

    [root@localhost beinan]# modprobe -c
    

    modprobe -c 可以查看modules 的配置文件,比如模块的别名是什么等;

     modprobe -l
    

    modprobe -l 是列出内核中所有的模块,包括已挂载和未挂载的;通过modprobe -l ,我们能查看到我们所需要的模块,然后根据我们的需要来挂载;其实modprobe -l 读取的模块列表就位于 /lib/modules/‘uname -r’ 目录中;其中uname -r 是内核的版本;

    [root@localhost beinan]# uname -r2.6.11-1.1369_FC4[root@localhost beinan]# ls /lib/modules/2.6.11-1.1369_FC4/
    
    [root@localhost beinan]# modprobe   模块名   注:挂载一个模块;
    

    举例:

    [root@localhost beinan]# modprobe ne2k-pci  //挂载 ne2k-pci 模块;
    [root@localhost beinan]# modprobe vfat   //挂载vfat 模块
    [root@localhost beinan]# modprobe ntfs    //挂载ntfs 模块
    [root@localhost beinan]# lsmod   //列出已挂载模块, 我们会看到ne2k-pci ,vfat ,ntfs的模块;
    

    注意: 模块名是不能带有后缀的,我们通过modprobe -l 所看到的模块,都是带有.ko 或.o后缀;

    [root@localhost beinan]# modprobe -r  模块名  //移除已加载的模块,和rmmod 功能相同;
    

    注意:模块名是不能带有后缀的,我们通过modprobe -l 所看到的模块,都是带有.ko 或.o后缀;

    [root@localhost beinan]# modprobe -r  模块名 
    

    举例:

    [root@localhost beinan]# modprobe  -r ne2k-pci
    

    就说这么多吧,更为详细的还是用 man modprobe 来查看和尝试;

    4、rmmod 移除已挂载模块;

    rmmod  模块名
    

    注意: 模块名是不能带有后缀的,我们通过modprobe -l 所看到的模块,都是带有.ko 或.o后缀;

    举例:

    [root@localhost beinan]# rmmod  vfat  //移除已挂载的模块vfat 
    

    5、depmod 创建模块依赖关系的列表

    这个模块管理工具是创建模块依赖关系的列表,有几个参数我们注意一下就行了,目前的的Linux 发行版所用的内核是2.6x版本,是自动解决依赖关系,所以这个命令知道就行了;模块之前也有依赖关系,比如我们想驱动USB 移动硬盘,目前有两种驱动,一种是udev ,在内核中有,但目前不太稳定;另一种办法是用usb-storage驱动,而usb-storage 依赖的模块是scsi 模块,所以我们要用usb-storage 的模块,也得把scsi 编译安装;

    再举个例子:sata的硬盘,在 Linux中的设备表示的是/dev/sd* ,比如 /dev/sda,/dev/sdb 等… 系统要驱动 sata硬盘,则需要把sata在内核中选中,或编译成模块,或内置于内核之中,在此同时,还需要在内核中选中ide ,scsi 的支持等;
    depmod 工具的洋文原意:depmod — program to generate modules.dep and map files.(译的:为modules.dep 文件或映射文件创建依赖关系)

    [root@localhost beinan]# depmod  -a    注:为所有列在/etc/modprobe.conf 或/etc/modules.conf  中的所有模块创建依赖关系,并且写入到modules.dep文件;
    [root@localhost beinan]# depmod -e     注:列出已挂载但不可用的模块;
    [root@localhost beinan]# depmod  -n    注:列出所有模块的依赖关系,但仅仅是输出出来 (Write the dependency file on stdout only) modules.dep 位于 /lib/modules/内核版本 目录
    

    比如 Fedora Core 4.0 中,系统默认的内核:

    [root@localhost beinan]# ls /lib/modules/2.6.11-1.1369_FC4/modules.dep/lib/modules/2.6.11-1.1369_FC4/modules.dep
    

    6、insmod 挂载模块;

    insmod 这个工具,和modprobe 有点类似,但功能上没有modprobe 强,modprobe 在挂载模块是不用指定模块文件的路径,也不用带文件的后缀.o 或.ko ;而insmod 需要的是模块的所在目录的绝对路径,并且一定要带有模块文件名后缀的(modulefile.o 或modulesfile.ko );

    对于这个工具,我们只是介绍一下, 并不推荐使用。因为模块有依赖关系,对于新手来说,可能不知道这个模块依赖和哪个模块依赖;

    举例:

    [root@localhost beinan]# insmod /lib/modules/2.6.11-1.1369_FC4/kernel/drivers/net/tg3.ko
    

    我们要到 /lib/modules/内核版本 uname -r 的命令输出/kernel/drivers 中找相对应的模块才行,要有绝对路径,而且必须要用到文件名的全称,不能把文件名的后缀省略;

    二,与内核模块加载相关的配置文件;

    1、模块的配置文件 modules.conf 或 modprobe.conf

    内核模块的开机自动挂载模块一般是位于一个配置文件,一般的Linux发行版本都有 /etc/modules.conf 或 /etc/modprobe.conf 。比如Fedora Core 4.0 内核模块开机自动加载文件是 /etc/modprobe.conf ;在这个文件中,一般是写入模块的加载命令或模块的别名的定义等;比如我们在modules.conf 中可能会发行类似的一行 ;

    alias eth0 8139too 
    

    而8029的网卡应该是

    alias eth0 ne2k-pci 
    

    这样系统启动的时候,首先会modprobe 8139too ,然后再为8139too 指定别名为 eth0,然后我们在登录的时候,用 ifconfig 就会查看到网卡的IP 等情况,当然您得为网卡设置IP 才行;

    一般的情况下,modproe.conf 或modules.conf的内容是我们用相应的硬件配置工具而生成的;如果您的硬件驱动是没有被内核支持,您自己到硬件的厂商下载而来的驱动。一般的情况下都有安装和帮助文件。他们的驱动在配置时,他会写入硬件的支持到modules.conf 或modprobe.conf 文件中。

    再比如我们的声卡在modules.conf 或modprobe.conf 中也有相应的内容,这是由alsaconf 配置工具生成的,明白了吧;同理网卡在modprobe.conf 或modules.conf中的内容也是由网卡的配置工具而来的。

    有些硬件是以内核模块的方式驱动的,模块一旦加载上就能用,也没有什么配置工具,比如vfat 和ntfs 的支持;如果是硬件驱动不以模块的方式支持,而是直接编入内核,也不会用在modprobe.conf 或 modules.conf 中加入什么内容;

    如果您有些模块不能开机加载,您想让一些模块加机自动加载,就可以把modprobe 模块 直接写入配置文件;

    2、内核模块其它配置文件

    内核模块的其它配置文件还是需要了解的,比如 /lib/modules/内核版本目录下的几个文件;了解一下就行;比如:

    [root@localhost beinan]# uname -r2.6.11-1.1369_FC4
    [root@localhost beinan]# ls /lib/modules/2.6.11-1.1369_FC4/build   
    misc  modules.ccwmap  modules.ieee1394map  modules.isapnpmap
    modules.symbols  sourcekernel  modules.alias  modules.dep 
    modules.inputmap  modules.pcimap   modules.usbmap
    

    三,、硬件驱动在系统中的目录;

    硬件驱动是必须由内核支持的,无论是我们自己安装驱动,还是内核自带的驱动都是如此。硬件驱动如果是以内核模块支持的,驱动目录位于: /lib/modules/内核版本/kernel/目录 或 /lib/modules/内核版本/kernel/drivers 目录中;

    [root@localhost beinan]# uname -r2.6.11-1.1369_FC4
    [root@localhost beinan]# ls /lib/modules/2.6.11-1.1369_FC4/
    kernelarch  crypto  drivers  fs  lib  net  sound
    

    注:只有驱动在内核中以模块的方法支持,驱动才位于 /lib/modules/相应的目录;如果是直接置入内核的,不会出现在/lib/modules驱动相关的目录;

    四、自己编译驱动的办法;

    通过源码编译驱动一般是./configure ;make;make install ,有时程序不提供./configure ,我们可以make或make install ,或者执行make;make install ;如果不能make install ,则需要我们自己复制.o或者.ko文件到 /lib/modules/内核版本/kernel/目录 或 /lib/modules/内核版本/kernel/drivers 目录中相应的驱动目录;

    这个还是自己尝试吧,说也说不清楚,遇到问题后就知道怎么弄了;具体的还是驱动的REAME和INSTALL为准;

    现在大多驱动都是在编译安装时,都自动复制.o或.ko 文件到内核模块目录,大多不用我们自己动手复制过去。如果您尝试编译安装声卡驱动 alsa-drivers 就会明白以上意思;

    展开全文
  • 如何将驱动模块编译进内核

    千次阅读 2018-03-08 08:57:55
    我们知道若要给linux内核添加模块(驱动)有如下两种方式: (1)动态方式:采用insmod命令来给运行中的linux加载模块。 (2)静态方式:修改linux的配置菜单,添加模块相关文件源码对应目录,然后把模块直接编译...

    转载地址:http://blog.csdn.net/marz07101/article/details/7647400

    我们知道若要给linux内核添加模块(驱动)有如下两种方式:

    1)动态方式:采用insmod命令来给运行中的linux加载模块。

    2)静态方式修改linux的配置菜单,添加模块相关文件到源码对应目录,然后把模块直接编译进内核。

    对于动态方式,比较简单,下面我们介绍如何采用静态的方式把模块添加到内核。

    最终到达的效果是:在内核的配置菜单中可以配置我们添加的模块,并可以对我们添加的模块进行编译。

    内核的配置系统组成

    首先我们要了解Linux 2.6内核的配置系统的原理,比如我们在源码下运行“make menuconfig ”为神马会出现一个图形配置菜单,配置了这个菜单后又是如何改变了内核的编译策略滴。

    内核的配置系统一般由以下几部分组成:

    1Makefile分布在Linux内核源代码中的Makefile,定义Linux内核的编译规则。

    2)配置文件(Kconfig)给用户提供配置选项,修改该文件来改变配置菜单选项。

    3)配置工具包括配置命令解释器(对配置脚本中使用的配置命令进行解释),配置用户界面(提供字符界面和图形界面)。这些配置工具都是使用脚本语言编写的,如Tcl/TKPerl等。

    其原理可以简述如下:这里有两条主线,一条为配置线索,一条为编译线索。配置工具根据kconfig配置脚本产生配置菜单,然后根据配置菜单的配置情况生成顶层目录下的.config,在.config里定义了配置选择的配置宏定义,如下所示:


    如上所示,这里定义的这些配置宏变量会在Makefile里出现,如下所示:


    然后make 工具根据Makefile里这些宏的赋值情况来指导编译。所以理论上,我们可以直接修改.configMakefile来添加模块,但这样很麻烦,也容易出错,下面我们将会看到,实际上我们有两种方法来很容易的实现。

    如何添加模块到内核

    实际上,我们需要做的工作可简述如下:

    1将编写的模块或驱动源代码(比如是XXOO)复制到Linux内核源代码的相应目录。

    2)在该目录下的Kconfig文件中依葫芦画瓢的添加XXOO配置选项。

    3)在该目录的Makefile文件中依葫芦画瓢的添加XXOO编译选项。

    可以看到,我们奉行的原则是“依葫芦画瓢”,主要是添加

    一般的按照上面方式又可出现两种情况,一种为给XXOO驱动添加我们自己的目录,一种是不添加目录。两种情况的处理方式有点儿不一样哦。

    不加自己目录的情况

    1)把我们的驱动源文件(xxoo.c)放到对应目录下,具体放到哪里需要根据驱动的类型和特点。这里假设我们放到./driver/char下。

    2)然后我们修改./driver/char下的Kconfig文件,依葫芦添加即可,如下所示:


    注意这里的LT_XXOO这个名字可以随便写,但需要保持这个格式,他并不需要跟驱动源文件保持一致,但最好保持一致,等下我们在修改Makefile时会用到这个名字,他将会变成CONFIG_LT_XXOO,那个名字必须与这个名字对应。如上所示,tristate定义了这个配置选项的可选项有几个,help定义了这个配置选项的帮助信息,具体更多的规则这里不讲了。

    3)然后我们修改./driver/char下的Makefile文件,如下所示:


    这里我们可以看到,前面Kconfig里出现的LT_XXOO,在这里我们就需要使用到CONFIG_XXOO,实际上逻辑是酱汁滴:在Kconfig里定义了LT_XXOO,然后配置完成后,在顶层的.config里会产生CONFIG_XXOO,然后这里我们使用这个变量。

    到这里第一种情况下的添加方式就完成了。

    添加自己目录的情况

    1)在源码的对应目录下建立自己的目录(xxoo),这里假设为/drivers/char/xxoo 

    2) 把驱动源码放到新建的xxoo目录下,并在此目录下新建KconfigMakefile文件。然后给新建的KconfigMakefile添加内容。

    Kconfig下添加的内容如下:


    这个格式跟之前在Kconfig里添加选项类似。

    Makefile里写入的内容就更少了:

    添加这一句就可以了。

    3)第三也不复杂,还是依葫芦画瓢就可以了。

    我们在/drivers/char目录下添加了xxoo目录,我们总得在这个配置系统里进行登记吧,哈哈,不然配置系统怎么找到们呢。由于整个配置系统是递归调用滴,所以我们需要在xxoo的父目录也即char目录的KconfigMakefile文件里进行登记。具体如下:

    a). drivers/char/Kconfig中加入:source drivers/char/xxoo/Kconfig

    b). drivers/char/Makefile中加入:obj-$(CONFIG_LT_XXOO) += xxoo/

    添加过程依葫芦画瓢就可以了,灰常滴简单。

    好了,到这里如何给内核添加我们自己的模块或驱动就介绍完了。哈哈。。。

    展开全文
  • 驱动程序的使用可以按照两种方式编译,一种是静态编译进内核,另一种是编译成模块以供动态加载。由于uClinux不支持模块动态加载,而且嵌入式Linux不能够象桌面Linux那样灵活的使用insmod/rmmod加载卸载设备驱动程序...
  • linux内核驱动模块加载方式

    千次阅读 2010-03-01 21:43:00
    在类unix操作系统中,驱动加载方式一般...一、动态加载 动态加载是将驱动模块加载到内核中,而不能放入/lib/modules/下。 在2.4内核中,加载驱动命令为:insmod ,删除模块为:rmmod; 在2.6以上内核中,除了insmod与

    在类unix 操作系统 中,驱动 加载 方式一般分为:动态加载和静态加载,下面分别对其详细论述。
    一、动态加载
        动态加载是将驱动模块加载到内核 中,而不能放入/lib/modules/下。
         在2.4内核中,加载驱动命令 为:insmod ,删除模块为:rmmod;
         在2.6以上内核中,除了insmod与rmmod外,加载命令还有modprobe;
         insmod与modprobe不同之处:
         insmod 绝对路径/××.o,而modprobe ××即可,不用加.ko或.o后缀,也不用加路径;最重要的一点是:modprobe同时会加载当前模块所依赖的其它模块;
         lsmod查看当前加载到内核中的所有驱动模块,同时提供其它一些信息,比如其它模块是否在使用另一个模块。
    二、静态加载
    (一)概念
         在执行make menuconfig命令进行内核配置裁剪时,在窗口中可以选择是否编译入内核,还是放入/lib/modules/下相应内核版本目录中,还是不选。
    (二)操作步骤
         linux设备一般分为:字符设备、块设备和网络 设备,每种设备在内核源代码目录树drivers/下都有对应的目录,其加载方法类似,以下以字符设备静态加载为例,假设驱动程序源代码名为ledc.c,具体操作步骤如下:

         第一步:将ledc.c源程序放入内核源码drivers/char/下;
         第二步:修改drivers/char/Config.in文件,具体修改如下:
                按照打开文件 中的格式添加即可;
                在文件的适当位置(这个位置随便都可以,但这个位置决定其在make menuconfig窗口中所在位置)加入以下任一段代码:

        第三步:编辑配置文件Kconfig,加入驱动选项,使之在make menuconfig 的时候出现
    打开 kernel-2.6.13/drivers/char/Kconfig 文件,添加
    config DEMO_WYK
            tristate "DEMO test module"
            depends on X86_32

    其他版本有:config QQ2440_HELLO_MODULE
            tristate "QQ2440/mini2440 Hello Module sample"
            depends on ARCH_S3C2410
            help
            QQ2440 hello module sample

    or

       tristate 'LedDriver' CONFIG_LEDC
                if [ "$CONFIG_LEDC" = "y" ];then
                bool '  Support for led on h9200 board' CONFIG_LEDC_CONSOLE
                fi
                说明:以上代码使用tristate来定义一个宏,表示此驱动可以直接编译至内核(用*选择),也可以编制至/lib/modules/下(用M选择),或者不编译(不选)。

                bool 'LedDriver' CONFIG_LEDC
                if [ "$CONFIG_LEDC" = "y" ];then
                bool '  Support for led on h9200 board' CONFIG_LEDC_CONSOLE
                fi
                说明:以上代码使用tristate来定义一个宏,表示此驱动只能直接编译至内核(用*选择)或者不编译(不选),不能编制至/lib/modules/下(用M选择)。

         第四步:修改drivers/char/Makefile文件
                在适当位置加入下面一行代码:
                 obj-$(CONFIG_LEDC)  +=  ledc.o
                或者在obj-y一行中加入ledc.o,如:
                obj-y += ledc.o mem.o 后面不变;

    Step5:在kernel-2.6.13目录下执行make menuconfig
    在Device Driver——Character Driver下将QQ2440_HELLO_MODULE选为M,模块方式,退出保存设置。

    Step6:执行命令 make modules。在char目录下产生qq2440_hello_module.ko的文件。传到开发板上。
    Step7:在超级终端 执行 chmod a+rwx qq2440_hello_module.ko
    Step8:加载驱动模块,执行insmod qq2440_hello_module.ko
    显示:
    Hello, QQ2440 module is installed !
    Step9:卸载驱动模块,执行rmmod qq2440_hello_module.ko
    显示加载驱动模块,执行
    Good-bye, QQ2440 module was removed!

    展开全文
  • Linux内核模块(Linux Kernel Module)和硬件驱动程序(Hardware Driver)ioctl写一个简单的内核模块内核模块HelloWorld内核模块Makefile编写一个提供ioctl接口的设备驱动程序内核模块)Linux的设备设备的分类设备...

    什么是Linux内核模块、驱动程序和ioctl?

    Linux内核模块(Linux Kernel Module)和硬件驱动程序(Hardware Driver)

    Linux使用可加载的内核模块(loadable kernel module, LKM)来在运行时动态的添加(或删除)Linux内核的代码。
    它有几点好处:
    体积:模块化的系统可以减少Linux内核的体积,需要用到的模块用时再加载。
    可扩展+安全:内核模块是运行在内核态的,它与用户态应用分离,增强了安全性,又保证了内核的可扩展性。

    一个比较典型的Linux可加载内核模块就是Linux中设备的驱动程序。
    驱动程序如下图所示,它向下直接与硬件沟通,向上为系统调用提供具体实现。
    经过驱动程序的这一层封装,让用户态的程序无需关心硬件运行细节,而直接使用其提供的API,降低了(用户态)程序开发者的开发难度。
    Linux kernel space and user space

    ioctl

    刚才提到,用户态程序是无法直接调用内核态API的,那么应用程序开发者如何与硬件沟通?
    Linux就提供了一系列的系统调用,用来让开发者在用户态与系统沟通,进而与硬件沟通,其中就包括了我们常用的open、close、read、write和ioctl。

    用户态的ioctl原型为

    int ioctl(int fd, int cmd, ...)
    

    而陷入内核之后,ioctl的函数原型为

    int (*ioctl)(struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)
    

    他们之间的关系如下图所示,fd被转换为两个结构体,用来标识操作的设备文件,cmd被原封不动的传入了驱动中。ioctl

    写一个简单的内核模块

    内核模块HelloWorld

    我们结合代码来看一下一个最简单的内核模块的例子

    #include <linux/init.h>             // 包含了__init和__exit函数的宏定义
    #include <linux/module.h>           // 可加载内核模块的核心的头文件
    #include <linux/kernel.h>           // 包含了kernel中的类型、宏和函数等
    
    MODULE_LICENSE("GPL");              //此模块使用的licence,如果不用GPL的话编译时会出warning
    MODULE_AUTHOR("StayrealS");      ///模块作者,加载后使用modeinfo可以在系统中看到
    MODULE_DESCRIPTION("A simple Linux driver.");  //模块信息,加载后使用modeinfo可以在系统中看到
    MODULE_VERSION("0.1");              ///模块版本,加载后使用modeinfo可以在系统中看到
    
    
    /** @brief Module初始化函数,作为module的入点,使用insmod加载时会调用本函数。
     */
    static int __init helloHW_init(void){
       printk(KERN_INFO "HW: HelloWorld from the BBB LKM!\n");
       return 0;
    }
    
    /** @brief 与init函数相似,在rmmod卸载本模块的时候会调用本函数进行清理等工作。
     */
    static void __exit helloHW_exit(void){
       printk(KERN_INFO "HW: Goodbye from the HW LKM!\n");
    }
    
    /** @brief 一个模块必需使用module_init() 和module_exit() 宏来
     *  定义module的入点和出点
     */
    module_init(helloHW_init);
    module_exit(helloHW_exit);
    

    内核模块Makefile

    另外,还需要Makefile

    obj-m+=hello.o
    
    all:
    	make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules # 使用M=本文件夹的方式编译本文件夹下的.o为内核模块.ko
    clean:
    	make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
    

    其中$shell uname -r用来打印本机linux 内核的版本信息,用于找寻linux内核关于编译module用到的工具。接下来,只需要使用make就可以将hello world编译成为后缀名为.ko的可加载module。
    接下来,使用

    insmod hello.ko # 加载模块
    lsmod # 查看已加载的模块
    rmmod hello.ko  # 卸载模块
    

    来加载或卸载编写好的模块。

    刚才在编写模块的时候打印是使用了printk,它是printf的内核态版本。
    printk的输出不会打印在linux终端上,但是会打印到内核缓冲区中,可以使用

    dmesg # 查看内核缓冲区
    dmesg -c # 清空内核缓冲区
    

    编写一个提供ioctl接口的设备驱动程序(内核模块)

    如第一部分中所说,设备驱动程序是内核模块的一个应用案例。本节我们将编写一个提供ioctl接口的设备驱动程序。

    Linux的设备

    设备的分类

    Linux中I/O设备分为两类:字符设备和块设备。两种设备本身没有严格限制,但是,基于不同的功能进行了分类。

    • 字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取。相反,此类设备支持按字节/字符来读写数据。举例来说,键盘、串口、调制解调器都是典型的字符设备。
    • 块设备:应用程序可以随机访问设备数据,程序可自行确定读取数据的位置。硬盘、软盘、CD-ROM驱动器和闪存都是典型的块设备,应用程序可以寻址磁盘上的任何位置,并由此读取数据。此外,数据的读写只能以块(通常是512B)的倍数进行。与字符设备不同,块设备并不支持基于字符的寻址。

    这两种设备都可以通过访问文件系统中的设备文件(在/dev下)来访问。例如,接下来我们要编写的一个虚拟字符设备,就被挂载在/dev/test处。

    设备的主编号(major number)与副编号(minor number)

    设备的主编号是为了让操作系统区分不同种类的设备的。例如/dev/ram0/dev/null的主编号都是1。
    设备的副编号是为了区分同种类的不同设备的,例如以上两个设备的副编号一定不同。
    设备的主副编号可以在/dev文件夹下使用ls -l查看。如下图:第一个设备apm_bios的主编号为10,副编号为134。
    主编号副编号

    编写驱动程序

    #include <linux/module.h>    // included for all kernel modules
    #include <linux/kernel.h>    // included for KERN_INFO
    #include <linux/init.h>      // included for __init and __exit macros
    #include <linux/scpi_protocol.h>
    #include <asm/io.h>
    #include <linux/slab.h>
    #include <linux/fs.h>        // file_operation is defined in this header
    
    #include <linux/device.h>
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("StayrealS");
    MODULE_DESCRIPTION("Driver as a test case");
    
    static int      majorNumber;
    static struct   class*  test_module_class = NULL;
    static struct   device* test_module_device = NULL;
    #define DEVICE_NAME "test"      //define device name
    #define CLASS_NAME  "test_module"
    
    //函数原型
    static long test_module_ioctl(struct file *, unsigned int, unsigned long);
    static int __init test_init(void);
    static void __exit test_exit(void);
    
    ///linux/fs.h中的file_operations结构体列出了所有操作系统允许的对设备文件的操作。在我们的驱动中,需要将其中需要的函数进行实现。下面这个结构体就是向操作系统声明,那些规定好的操作在本模块里是由哪个函数实现的。例如下文就表示unlocked_ioctl是由本模块中的test_module_ioctl()函数实现的。
    static const struct file_operations test_module_fo = {
            .owner = THIS_MODULE,
            .unlocked_ioctl = test_module_ioctl,
    };
    
    //本模块ioctl回调函数的实现
    static long test_module_ioctl(struct file *file,        /* ditto */
                     unsigned int cmd,      /* number and param for ioctl */
                     unsigned long param)
    {
            /* ioctl回调函数中一般都使用switch结构来处理不同的输入参数(cmd) */
            switch(cmd){
            case 0:
            {
                    printk(KERN_INFO "[TestModule:] Inner function (ioctl 0) finished.\n");
                    break;
            }
            default:
                    printk(KERN_INFO "[TestModule:] Unknown ioctl cmd!\n");
                    return -EINVAL;
            }
            return 0;
    }
    
    
    static int __init test_init(void){
            printk(KERN_INFO "[TestModule:] Entering test module. \n");
            // 在加载本模块时,首先向操作系统注册一个chrdev,也即字节设备,三个参数分别为:主设备号(填写0即为等待系统分配),设备名称以及file_operation的结构体。返回值为系统分配的主设备号。
            majorNumber = register_chrdev(0, DEVICE_NAME, &test_module_fo);
            if(majorNumber < 0){
                    printk(KERN_INFO "[TestModule:] Failed to register a major number. \n");
                    return majorNumber;
            }
            printk(KERN_INFO "[TestModule:] Successful to register a major number %d. \n", majorNumber);
    
            //接下来,注册设备类
            test_module_class = class_create(THIS_MODULE, CLASS_NAME);
            if(IS_ERR(test_module_class)){
                    unregister_chrdev(majorNumber, DEVICE_NAME);
                    printk(KERN_INFO "[TestModule:] Class device register failed!\n");
                    return PTR_ERR(test_module_class);
            }
            printk(KERN_INFO "[TestModule:] Class device register success!\n");
    
            //最后,使用device_create函数注册设备驱动
            test_module_device = device_create(test_module_class, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
            if (IS_ERR(test_module_device)){               // Clean up if there is an error
                    class_destroy(test_module_class);           // Repeated code but the alternative is goto statements
                    unregister_chrdev(majorNumber, DEVICE_NAME);
                    printk(KERN_ALERT "Failed to create the device\n");
                    return PTR_ERR(test_module_device);
            }
            printk(KERN_INFO "[TestModule:] Test module register successful. \n");
            return 0;
    }
    
    
    static void __exit test_exit(void)
    {
    		//退出时,依次清理生成的device, class和chrdev。这样就将系统/dev下的设备文件删除,并自动注销了/proc/devices的设备。
            printk(KERN_INFO "[TestModule:] Start to clean up module.\n");
            device_destroy(test_module_class, MKDEV(majorNumber, 0));
            class_destroy(test_module_class);
            unregister_chrdev(majorNumber, DEVICE_NAME);
            printk(KERN_INFO "[TestModule:] Clean up successful. Bye.\n");
    
    }
    
    module_init(test_init);
    module_exit(test_exit);
    

    相信读者也发现了,在我的test_module_fo结构体中,没有直接使用.ioctl=test_module_ioctl而是使用了.unlocked_ioctl=test_module_ioctl。这是因为在较新的Linux内核中,已经修改了系统调用ioctl的内部实现为unlocked_ioctl,实现时要注意这一点。最新的file_operation结构体定义如下:(我使用的内核版本在/usr/src/linux-headers-4.4.145-armv7-x16/include/linux/fs.h中)

     // Note: __user refers to a user-space address.
    struct file_operations {
       struct module *owner;                             // Pointer to the LKM that owns the structure
       loff_t (*llseek) (struct file *, loff_t, int);    // Change current read/write position in a file
       ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);    // Used to retrieve data from the device
       ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);   // Used to send data to the device
       ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);  // Asynchronous read
       ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); // Asynchronous write
       ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);            // possibly asynchronous read
       ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);           // possibly asynchronous write
       int (*iterate) (struct file *, struct dir_context *);                // called when VFS needs to read the directory contents
       unsigned int (*poll) (struct file *, struct poll_table_struct *);    // Does a read or write block?
       long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); // Called by the ioctl system call
       long (*compat_ioctl) (struct file *, unsigned int, unsigned long);   // Called by the ioctl system call
       int (*mmap) (struct file *, struct vm_area_struct *);                // Called by mmap system call
       int (*mremap)(struct file *, struct vm_area_struct *);               // Called by memory remap system call 
       int (*open) (struct inode *, struct file *);             // first operation performed on a device file
       int (*flush) (struct file *, fl_owner_t id);             // called when a process closes its copy of the descriptor
       int (*release) (struct inode *, struct file *);          // called when a file structure is being released
       int (*fsync) (struct file *, loff_t, loff_t, int datasync);  // notify device of change in its FASYNC flag
       int (*aio_fsync) (struct kiocb *, int datasync);         // synchronous notify device of change in its FASYNC flag
       int (*fasync) (int, struct file *, int);                 // asynchronous notify device of change in its FASYNC flag
       int (*lock) (struct file *, int, struct file_lock *);    // used to implement file locking};
    

    同理,仿照上文编写makefile、使用insmod将设备(模块)加载。
    接下来,我们可以编写应用程序来访问这个新添加的设备了。

    #include <stdlib.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    
    
    int main(){
            int fd;
            fd = open("/dev/test", O_RDWR); //我们的设备挂载在/dev/test处
            if (fd < 0){
                    perror("Failed to open the device...");
                    return errno;
            }else{
                    printf("Open device successful!\n");
            }
            ioctl(fd, 0);
            printf("Called ioctl with parameter 0!\n");
    }
    

    注意,运行本程序时需要使用sudo命令,否则可能因为权限问题访问设备文件失败。可以使用/etc/udev/rules.d中的规则将这个新设备文件调整权限,具体请参阅其他文章。

    Q:我们并没有实现open函数,操作系统也能将它打开吗?
    A:答案是是的。操作系统默认帮我们实现了基础的open、close函数,方便开发者进行开发。

    参考文献

    Linux设备文件系统
    IOCTL
    derekmolly
    Linux Driver Tutorial
    字符驱动框架入门

    展开全文
  • 编译Linux驱动程序模块,自定义内核和引导程序
  • 驱动程序直接编译进内核

    千次阅读 2016-04-07 19:41:03
    hello程序直接编译进内核: 第一;先将hello.c拷贝在内核源代码中drivers/char/,vi /drivers/char/Kconfig 添加 config HELLO_DRIVER  bool(tristate) "my hello driver"。 然后回到源代码目录下make ...
  • linux内核模块加载及卸载

    千次阅读 2019-09-18 09:13:33
    在具体的设备驱动开发中,将驱动编译为内核模块也有很强的工程意义,因为如果将正在开发中的驱动直接编译入内核,而开发过程中会不断修改驱动的代码,则需要不断地编译内核并重启内核,但是如果编译为模块,则只需要...
  • linux驱动模块加载和卸载

    千次阅读 2020-01-09 12:04:03
    Linux 驱动有两种运行方式,第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启动的时候就会自动运行驱动程序。第二种就是将驱动编译成模块(Linux 下模块扩展名为.ko),在Linux 内核启动以后使用“insmod”...
  • Linux中的大部分驱动程序,是以模块的形式编写的.这些驱动程序源码可以修改到内核中,也可以把他们编译成模块形势,在需要的时候动态加载.
  • 在Linux内核中增加新驱动模块

    千次阅读 2018-11-03 09:29:46
    开发环境 开发板:A33-Vstar 开发板系统: Linux/arm 3.4.39 Kernel Ubuntu版本:Ubuntu14.04 ...新增内核驱动,并可以通过make menuconfig配置。 内核完整路径:~/A33-Vstar/dragonboard/linux...
  • 内核加载驱动的实现

    万次阅读 2018-08-28 21:56:23
    利用udevd加载驱动内核实现,直接上调用栈吧): 查看驱动相关命令 cat /lib/modules/3.10.0-327.el7.x86_64/modules.dep|grep tun 查看系统中模块依赖关系 nm kernel/net/openvswitch/vport-geneve.ko ...
  • Linux内核设备驱动模块自动加载机制

    万次阅读 2013-08-08 15:06:06
    摘要: 现在大多数硬件设备的驱动都是作为模块出现的,Linux启动过程中会自动加载这些模块,本文通过内核源码简要说明这个过程。 1 驱动模块本身包含设备商、设备ID号等详细信息 如果想让内核启动过程中自动加载某个...
  • 在嵌入式Linux操作系统下,一般是利用insmod指令来实现向系统内核载入模块,如载入键盘驱动: # insmod /lib/modules/2.6.30/em9x60_keypad 在嵌入式应用中,往往是需要自动运行模式,这就需要通过编程来实现。...
  • 设备驱动程序是操纵设备的内核模块,I/O 管理器、即插即用管理器和电源管理器都需要与设备的驱动程序打交道。 在Windows I/O 系统中,设备驱动程序不仅为操作系统提供了支持各种 I/O 设备的能力, 也是 Windows ...
  • 内核模块是Linux内核向外部提供的一个插口,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),我们简称为模块。Linux内核之所以提供模块机制,是因为它本身是一个单内核(monolithic kernel)。单内核的...
  • HelloWorld 程序编写及编入内核驱动, 创建一个 hello 的驱动,在开机的时候加载,并打印"Hello world",相关简介: 1,增加驱动文件 hello.c 和对应的 Makefile、Kconfig 2,修改上一级的 Makefile 与 Kconfig 3,...
  • 内核模块加载命令

    千次阅读 2014-08-08 10:35:16
    1、lsmod 列加以挂载的内核模块; lsmod 是列出目前系统中已加载模块的名称及大小等;另外我们还可以查看 /proc/modules ,我们一样可以知道系统已经加载模块; 代码:[root@localhost beinan]# lsmod 2、...
  • 《深入Linux设备驱动程序内核机制》 第1章 内核模块。内容包括内核模块加载过程,内核模块如何向外界导出符号,模块如何使用内核或者其他模块导出的符号,模块间的依赖关系,模块的版本控制等内幕技术细节。
  • Linux内核模块加载

    千次阅读 2013-01-05 13:20:59
    本系列参考陈学松的《深入Linux设备驱动程序内核机制》 Linux内核模块形式上以.ko文件存在,概念上类似于Windows的动态链接库dll,内核模块可以在系统运行期间动态扩展系统功能而无须重新编译一个新的内核镜像...
  • 经常在 Android 的 init.rc 文件中看到类似 insmod *.ko 这样的命令,为某个设备加载驱动程序,在 insmod 命令的实现过程中,会用到 Linux 系统的系统调用函数: int init_module(void *module_image, unsigned ...
  • 这里不为真实的硬件设备编写内核驱动程序,为了方便描述编写内核驱动程序的过程,我们使用一个虚拟的硬件设备。先大概看下内核驱动程序的编写流程,例子没有实际意义,只是模板。 1.在kernel/drivers目录下新建...
  • Linux驱动模块编译进内核

    千次阅读 2016-08-26 16:46:30
    一、 驱动程序编译进内核的步骤 在 linux 内核中增加程序需要完成以下三项工作: 1. 将编写的源代码复制 Linux 内核源代码的相应目录; 2. 在目录的 Kconfig 文件中增加新源代码对应项目的编译配置选项; 3. ...
  • 1.Linux驱动的加载方法:在驱动路径下 insmod testdiver.ko 2.卸载方法:任何路径下 rmmmod testdiver 3.怎么判断驱动是否加载: 3.1进入根目录 : find | grep testdiver ,可查看...查看驱动程序的主设备号: ...
  • Linux驱动程序如何编译进内核

    千次阅读 2016-05-28 22:22:55
    Linux驱动程序如何编译进内核 2014-04-17 17:48 241人阅读 评论(0) 收藏 举报  分类:   linux 驱动(8)  版权声明:本文为博主原创文章,未经博主允许不得转载。  很多刚接触...
  • 内核模块加载与卸载

    千次阅读 2019-08-19 18:46:11
    2: 内核模块存放的位置 /lib/modules/($uname -r)/kernel 3: 模块的依赖性:检查 /lib/modules/($uname -r)/modules.dep文件,它记录了内核支持模块的依赖关系。 .dep文件的制作:通过depmod命令 eg:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,946
精华内容 24,778
关键字:

驱动程序模块加载到内核