zl程序教程

您现在的位置是:首页 >  其他

当前栏目

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

2023-02-26 12:27:09 时间

构建第一个驱动程序

驱动模块的组成

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

(福利推荐:阿里云、腾讯云、华为云服务器最新限时优惠活动,云服务器1核2G仅88元/年、2核4G仅698元/3年,点击这里立即抢购>>>

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

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 Worldn");   //加载函数打印 Hello World 信息     return 0; }  static void hello_exit(void) {     printk(KERN_ALERT "Goodbye, Worldn"); //卸载函数 }  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> 

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


本站部分内容转载自网络,版权属于原作者所有,如有异议请联系QQ153890879修改或删除,谢谢!
转载请注明原文链接:Linux驱动开发之构建第一个驱动程序

你还在原价购买阿里云、腾讯云、华为云、天翼云产品?那就亏大啦!现在申请成为四大品牌云厂商VIP用户,可以3折优惠价购买云服务器等云产品,并且可享四大云服务商产品终身VIP优惠价,还等什么?赶紧点击下面对应链接免费申请VIP客户吧:

1、点击这里立即申请成为腾讯云VIP客户

2、点击这里立即注册成为天翼云VIP客户

3、点击这里立即申请成为华为云VIP客户

4、点击这里立享阿里云产品终身VIP优惠价

喜欢 (0)
[[email protected]]
分享 (0)