一、内核模块
内核模块是一种可以动态加载到操作系统内核中并扩展其功能的软件。它们允许在运行的操作系统内核中增加新的功能或驱动程序,而无需重新启动计算机。
在linux系统中,驱动程序是各自独立存在的,而且驱动程序中包含一个moudle,比如:led驱动,按键驱动,lcd驱动。各个驱动程序是由应用程序来进行关联的,利于linux系统对硬件的支持和管理。
可以通过lsmod来查看当前系统中module的存在
[root@GEC6818 /dev]#lsmod
buttons_drv 2882 0 - Live 0xbf000000 (O)
二、内核模块设计
一个module的框架:
[1]初始化函数 ---- 根据自己驱动的要求来定义一个初始化---- 比如:LED
static int __init gec6818led_init(void)
{
printk("gec6818_led_init\n"); //相当于应用程序的printf,在驱动程序中,不支持libc
//所有的接口都来看于linux内核的接口:位于include/linux
//初始化的工作
return 0;
}
[2]退出函数
static void __exit gec6818led_exit(void)
{
printk("gec6818led_exit\n");
}
[3] moudle入口和出口
module_init(gec6818led_init); //当用户执行insmod led_drv.ko时,调用module_init,然而,
//gec6818led_init被调用
module_exit(gec6818led_exit); //当用户执行rmmod led_drv时,调用moudle_exit
[4] 关于一个驱动的说明
MODULE_AUTHOR("12580.zhang3");
MODULE_DESCRIPTION("GEC6818 LED Device Driver");
MODULE_LICENSE("GPL");
说明:
1)驱动程序的入口是module_init,应用程序的入口是main
2)驱动程序的出口是module_exit,应用程序没有出口
3)驱动程序的头文件要依赖于linux内核源码,应用程序的头文件依赖于libc,所以编译驱动程序要关联内核的源码
4)驱动程序的编译要用Makefile,应用程序可以用Makefile,也可以用一个命令
三、Makefile文件编译的输出
$ make ---> 执行make命令
make ARCH=arm CROSS_COMPILE=/home/gec/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi- -C /home/gec/6818GEC/kernel M=/mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv modules
make[1]: Entering directory '/home/gec/6818GEC/kernel' --->进入内核
CC [M] /mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv/led_drv.o
Building modules, stage 2.
MODPOST 1 modules
CC /mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv/led_drv.mod.o
LD [M] /mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv/led_drv.ko
make[1]: Leaving directory '/home/gec/6818GEC/kernel' ----> 离开内核
注意事项:
1)驱动程序的代码路径不能有中文或者空格
2)编译驱动程序之前,一定要先编译内核
make ARCH=arm CROSS_COMPILE=/home/gec/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi- -C /home/gec/6818GEC/kernel M=/mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv modules
make[1]: Entering directory '/home/gec/6818GEC/kernel'
ERROR: Kernel configuration is invalid. ---->内核的配置无效
include/generated/autoconf.h or include/config/auto.conf are missing.
Run 'make oldconfig && make prepare' on kernel src to fix it.
WARNING: Symbol version dump /home/gec/6818GEC/kernel/Module.symvers
is missing; modules will have no dependencies and modversions.
CC [M] /mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv/led_drv.o
In file included from <command-line>:0:0:
/home/gec/6818GEC/kernel/include/linux/kconfig.h:4:32: fatal error: generated/autoconf.h: No such file or directory
#include <generated/autoconf.h>
^
compilation terminated.
scripts/Makefile.build:313: recipe for target '/mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv/led_drv.o' failed
make[2]: *** [/mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv/led_drv.o] Error 1
Makefile:1365: recipe for target '_module_/mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv' failed
make[1]: *** [_module_/mnt/hgfs/GZ1853/linux_drvier/4module/code/led_drv] Error 2
make[1]: Leaving directory '/home/gec/6818GEC/kernel'
Makefile:16: recipe for target 'default' failed
make: *** [default] Error 2
四、安装驱动
#insmod led_drv.ko ----> 安装驱动
#lsmod ---- >查看驱动
#rmmod led_drv.ko ----> 删除驱动
问题:
[root@GEC6818 /6818_driver]#insmod led_drv.ko
insmod: can't insert 'led_drv.ko': File exists ----> 当前系统下,已安装了该驱动
解决办法:利用lsmod查看系统已有的驱动,如果有,则先删除驱动,再插入驱动
[root@GEC6818 /6818_driver]#lsmod
led_drv 747 0 - Live 0xbf000000 (O)
[root@GEC6818 /6818_driver]#rmmod led_drv
[ 392.193000] gec6818led_exit
[root@GEC6818 /6818_driver]#lsmod
[root@GEC6818 /6818_driver]#
五、控制打印信息输出优先级
在linux内核中,对于控制台输出有优先级:
//kernel\kernel\printk.c
int console_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL, /* console_loglevel */ 7
DEFAULT_MESSAGE_LOGLEVEL, /* default_message_loglevel */ 7
MINIMUM_CONSOLE_LOGLEVEL, /* minimum_console_loglevel */ 1
DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */ 7
};
/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
以CONFIG_的宏,是需要配置内核时候来进行设置,所有的配置项在.config文件中可以找到,当配置完成后,经过编译内核,会把.config配置文件自动生成一个.h文件。
想要在控制台打印消息,则default_message_loglevel(默认消息日志的等级) > default_console_loglevel(默认控制台日志等级)。数值越小,优先级越高。
控制方法一:
查看当前系统控制台日志等级:/proc/sys/kernel/printk
[root@GEC6818 /6818_driver]#cat /proc/sys/kernel/printk
7 7 1 7
修改当前控制台的日志等级
[root@GEC6818 /]#echo 7 4 1 7 > /proc/sys/kernel/printk ----> 只当前一次有效
把设置的方法写到配置文件中:/etc/profile
echo 7 4 1 7 > /proc/sys/kernel/printk
保存并且退出,重启开发板
控制方法二:修改printk函数打印消息的级别
#define KERN_EMERG "<0>" /* system is unusable*/
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
在驱动程序中,设置printk函数的消息的级别
printk("<4>" "gec6818led_exit\n"); ---> 消息的级别与消息之间用空格隔开
printk(KERN_WARNING "gec6818led_exit\n");
解决办法三:在配置内核中,进行修改
Kernel hacking
----->
CONFIG_DEFAULT_MESSAGE_LOGLEVEL=7
$ make menuconfig ----> 用菜单来进行内核配置
HOSTCC scripts/kconfig/lxdialog/checklist.o
HOSTCC scripts/kconfig/lxdialog/inputbox.o
HOSTCC scripts/kconfig/lxdialog/menubox.o
HOSTCC scripts/kconfig/lxdialog/textbox.o
HOSTCC scripts/kconfig/lxdialog/util.o
HOSTCC scripts/kconfig/lxdialog/yesno.o
HOSTCC scripts/kconfig/mconf.o
HOSTLD scripts/kconfig/mconf
scripts/kconfig/mconf Kconfig
#
# configuration written to .config -----> 把内核的配置写入到.config文件中
#
*** End of the configuration. ----> 退出菜单配置,完成内核配置
*** Execute 'make' to start the build or try 'make help'.
$ cp .config arch/arm/configs/GEC6818_defconfig ---> 把.config配置文件拷回给具体的硬件平台
$ cd ..
$ ./mk -k ---->重新编译内核
重新烧写内核到开发板----- boot.img
六、驱动程序调试
$ file led_drv.ko
led_drv.ko: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), BuildID[sha1]=548395fe828e760c1cb522b67f93da3386efca21, not stripped
$ size led_drv.ko
text data bss dec hex filename
276 360 0 636 27c led_drv.ko
$ modinfo led_drv.ko
filename: /mnt/hgfs/GZ1853/linux_drvier/4module/led_drv/led_drv.ko
license: GPL
description: GEC6818 LED Device Driver
author: 1853.zhang3
depends:
vermagic: 3.4.39-gec SMP preempt mod_unload ARMv7 p2v8
觉得有帮助的话,打赏一下呗。。