Linux驱动开发之构建第一个驱动程序
构建第一个驱动程序
驱动模块的组成
一个驱动模块主要由以下部分组成:、
(福利推荐:阿里云、腾讯云、华为云服务器最新限时优惠活动,云服务器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模块
正确编译内核模块,需要满足以下条件:
编译内核模块的条件
- 使用正确版本的编译工具、模块工具和其他必要的工具。
- 应该有一份内核源码,该源码的版本应该和系统目前 使用的内核版本一致。
- 内核源码应该至少编译一次,也就是执行过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个工作:
- 编写驱动程序文件
- 将驱动程序文件放到linux内核源码的相应目录中,如果没有合适的目录,可以自己建立一个目录存放驱动程序文件。
- 在目录的Kconfig文件中添加新驱动程序对于的项目编译选择。
- 在目录的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>
你还在原价购买阿里云、腾讯云、华为云、天翼云产品?那就亏大啦!现在申请成为四大品牌云厂商VIP用户,可以3折优惠价购买云服务器等云产品,并且可享四大云服务商产品终身VIP优惠价,还等什么?赶紧点击下面对应链接免费申请VIP客户吧:
相关文章
- Goodbye 2020,Welcome 2021 | 沉淀 2021
- Goodbye 2019,Welcome 2020 | 沉淀 2020
- VSTS 执行git pull报错问题修复
- 软件分享 | 第四期 Linux远程连接神器获取和安装
- PHP网页简单编写
- c++使用icu国际化(i18n)
- go-cqhttp(电脑机器人搭建)教程
- Linux|安装nginx改造升级版(Tengine)
- Linux用户管理命令
- Linux文件权限与归属
- Linux文件的特殊权限[SUID&SGID&SBIT]
- 在arm上编译php
- 《安富莱嵌入式周报》第296期:硬件电路实现SPI转以太网,单片机3D游戏图形引擎,Linux基金会年度报告,安捷伦直流电源原理图,KEIL C51更新9.61
- 《安富莱嵌入式周报》第297期:开源生物医学成像系统,可肺部成像,C算法合集500例,突出极致运算速度,数值方法书籍,芯片级激光隔离,3D打印机固件Marlin
- 深入浅出理解Linux thermal governor之IPA
- 基于ArgoCD的GitOps转型实战经验
- GitOps: Kubernetes CI/CD 的缺失环节
- Linux网络编程-TCP客户端如何获取要连接的服务端IP?
- 嵌入式Qt-表格使用测试
- PHP常见的几种数据结构