当前位置: 首页 > ds >正文

Linux驱动学习day1

通用的驱动框架

linux下一切皆文件

1、构造file_operations结构体

实现功能APP调用read函数,把驱动中保存的数据放回给APP

APP调用write函数,传入数据保存在驱动中

构造这个结构体,需要什么函数就在里面先写,然后到外面去创建该函数。

static struct file_operations hello_drv = {.owner       = THIS_MODULE,.open        = hello_drv_open,.read        = hello_drv_read,.wirte       = hello_drv_write,.release     = hello_drv_close,
}

2、实现对应的open/read/write函数

ssize_t (*read) (struct file* , char __user* , size_t , loft_t *);ssize_t (*write) (struct file* , const char __user* , size_t , loft_t *);
static ssize_t hello_drv_read (struct file* file, char __user* buf, size_t size, loft_t * offset)
{/*  __user表示其buf来自用户空间,不能直接访问 */printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);/* copy_to_user(void * to , const void * from , unsigned long n); */copy_to_user(buf , kernel_buf , MIN(size , 1024));return MIN(size , 1024);
}static ssize_t hello_drv_write (struct file* file, char __user* buf, size_t size, loft_t * offset)
{/*  __user表示其buf来自用户空间,不能直接访问 */printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);/* copy_from_user(kernel_buf , const void__user * from , unsigned long n); */copy_from_user(kernel_buf , buf , MIN(size , 1024));return MIN(size , 1024);
}static int hello_drv_open (struct inode* node , struct file* file)
{printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);return 0;
}static int hello_drv_close (struct inode* node , struct file* file)
{printk("%s %s %d\n" , __FILE__ , __FUNCTION__ , __LINE__);return 0;
}

3、将file_operations结构体告诉内核(在入口函数)

/* static inline int register_chrdev() */
static int __init hello_init(void)
{major = register_chrdev(0 , "hello_drv" , &hello_drv);/* 这里参数1填0是让驱动找到chardrv数组(理解成数组)中空的地方,并且返回该地方的索引值为主设备号 */return 0;
}

4、unregister(在出口函数)

/* unregister_chrdev() */
static void __exit hello_exit(void)
{unregister_chrdev(major , "hello_drv");
}

hello_drv驱动代码

#include <linux/module.h>     // 最基本模块宏
#include <linux/kernel.h>     // printk
#include <linux/init.h>       // __init/__exit
#include <linux/fs.h>         // register_chrdev 等
#include <linux/uaccess.h>    // copy_to_user, copy_from_user
#include <linux/types.h>      // dev_t, bool 等类型static int major = 0;
static char kernel_buf[1024];
#define MIN(a,b)    (a < b ? a : b)/* funciton */static ssize_t hello_drv_read (struct file *file, char __user *buf , size_t size, loff_t * offset)
{printk(KERN_INFO "[hello_drv] %s:%d\n" , __FUNCTION__ , __LINE__);if(copy_to_user(buf, kernel_buf, MIN(1024 , size)) != 0){return -1;} return MIN(1024 , size);
}static ssize_t hello_drv_write (struct file *file, const char __user *buf , size_t size, loff_t * offset)
{printk(KERN_INFO "[hello_drv] %s:%d\n" , __FUNCTION__ , __LINE__);if(copy_from_user(kernel_buf, buf, MIN(1024,size)) != 0){return -1;}return MIN(1024 , size);
}static int hello_drv_open (struct inode *node, struct file *file)
{printk(KERN_INFO "[hello_drv] %s:%d\n" , __FUNCTION__ , __LINE__);return 0;
}static int hello_drv_close (struct inode *node, struct file *file)
{printk(KERN_INFO "[hello_drv] %s:%d\n" , __FUNCTION__ , __LINE__);return 0;
}/*  create struct file_operations */
static struct file_operations hello_drv = 
{.owner          = THIS_MODULE,.open           = hello_drv_open,.read           = hello_drv_read,.write          = hello_drv_write,.release        = hello_drv_close,
};/* register_chrdev */
static int __init hello_init(void)
{major = register_chrdev(0 , "hello_drv", &hello_drv);return 0;
}/* entry function */
static void __exit  hello_exit(void)
{unregister_chrdev(major, "hello_drv");return;
}module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

编译驱动程序

这里我用了自己的makefile,我用了韦东山老师和原子哥的makefile均编译不了,有的原因是找不到交叉编译器等,或者某个头文件。我显示指定了交叉编译器等。

