PC操作系统:Ubuntu 16.04 LTS
编译器:arm-poky-linux-gnueabi-gcc 4.9.1
基于i.mx6平台对Linux驱动模块修修改改也有段时间了,其中断断续续查了一大堆资料感觉知识并不那么连贯,是时候自己从基础开始整理整理了。
一、创建一个最小驱动模块模板
创建一个hello.c文件
#include<linux/init.h>
#include<linux/module.h>
/*************************************************************************************************/
// 局部宏定义
/*************************************************************************************************/
#define EN_DEBUG 1 /* 调试信息开关 */
#if EN_DEBUG
#define PRINT(x...) printk(KERN_EMERG x) /* 提高打印等级 */
#else
#define PRINT(x...)
#endif
/**************************************************************************************************
** 函数名称: drv_init
** 功能: 驱动初始化函数,在加载时被调用
** 参数: 无
** 返回: 无
**************************************************************************************************/
static int __init drv_init(void)
{
PRINT("%s ------ \n", __FUNCTION__);
return 0;
}
/**************************************************************************************************
** 函数名称: drv_exit
** 功能描述: i2c驱动退出函数,在卸载时被调用
** 参数: 无
** 返回: 无
**************************************************************************************************/
static void __exit drv_exit(void)
{
PRINT("%s ------ \n", __FUNCTION__);
}
module_init(drv_init); /* 模块初始化 */
module_exit(drv_exit); /* 模块卸载 */
MODULE_AUTHOR("hrx"); /* 模块作者 */
MODULE_DESCRIPTION("Linux Driver"); /* 模块描述 */
MODULE_VERSION("1.0.0"); /* 模块版本 */
MODULE_LICENSE("GPL"); /* 模块遵守的License */
该模块实现的功能
- 加载函数
- 卸载函数
- 作者、描述、版本、许可权限等描述
- 打印调试信息(printk)
打印调试解释
- 用户层使用printf函数
- Linux内核层使用prink函数
- prink中的KERN_EMERG宏是设定输出级别(我选的这个是可以直接输出调试信息的输出级别),更多定义在kernel\include\linux\kern_levels.h文件中可以看到。
- 如果打印级别不高无法输出的话也可以通过dmesg -c命令输出缓存的打印信息(-c是为了输出后清空缓存)。
二、编写Makefile
#当前目录路径
PWD = $(shell pwd)
#Linux内核根目录路径
KERNEL_DIR = $(PWD)/../../kernel
#驱动名称
DRVNAME = hello
#obj-m选项是会以模块进行编译,不会被编译进内核,而是独立生成一个.ko文件
#obj-y选项是会被编译进内核
obj-m += hello.o
build:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD)
clean:
@rm -rf *.o *.ko .*.cmd *.mod.c *.order *.symvers .tmp_versions *~
说明
- KERNEL_DIR为相对于当前Makefile的内核目录路径。
- **$(MAKE)**是make命令的宏定义,其中可能包含其他参数(我这里是只有make)。
- -C这个C是大写的,是为了切换到KERNEL_DIR目录再进行make。
- **M=**是指定Linux驱动源码所在的目录。
三、生成驱动文件(*.ko)
1、配置交叉编译环境
由于我是在开发板上用的,所以要交叉编译。
参考:配置imx6交叉编译环境
2、编译
执行make命令即可生成.ko文件。
make build
四、加载及卸载驱动
首先要先把hello.ko传到开发板上,才能执行后续操作。
1、加载驱动(insmod)
root@imx6qsabresd:/tmp# insmod hello.ko
[ 7707.518557] drv_init ------
这就输出了初始化函数。
2、卸载驱动(rmmod)
root@imx6qsabresd:/tmp# rmmod hello.ko
[ 7843.203066] drv_exit ------
这就输出了退出函数。
五、查看驱动信息
1、列出已加载的驱动模块(lsmod)
root@imx6qsabresd:/tmp# lsmod
Tainted: G
hello 832 0 - Live 0x7f057000 (O)
lsmod其实是读取了**/proc/modules**文件的内容
root@imx6qsabresd:/tmp# cat /proc/modules
hello 832 0 - Live 0x7f057000 (O)
2、驱动加载信息储存位置
驱动被加载后会在 /sys/module/ 目录下生成一个与驱动文件同名的文件夹,里面存放着驱动加载信息。
root@imx6qsabresd:/tmp# ls -l /sys/module/hello/
-r--r--r-- 1 root root 4096 Nov 10 19:40 coresize
drwxr-xr-x 2 root root 0 Nov 10 19:40 holders
-r--r--r-- 1 root root 4096 Nov 10 19:40 initsize
-r--r--r-- 1 root root 4096 Nov 10 19:40 initstate
drwxr-xr-x 2 root root 0 Nov 10 19:40 notes
-r--r--r-- 1 root root 4096 Nov 10 19:40 refcnt
drwxr-xr-x 2 root root 0 Nov 10 19:40 sections
-r--r--r-- 1 root root 4096 Nov 10 19:40 srcversion
-r--r--r-- 1 root root 4096 Nov 10 19:40 taint
--w------- 1 root root 4096 Nov 10 19:35 uevent
3、查看驱动文件信息(modinfo)
- 这个命令直接查询驱动文件,不需要加载。
- 开发板被阉割了这个命令,这是在PC上查看的驱动文件信息。
- 之前在驱动文件里定义的作者、描述、版本、许可权限等描述都可以查得到。
hrx@@@:~/Imx6/platform/driver/hello$ modinfo hello.ko
filename: /home/hrx/Imx6/platform/driver/hello/hello.ko
license: GPL
version: 1.0.0
description: Linux Driver
author: hrx
srcversion: A5F2F4E32D4DF79D5CF15B7
depends:
vermagic: 3.14.38-6UL_ga-svn12810 SMP preempt mod_unload modversions ARMv7 p2v8
参考1:https://blog.csdn.net/leon1741/article/details/78165507