Linux驱动开发之构建第一个驱动程序

简介: Linux驱动开发基础

构建第一个驱动程序

驱动模块的组成

一个驱动模块主要由以下部分组成:、

组成 选项
头文件 必选
模块参数 可选
模块功能函数 可选
其他 可选
模块加载函数 必须
模块卸载函数 必须
模块许可声明 必须

1.头文件(必选)

#include <linux/module.h>
#include <linux/init.h>

2.模块参数(可选 )

模块参数是驱动模块加载时,需要传递给驱动模块的参数。

3.模块加载函数(必须 )

模块加载函数是模块加载时,需要执行的函数。

4.模块卸载函数(必须)

模块卸载函数是模块卸载时,需要执行的函数。

5.模块许可声明(必须)

模块许可声明表示模块受内核支持的程度,有许可权的模块会更受到开发人员的重视。需要使用MODULE_LICENSE表示该模块哦的许可权限。内核可识别的许可权限如下:

MODULE_LICENSE("GPL"); //任一版本的GNU公共许可权 
MODULE_LICENSE("GPL v2"); //GPL版本2许可权
MODULE_LICENSE("GPL and additional rights"); //GPL及其附加许可权
MODULE_LICENSE("Dual BSD/GPL");  //BSD/GPL双重许可权
MODULE_LICENSE("Dual MPL/GPL");  //MPL/GPL双重许可权
MODULE_LICENSE("Proprietary");   //专有许可权

第一个驱动模块

开始编写一个最简单的驱动模块

#include <linux/module.h>        
#include <linux/init.h>            //导入需要的头文件

static int hello_init(void)
{
    printk(KERN_ALERT "Hello World\n");   //加载函数打印 Hello World 信息
    return 0;
}

static void hello_exit(void)
{
    printk(KERN_ALERT "Goodbye, World\n"); //卸载函数
}

module_init(hello_init);    //指定模块加载函数
module_exit(hello_exit);    //指定模块卸载函数
MODULE_LICENSE("Dual BSD/GPL"); // 指定许可权为 Dual BSD/GPL

编译Hello World模块

正确编译内核模块,需要满足以下条件:

编译内核模块的条件

  1. 使用正确版本的编译工具、模块工具和其他必要的工具。
  2. 应该有一份内核源码,该源码的版本应该和系统目前 使用的内核版本一致。
  3. 内核源码应该至少编译一次,也就是执行过make命令。

Makefile文件

一个完整的Makefile文件如下:

ifeq ($(KERNELRELEASE),)
    KERNELDIR ?= /linux-2.6.29.4/linux-2.6.29.4
    PWD := $(shell pwd)
modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
else
    obj-m := hello.o
endif

编译模块

使用make命令就可以生成模块文件hello.ko了

模块的操作

命令 操作
insmod 加载模块
rmmod 卸载模块
modprobe 比较高级的加载和卸载模块命令
lsmod 查看已经加载的模块和信息
modinfo 查询模块的相关信息

加载Hello World模块后系统发生的变化

在路径/proc/modules下发生变化,modules文件中会增加如下一行:

cat modules | grep he*

lsmod | grep hello

/proc/devices 文件没有变化,因为hello.ko模块不是一个设备模块
/sys/module/目录会增加hello这个模块的基本信息。
在/sys/module/下会增加一个hello目录。 使用tree -a hello 可以查看以层次结构为组织的内核模块信息属性

模块参数和模块之间通信

可以使用module_param(参数名,参数数据类型,参数读写权限)来为模块定义个参数。

static long a = 1;
static int b = 1;
module_param(a,long,S_IRUGO);
module_param(b,int,S_IRUGO);

参数数据类型可以是byte、short、ushort、int、uint、long、ulong、bool、charp(字符指针类型)。不支持浮点类型,printk()也不支持浮点类型。

导出函数

使用EXPORT_SYMBOL使函数变为导出函数。

将模块加入内核

向内核添加模块

向liunx内核中添加驱动模块,需要完成3个工作:

  1. 编写驱动程序文件
  2. 将驱动程序文件放到linux内核源码的相应目录中,如果没有合适的目录,可以自己建立一个目录存放驱动程序文件。
  3. 在目录的Kconfig文件中添加新驱动程序对于的项目编译选择。
  4. 在目录的Makefile文件中添加新驱动程序的编译语句。