# 内核源码路径
KERN_DIR := /home/dd/RK3568/SDK/linux/rk3568_linux_sdk/kernel
CURRENT_PATH := $(shell pwd)# 驱动模块目标
obj-m := hello_drv.o# 交叉编译工具链
CROSS_COMPILE ?= /home/dd/RK3568/SDK/linux/rk3568_linux_sdk/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
ARCH ?= arm64# 默认目标:编译模块 + 用户程序
build: kernel_modules hello_test# 编译内核模块
kernel_modules:$(MAKE) -C $(KERN_DIR) M=$(CURRENT_PATH) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules# 编译用户态测试程序
hello_test: hello_test.c$(CROSS_COMPILE)gcc -o hello_test hello_test.c# 清理所有编译产物
clean:$(MAKE) -C $(KERN_DIR) M=$(CURRENT_PATH) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) cleanrm -f hello_test

使用make命令即可生成.ko驱动文件。

hello_test测试文件编写

这里就是之前常使用的标准库的调用,但是打开的是之前创建好的驱动文件。

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>int main(argc , char*argv[])
{int fd = 0;int len = 0;char buf[1024] = {0};if(argc < 2){    printf("Usage:\n");/* <dev> 表示参数必须含有这个设备文件名  , [string] 表示可有也可没有 */printf("%s <dev> [string]\n" , argv[0]);return -1;}/*open*/fd = open(argv[1] , O_RDWR);if(fd < 0){printf("can not open file\n");return -1;}if(argc == 3){    /*write*/len = write(fd , argv[2] , strlen(argv[2]));if(len < 0){printf("write error");return -1;}printf("write len:%d\n" , len);}else{    /*read*/len = read(fd , buf , strlen(buf) - 1);if(len < 0){    printf("read error\n");}printf("read buf:%s\n" , buf);}/*close*/close(fd);return 0;
}

导入板子

使用的是nfs方法导入,首先先创建共享目录 , 然后在Linux 主机端添加环境变量,设置前先要看板子上是否能ping通主机IP

sudo exportfs -ra
/home/dd/nfs_share xx.xx.xx.xxx(rw,wdelay,no_root_squash,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)

xx.xx.xx.xxx是Linux开发板的IP地址 , 前面那个目录是共享目录。 

添加变量之后使用下面命令可查看刚刚添加的变量

sudo exportfs -v

 我的机子需要关闭防火墙,不然板子上的命令一直被PC端拒绝。

sudo ufw disable

设置好上述环境,在板子上输入(开发板要先建好共享目录文件夹)

mount -t nfs -o vers=3,nolock xx.xx.xx.xxx:/home/dd/nfs_share /mnt/driver_project/

执行上述命令,切换到共享目录执行ls即可看到共享文件。

在共享文件中用insmod hello_drv.ko(手动加载内核模块)命令,使用cat /proc/devices 即可看到生成的驱动hello_drv。 236就是主设备号。

然后创建结点mknod /dev/hello c 236 0 。之后就可以运行测试脚本了,读写均没有问题。

总结

阅读驱动源码的工具好难调(一开始时间都比较多花费在配置环境等上面),用vscode+clangd确实是很方便(配置好环境)。驱动学习加油!

http://www.xdnf.cn/news/12717.html

相关文章:

  • Numpy5——数组的扩充(相加、复制、广播)排序,形状调整
  • 计算机图形学:(七)渲染目标
  • MybatisPlus中LambdaQueryWrapper 校验非空,非空判断
  • 解决cesium加载模型太黑暗,程序崩溃,不显示,位置不对模型太大,Cesium加载gltf/glb模型后变暗,变慢问题
  • x32dbg/x64dbg SwissArmyKnife 插件导入map文件不生效
  • 数论——同余问题全家桶2 不定方程和同余方程
  • 高考:如何合理选择学科、专业以及职业
  • 大模型时代的“思考“与“行动“:人工智能的认知革命
  • 智能仓储解决方案:如何为您的企业选择最佳系统 (提升效率 降低成本)
  • 多模态动态图卷积神经网络及Transformer多头注意力机制设计
  • window查看SVN账号密码
  • Blaster - Multiplayer P145-P152: 多种武器
  • 2025年SDK游戏盾实战深度解析:防御T级攻击与AI反作弊的终极方案
  • 大学课程:计算机科学与技术专业主要课程,是否落伍了?
  • ORM框架(SQLAlchemy 与 Tortoise )
  • idea中 maven 本地仓库有jar包,但还是找不到,解决打包失败和无法引用的问题
  • 【iSAQB软件架构】魔法成功软件项目的矩形
  • D1675/HBT191单通道高清视频放大电路解析
  • DAY 23 pipeline管道
  • 10分钟私有部署Deepseek-R1-0518,打造团队专属AI助手
  • 小牛电动NXT,市场销量第一
  • 嵌入式学习笔记- freeRTOS 带FromISR后缀的函数
  • 打卡day46
  • 【leetcode】3. 无重复字符的最长子串
  • 变频串联谐振试验技术解析
  • 【python基础知识】变量名和方法名的单下划线(_)和双下划线(__)总结
  • Unity基础-数学向量
  • 无刷电机的驱动MOSFET
  • Windows安装 cityflow
  • 超声波清洗设备的清洗效果如何?