嵌入式内核及驱动开发之学习笔记(一) 认识与使用驱动模块

举报
王建峰 发表于 2021/11/19 02:50:10 2021/11/19
【摘要】 应用层的进程是通过内核层驱动来访问硬件的,Linux内核源码在每次编译之后生成一个总的镜像,将镜像加载内存中运行并使用(内核在系统运行时会占用内核空间)。驱动属于内核源码的一部分,如果每次修改驱动都要重新编译加载内核的话,这太麻烦了,所以我们通过模块,使驱动可以独立于内核镜像之外,并能动态的加载和卸载。 在搭建好开发环境之后,通过so...

应用层的进程是通过内核层驱动来访问硬件的,Linux内核源码在每次编译之后生成一个总的镜像,将镜像加载内存中运行并使用(内核在系统运行时会占用内核空间)。驱动属于内核源码的一部分,如果每次修改驱动都要重新编译加载内核的话,这太麻烦了,所以我们通过模块,使驱动可以独立于内核镜像之外,并能动态的加载和卸载。

在搭建好开发环境之后,通过source insight我们可以方便的查看和编辑内核源码,并结合交叉编译工具链实现编辑--编译--烧录--执行 的完整开发流程。

  • 物理机(Win7)     程序编辑环境
  • 虚拟机(Ubuntu14.04) 程序的编译环境
  • 开发板(FS4412)  程序的调试运行环境

 

编写驱动

创建驱动文件hello.c,编辑源码


  
  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/stat.h>
  4. static int __init hello_drv_init(void)
  5. {
  6. printk("-------%s-------------\n", __FUNCTION__);
  7. return 0;
  8. }
  9. static void __exit hello_drv_exit(void)
  10. {
  11. printk("-------%s-------------\n", __FUNCTION__);
  12. }
  13. module_init(hello_drv_init);
  14. module_exit(hello_drv_exit);
  15. MODULE_LICENSE("GPL");

这个简单的驱动模块代码,没有涉及到任何硬件,它的功能是可以在加载和卸载该模块时打印输出信息,仅此。要建立这个框架,分成4个步骤:

1.加载头文件 
#include <linux/init.h>                 包含模块装卸载函数原型声明
#include <linux/module.h>               这里没有用到其实
#include <linux/stat.h>                  包含printk函数的原型声明

2.驱动模块装载/卸载函数声明
module_init(hello_drv_init);                          装载 hello_drv_init为回调函数
module_exit(hello_drv_exit);                          卸载 hello_drv_exit为回调函数

3.实现函数入口(回调函数实现)
static int __init hello_drv_init(void)
{undefined
    return 0;
}

static void __exit hello_drv_exit(void)
{undefined
     return ;
}

4.GPL声明
MODULE_LICENSE("GPL");       一种开源声明
 

 

编辑Makefile的规则

用来负责驱动文件的编译和管理。这里make -C参数,借用了内核源码根目录中的Makefile文件的规则来编译驱动代码生成成.ko模块。


  
  1. ROOTFS_DIR = /nfs/rootfs
  2. ifeq ($(KERNELRELEASE), )
  3. #利用当前系统的内核中的Makefile进行编译
  4. #KERNELDIR:= /lib/modules/$(shell uname -r)/build/
  5. #或是指定内核源码的根目录
  6. KERNEL_DIR = /mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412
  7. CUR_DIR = $(shell pwd)
  8. all :
  9. make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
  10. clean :
  11. make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
  12. install:
  13. cp -raf *.ko $(ROOTFS_DIR)/drv_module
  14. else
  15. obj-m += hello.o
  16. endif

 

ROOTFS_DIR变量指定 nfs共享根目录
KERNEL_DIR变量指定 内核源码的根目录(绝对路径)
obj-m += hello.o 表示将hello.o这个目标文件编译成模块

 

编译动作

Ubuntu系统中进入Makefile所在目录


  
  1. linux@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/hello_drv$ make
  2. linux@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/hello_drv$ make install

 

 

加载/卸载模块的指令

在内核运行时

  • insmod xxx.ko   装载
  • rmmod xxx       卸载
  • modinfo         查看

 

其他

对模块的传参

有时候对原厂开发,一些代码叫做固件,我们不能进入函数的内部去修改参数的默认指定。通过命令行传参

  • 手动传递

insmod hello.ko myname="george" myvalue=33

  • 函数接收

 module_param(name, type, perm)

 

符号表

使用 EXPORT_SYMBOL 可以将一个函数以符号的方式导出给其他模块使用。原理类似于应用层的动态库。 这样就使模块与模块之间形成依赖关系(使用方依赖于导出符号的模块),例如

EXPORT_SYMBOL(my_add);   my_add是导出的函数名


  
  1. //math.c
  2. #include <linux/module.h>
  3. #include <linux/init.h>
  4. int my_add(int a, int b)
  5. {
  6. return a+b;
  7. }
  8. EXPORT_SYMBOL(my_add);
  9. MODULE_LICENSE("GPL");

 

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/83994667

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。