linux 驱动入门

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值