Rk3568驱动开发_驱动实现流程以及本质_3

1设备号:


cat /proc/devices

编写驱动模块需要要想加载到内核并与设备正常通信,那就需要申请一个设备号,用cat /proc/devices可以查看已经被占用的设备号

设备号有什么用?不同设备其驱动实现不同用设备号去区分,例如字符驱动键盘,块设备驱动固态硬盘,这两种设备不同驱动实现方式不同所以用不同主设备号区分,什么会是从设备号?例如串口,一台电脑接入两根串口,这俩串口用的驱动是一样的,但是要区分不同的串口,就用从设备号区分

2.注册节点:

为什么要注册节点?Linux上一切皆文件,例如你写了串口驱动,你要调用接到你电脑上的串口,串口接到你的电脑上电脑识别后就会创建这样一个节点,供open函数打开串口做后续操作

目前刚学习是手动创建,后面估计会用一些方法自动创建

3.实现流程:

驱动代码:

c 复制代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>


#define CHREDEVBASE_MAJOR 200  // 主设备号
#define CHRDEVNAME  "chrdevbase"  // 名字



static int chrdevbase_open(struct inode* inode, struct file* filp){
    printk("chrdevbase_open\r\n");
    return 0;
}

static int chrdevbase_release(struct inode* inode, struct file* filp){
    printk("chrdevbase_release\r\n");
    return 0;
}

static ssize_t chrdevbase_read(struct file* filp, __user char* buf, size_t count, 
                                loff_t* ppos){
    printk("hrdevbase_read\r\n");
    return 0;                
}

static ssize_t chrdevbase_write(struct file* filp, const char __user *buf, 
                        size_t count, loff_t* ppos){
    printk("chrdevbase_write\r\n");
    return 0;
}


// 字符设备操作集合
static struct file_operations chrdevbase_fops={
    .owner = THIS_MODULE,       // 属于这个驱动模块
    .open = chrdevbase_open,
    .release = chrdevbase_release,
    .read = chrdevbase_read,
    .write = chrdevbase_write,
};



static int __init chrdevbase_init(void){   

    int ret = 0;

    printk("chrdevbase_init\r\n");
    // 注册字符设备
    ret = register_chrdev(CHREDEVBASE_MAJOR, CHRDEVNAME, &chrdevbase_fops);
    
    if(ret < 0){
        printk("chrdevbase init failed\r\n");
        unregister_chrdev(CHREDEVBASE_MAJOR, CHRDEVNAME);
    }

    return 0;
}

static void __exit chrdevbase_exit(void){
    printk("chrdevbase_exit\r\n");
}

// 核心修正点:添加以下声明
MODULE_LICENSE("GPL");          // 必须声明许可证(例如GPL)
MODULE_AUTHOR("Narnat");      // 可选:作者信息
MODULE_DESCRIPTION("chrdevbase"); // 可选:模块描述

// 模块入口与出口
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

makefile:

复制代码
# 设置绝对路径(避免相对路径引发的错误)
KERNELDIR := /home/saisi/RK3568/SDK/linux/rk3568_linux_sdk/kernel
CURRENT_PATH := $(shell pwd)

# 必须明确指定架构和交叉编译器!!!
ARCH := arm64
# 关键修正点:使用ARM官方工具链的正确前缀和路径
TOOLCHAIN_PATH := /usr/local/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu/bin
CROSS_COMPILE := $(TOOLCHAIN_PATH)/aarch64-none-linux-gnu-

# 模块名称
obj-m := chrdevbase.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) ARCH=$(ARCH) clean
	rm -rf app
app:
	/usr/local/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc chrdevbaseApp.c -o app

app代码:

c 复制代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>


int main(int argc, char* argv[]){

    int ret = 0;
    int fd;
    char readbuf[10], writebuf[50];
    
    char* filename;
    filename = argv[1];

    fd = open(filename, O_RDWR);

    if(fd < 0){
      printf("cant open file %s\r\n", filename);
      return -1;
    }

    ret =  read(fd, readbuf, 10);
    if(ret < 0){
      printf("read file %s failed\r\n", filename);
      return -1;
    }

    ret = write(fd, writebuf, 50);
    if(ret < 0){
      printf("write file %s failed!\r\n", filename);
    }

    close(fd);

    return 0;
}

驱动代码中register_chrdev和unregister_chrdev是用于注册字符驱动和注销字符驱动的函数,也就是指明你的驱动类型其中要指定你的主设备号和名字把这些写好后挂载模块后能在系统里找到你注册的信息


lsmod显示的只是你的这个模块名字

注意:注册完节点后,我们的这个驱动会被当做一个文件,就像linux上的摄像头设备一样/dev/video0也是一个文件


用我们写的程序在应用层去打开、读、写的时候本质会调用到我们自己写的驱动

这张图片的含金量还在增加

4.最后总结:

Linux中为何一切皆文件?open函数打开txt文件调用的是读取txt文件的函数,open函数打开串口是调用串口的驱动函数,打开chrdevbase文件就是调用我们自己写的驱动函数,也就是说每一个设备都有其对应的实现驱动模块,open打开他的时候就会去调用这个接口,我们要做的就是实现这些驱动的接口,创建一个节点/dev节点也就是所谓文件,open调用这个文件的时候能正确的调用我们的驱动模块,实现我们的目的

最开始没有学驱动的时候我摸不着头脑,觉得驱动它遥不可及,当我真正下定决心去学的时候当我迈出第一步的时候我发现它并非不可能,无论是学习还是人生,最难的应该是迈出第一步,试试呗

相关推荐
嵌入式郑工3 小时前
RK3566 LubanCat 开发板 USB Gadget 配置完整复盘
linux·驱动开发·ubuntu
雾削木1 天前
树莓派 ESPHome 固件编译与烧录全攻略(解决超时与串口识别问题)
驱动开发
春日见2 天前
win11 分屏设置
java·开发语言·驱动开发·docker·单例模式·计算机外设
DarkAthena2 天前
【GaussDB】手动编译不同python版本的psycopg2驱动以适配airflow
驱动开发·python·gaussdb
松涛和鸣3 天前
DAY66 SPI Driver for ADXL345 Accelerometer
linux·网络·arm开发·数据库·驱动开发
嵌入式郑工3 天前
# RK3576 平台 RTC 时钟调试全过程
linux·驱动开发·ubuntu
GS8FG3 天前
针对Linux,RK3568平台下,I2C驱动的一点小小的领悟
linux·驱动开发
一路往蓝-Anbo3 天前
第 4 篇:策略模式 (Strategy) —— 算法的热插拔艺术
网络·驱动开发·stm32·嵌入式硬件·算法·系统架构·策略模式
A-花开堪折3 天前
RK3568 Android 11 驱动开发(五):串口驱动适配
驱动开发
bandaoyu4 天前
【RDMA】rdma指令
驱动开发