menuconfig、Kconfig、makefile、./config之间的关系
1,流程说明
(1)内核在编译之前,需要进行配置;而配置项则是类似于“make xxx_config”在 kernel文件下arch/arm/configs/xxx_config。
(2)上述为粗略配置,当前目录下会生成 .config(重要文件)
(3)执行make menuconfig 进行精细配置
(4)编译运行
2,menuconfig读取Kconfig文件
(1)menuconfig显示菜单内容,一方面是菜单的目录结构,另一方面是每一个菜单项目的细节。
(2)菜单内容是由内核源码树下各个目录下的Kconfig文件按照一定的格式来支持的。
(3)Kconfig文件中按照一定的格式包含了一个又一个的配置项,每一个配置项在make menuconfig中都会成为一个菜单项目。
(4)而且menuconfig中显示的菜单目录结构和源码目录中的Kconfig的目录结构是一样的。
(5)相应的Kconfig文件中删除一个config项,则再次 make menuconfig 时这个项目已经看不到了。
3,menuconfig读取/写入.config文件
(1)虽然menuconfig菜单项来自于Kconfig,但是每一个菜单的选项结果(Y、N、M)却不是保存在Kconfig中的。
(2)make menuconfig 打开时,会读取 .config 文件;并且用 .config 中的文件配置选择结果来初始化 menuconfig 中各个菜单的选项值。
4,Kconfig和.config文件和Makefile三者的关联
(1)配置项被配置成Y、N、M会影响.config文件中的CONFIG_XXX变量的配置值。
(2)CONFIG_XXX变量的配置值会影响Makefile编译
(3)=y 则会编入,=m 会被单独链接成一个 .ko模块,如果没有则代码不会被编译
总结:
1,menuconfig 菜单项的项目内容从Kconfig文件来
2,menuconfig 菜单项的选择值从.config文件来
编写驱动文件:
1,编写一个空壳驱动,所需函数
(1)模块安装函数 __init 和卸载函数 __exit : #include <linux/init.h>
(2)如何调用上述的注册 : module_init 和 module_exit
(3)register_chrdev(函数参数参考内核源码);向内核使用 file_operations 结构体注册自己的驱动
成功返回0,失败返回其它
major : 设备驱动的号(标志当前设备的编号 正数 1~ 255)
*name : 设备名字 (当前设备驱动的,名字)
*fops : 指向file_operations结构体
(4)unregister_chrdev; 宏在 模块退出时,要记得回收
static inline void unregister_chrdev(unsigned int major, const char *name)
(5)file_operations ; 结构体里封装的是 应用层的函数调用(函数指针)
示例:
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = test_chardev_open, //打开文件
.release = test_chrdev_release, //关闭文件,内核中close就是(release)
};
2, 如何让内核给我们自动分配设备号
register_chrdev;函数注册的时候 major 给的值为0 即可
3,设备文件的创建 (应用层读写的是 安装模块的 设备文件)
(1)查看所安装模块分配的设备号 : cat /proc/devices
(2)mknod /dev/test c 250 0
mknod /dev/xxx c(字符设备文件) 主设备号 次设备号
4,应用和驱动之间的数据交换
PS :应用层的数据不能直接传输到内核驱动当中,内核有提供到函数。主要思想:复制
(1)copy_from_user : 将用户空间数据,传输到内核空间
原型:copy_from_user(void *to, const void __user *from, unsigned long n);
*to : 内核buf空间
*from : 用户buf空间
unsigned long n : 多个个字节
(2)copy_to_user : 将内核空间数据,传输到内核空间
5,简单驱动实例
驱动:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#define TESTNAME "chrdev"
static int test_chardev_open(struct inode *inode, struct file *file);
static int test_chrdev_release(struct inode *inode, struct file *file);
int major; //用来保存自动分配的驱动设备号
//1,自定义一个 file_operations test_fops 结构体变量,自己去填充
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = test_chardev_open, //打开文件
.release = test_chrdev_release, //关闭文件,内核中close就是(release)
};
//2,注册驱动模块
static int __init chrdev_init(void)
{
printk(KERN_INFO"test_chrdev_init is OK!\n");
major = register_chrdev(0,TESTNAME,&test_fops); //注册设备号
if (major < 0)
{
printk(KERN_ERR "register_chrdev fail\n");
return -EINVAL; //内核中的宏,用来知晓出错是怎样的
}
printk(KERN_INFO"register_chrdev success..\n");
return 0;
}
//3,模块卸载
static void __exit chrdev_exit(void)
{
unregister_chrdev( major,TESTNAME);
printk(KERN_INFO"test_chrdev_exit is OK!\n");
}
//4,open函数实现
static int test_chardev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO"test_chardev_open is OK\n");
return 0;
}
//5,close 关闭函数实现
static int test_chrdev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "test_chrdev_release is OK\n");
return 0;
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("gj"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("xxx"); // 描述模块的别名信息
应用 :
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE "/dev/test" // 刚才mknod创建的设备文件名
int main(void)
{
int fd = -1;
fd = open(FILE, O_RDWR);
if (fd < 0)
{
printf("open %s error.\n", FILE);
return -1;
}
printf("open %s success..\n", FILE);
// 读写文件
// 关闭文件
close(fd);
return 0;
}
Makefile :
# 开发板的linux内核的源码树目录
KERN_DIR = /kernel
obj-m += linux.o
all:
make -C $(KERN_DIR) M=`pwd` modules
arm-linux-gnueabi-gcc app.c -o app #应用程序要在开发板上运行,用gcc交叉编译链
cp:
cp *.ko /home/aston/rootfs/rootfs/driver_test
cp app /home/aston/rootfs/rootfs/driver_test
.PHONY: clean
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
现象 :上述准确无误后
(1)lsmod 安装模块和 rmmod 卸载模块,会看到正确打印的信息。
(2)mknod 创建完成设备文件之后,执行 应用程序,也会看到正确的打印信息。
把驱动写进内核
主要修改的点:
(1)对应文件夹下的 Konfig 的更改
(2)对应 Make file 文件的更改
(3)make menuconfig 添加
操作步骤:
(1)将写好的驱动源文件放入内核源码中正确的目录下
(2)在 Makefile中添加相应的依赖
(3)在Kconfig 中添加相应的配置项
(4)make meunconfig
————————————————
版权声明:本文为CSDN博主「噗呲_DayBreak」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_52372485/article/details/121318256