Kconfig

内核源码树的目录下都有两个文件Kconfig和Makefile。分布到各目录的Kconfig文件构成了一个分布式的内核配置数据库,每个Kconfig文件分别描述了所属目录源文档相关的内核配置菜单。在内核配置make menuconfig时,从Kconfig中读出菜单,用户选择后保存到.config这个内核配置文件中,在内核编译时,主目录中的Makefile调用这个.config文件,就知道了用户的选择。

Kconfig语法

主要关键字

config
menuconfig
choice/endchoice
comment
menu/endmenu //以上五项为菜单选项

if/endif //是一个条件选项

菜单入口

config 关键字定义一个新的配置选项,之后定义该配置选项的属性。属性可以有类型、输入提示,依赖关系,帮助信息和默认值等。

每个配置选项都必须指定一种类型,包括bool、tristate、string、hex和int,其中tristate和string是两种基本类型。

bool "set version information on all modules symbols" #定义bool类型菜单提示

依赖关系

depends on <expr>

如果定义了多个依赖关系,那么可以用&&来连接,表示与的关系。

bool "foo" if BAR #如果定义了BAR选项,那么使能foo选项
default y if BAR #如果定义BAR选项,那么foo的默认值为y,表示编译入内核

等价于


depends on BAR
bool "foo"
default y

菜单结构

菜单结构一般作为菜单入口的父菜单。

选择菜单(choice)

选择菜单定义一组选项。此选项的类型只能是boolean 和 tristate型。

"choice"
<choice options>
<choice block>
"endchoice"

在一个硬件有多个驱动的情况下可以使用choice菜单,使用choice 菜单可以实现最终只有一个驱动被编译进内核中,choice菜单可以接受的另一个选项是optional,这样选项就被设置被N,表示没有被选中。

注释菜单(comment)

comment <prompt>
<comment options>
目录
相关文章
|
5天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
1月前
|
Linux Android开发
嵌入式linux中Framebuffer 驱动程序框架分析
嵌入式linux中Framebuffer 驱动程序框架分析
27 0
|
21天前
|
Linux API C语言
FFmpeg开发笔记(一)搭建Linux系统的开发环境
本文指导初学者如何在Linux上搭建FFmpeg开发环境。首先,由于FFmpeg依赖第三方库,可以免去编译源码的复杂过程,直接安装预编译的FFmpeg动态库。推荐网站<https://github.com/BtbN/FFmpeg-Builds/releases>提供适用于不同系统的FFmpeg包。但在安装前,需确保系统有不低于2.22版本的glibc库。详细步骤包括下载glibc-2.23源码,配置、编译和安装。接着,下载Linux版FFmpeg安装包,解压至/usr/local/ffmpeg,并设置环境变量。最后编写和编译简单的C或C++测试程序验证FFmpeg环境是否正确配置。
37 8
FFmpeg开发笔记(一)搭建Linux系统的开发环境
|
1月前
|
存储 缓存 Linux
探秘Linux块设备驱动程序:成为内核开发大师的第一步
探秘Linux块设备驱动程序:成为内核开发大师的第一步
92 0
|
1月前
|
Linux 编译器 测试技术
探索Linux设备树:硬件描述与驱动程序的桥梁
探索Linux设备树:硬件描述与驱动程序的桥梁
67 0
|
1月前
|
存储 缓存 安全
掌握Linux字符设备驱动程序的核心要点
掌握Linux字符设备驱动程序的核心要点
58 0
|
1月前
|
Shell Linux 编译器
Linux Makefile 全面教程:使用 Makefile 进行项目管理和构建
Linux Makefile 全面教程:使用 Makefile 进行项目管理和构建
38 0
|
1月前
|
Linux
linux驱动开发-点亮第一个led灯
linux驱动开发-点亮第一个led灯
23 0
|
1月前
|
网络协议 Java Linux
Java 开发常用的 Linux 命令知识积累
Java 开发常用的 Linux 命令知识积累
38 0
|
1月前
|
Linux C语言 开发者
Linux嵌入式系统之交叉编译中构建交叉编译工具链
Linux嵌入式系统之交叉编译中构建交叉编译工具链
14 0