【Linux】第一个驱动程序,hello world!开启驱动之旅

news2025/1/20 7:10:40

目录

前言: 

 一、背景

二、驱动程序编写流程  

1.APP打开的文件在内核中如何表示?

2.编写驱动程序的流程

三、hello驱动程序实战

hello_drive.c

hello_drive_test.c

最终测试:

a.首先编译内核(如果没有编译过)

b.设置交叉编译工具链

c.编写makefile

d.上机测试

e.最终结果:


前言: 

经典环节:我一直深信,带着问题思考和实践,能够更容易理解并学习到。

(1)最简单的驱动程序是怎么样的?

(2)怎么编写驱动程序?

  • APP打开的文件在内核中如何表示?
  • 理解编写程序的流程
    • 怎么调用到自写的open/read/write函数?
    • 怎么告知内核有一个新的驱动程序?内核通过什么找到驱动程序?
    • 驱动程序的安装和卸载是怎么实现的?

(3)Hello驱动程序实战

写作不易。如果有所帮助,多多支持,欢迎关注,大家共同进步呀!

 一、背景

回顾前文,当我们APP调用glibc中open/read/write函数,会从用户态到内核态,调用相应的sys_open、sys_read等函数访问内核中的文件,打开相应的设备节点,寻找到并启动相应的驱动程序。其中,如何从用户态转到内核态等一些相关知识,参照文章:

【Linux】文件IO---应用开发角度_希希雾里的博客-CSDN博客

最简单的驱动程序是怎么样的?

这里最简单的驱动程序调用方式,驱动程序也提供自己的drv_open、drv_read等函数,一个驱动程序对应一个设备。

二、驱动程序编写流程  

基于上述,完成一个简单基本的驱动程序,我们需要去自己实现open/read/write函数,但完整实现一个驱动程序还会有一些具体问题。

具体问题:

  • APP打开的文件在内核中如何表示?
  • 理解编写程序的流程:
    • 怎么调用到自写的open/read/write函数?
    • 怎么告知内核有一个新的驱动程序?内核通过什么找到驱动程序?
    • 驱动程序的安装和卸载是怎么实现的?

1.APP打开的文件在内核中如何表示?

APP打开文件时,可以得到一个整数(文件句柄),对于APP的每一个文件句柄,在内核里面都会有一个“struct file”与之对应。打开字符设备节点时,内核中也有对应的struct file。

使用open打开文件时,传入的flags、mode等参数会被记录在内核中对应的struct file结构体里(f_flags,f_mode):

去读写文件时,文件的当前偏移地址也会保存在struct file结构体的f_pos成员里。

打开字符设备节点时,内核中也有相对应的struct file。注:成员里的结构体struct file_operations *f_op,这是由驱动程序提供的。

2.编写驱动程序的流程

    • ①确定主设备号,也可以让内核分配
    • ②定义自己的 file_operations 结构体---注:里面存有驱动的open/read/write函数
    • ③实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体
    • ④把 file_operations 结构体告诉内核:register_chrdev
    • ⑤谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数
    • ⑥有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用unregister_chrdev
    • ⑦其他完善:提供设备信息,自动创建设备节点:class_create、device_create

三、hello驱动程序实战

这里初步编写一个驱动程序,这里主体写hello_drive.c驱动程序以及测试程序hello_drive_test.c。

注:驱动程序和应用程序之间数据要使用copy_from_user/copy_to_user函数。

hello_drive.c

/*
	hello_drive.c
*/

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

/*第一步:确定主设备号,也可以让内核分配*/
static int major = 0;

//用于保存应用程序下发的数据,定义一个buffer
static char kernel_buffer[1024];
static struct class* hello_class;
#define MIN(a, b) (a < b ? a : b)


/*第三步:实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体*/
static ssize_t hello_drv_read (struct file * file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_to_user(buf, kernel_buffer, MIN(1024, size));
	return MIN(1024, size);
}
static ssize_t hello_drv_write (struct file * file,const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(kernel_buffer, buf, MIN(1024, size));
	return MIN(1024, size);
}
static int hello_drv_open (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}
static int hello_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


/*第二步:定义自己的 file_operations 结构体---注:里面存有驱动的open/read/write函数*/
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,
};

/*第四步:把 file_operations 结构体告诉内核:register_chrdev*/
/*第五步:定义一个入口函数, 调用register_chrdev*/
static int __init hello_init(void)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "hello", &hello_drv);

	//创建一个类,提供设备信息
	hello_class = class_create(THIS_MODULE, "hello_class");
	err = PTR_ERR(hello_class);
	if (IS_ERR(hello_class)){
		unregister_chrdev(major, "hello");
		return -1;
	}

	device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello");
	return 0;
}


/*第六步:定义一个出口函数,出口函数调用unregister_chrdev*/
static void __exit hello_exit(void)
{
	device_destroy(hello_class, MKDEV(major, 0));
	class_destroy(hello_class);
	major = unregister_chrdev(0, "hello");
}

/*第七步:其他完善:提供设备信息,自动创建设备节点:class_create、device_create*/

//把函数修饰成入口函数和出口函数
module_init(hello_init);
module_exit(hello_exit);

//内核遵循GPL协议,所以我们也要遵循GPL协议,才能够使用内核里的一些函数
MODULE_LICENSE("GPL");

hello_drive_test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/*
 * ./hello_drv_test -w abc
 * ./hello_drv_test -r
 */
int main(int argc, char **argv)
{
	int fd;
	char buf[1024];
	int len;
	
	/* 1. 判断参数 */
	if (argc < 2) 
	{
		printf("Usage: %s -w <string>\n", argv[0]);
		printf("       %s -r\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open("/dev/hello", O_RDWR);
	if (fd == -1)
	{
		printf("can not open file /dev/hello\n");
		return -1;
	}

	/* 3. 写文件或读文件 */
	if ((0 == strcmp(argv[1], "-w")) && (argc == 3))
	{
		len = strlen(argv[2]) + 1;
		len = len < 1024 ? len : 1024;
		write(fd, argv[2], len);
	}
	else
	{
		len = read(fd, buf, 1024);		
		buf[1023] = '\0';
		printf("APP read : %s\n", buf);
	}
	
	close(fd);
	
	return 0;
}

最终测试:

a.首先编译内核(如果没有编译过)

参照这篇文章【Linux】imx6ull开发板第一个驱动实验(led)_希希雾里的博客-CSDN博客

b.设置交叉编译工具链

export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin

c.编写makefile

注意:KERN_DIR是要对应开发板上的内核。

KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88

all:
        make -C $(KERN_DIR) M=`pwd` modules
        $(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c

clean:
        make -C $(KERN_DIR) M=`pwd` modules clean
        rm -rf modules.order
        rm -f hello_drv_test

obj-m   += hello_drv.o

 之后make编译并将.ko文件和hello_drive_test复制到nfs挂载的文件夹下。

cp *.ko hello_drv_test ~/nfs_rootfs/

d.上机测试

mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
//复制到开发板上
cp /mnt/hello_drive.ko ./
cp /mnt/hello_drive_test ./
//安装驱动模块
insmod hello_drive.ko

//查询是否有我们的hello程序
cat /proc/devices
lsmod

//查询是否有我们的设备节点
ls /dev/hello -l
//写入
./hello_drive_test -w hello_world!
//读取
./hello_drive_test -r 

e.最终结果:

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/522955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Makefile 与 docker 进行多服务 一次性构建

本机多服务一次性构建背景 本机开发多个服务,每个服务还会互相调用正常情况,开发者需要在本地启动多个服务,并且手动调用想着不使用gitlab ci/cd, 在本机快速通过 makefiledocker-compose 编排多个服务可执行源码在:https://github.com/webws/go-moda/tree/main/example/traci…

使用Segment Anything(SAM)模型进行自动标注

1.下载项目 项目1&#xff1a;https://github.com/zhouayi/SAM-Tool 项目2&#xff1a;https://github.com/facebookresearch/segment-anything git clone https://github.com/zhouayi/SAM-Tool.gitgit clone https://github.com/facebookresearch/segment-anything.git cd …

Nginx之正向代理与反向代理

1.什么是代理 打工人张三最近换了新工作&#xff0c;原来的住房离公司的路程太远&#xff0c;于是乎想要重新找一个离工作地不那么远的住房&#xff0c;由于工作繁忙&#xff0c;没有时间看房。 房东王五名下有2套住房&#xff0c;一套自己住&#xff0c;另一套想租出去&…

shell脚本----sed命令

文章目录 一、sed的工作流程二、sed的操作三、Sed命令使用3.1打印内容3.2删除行3.3替换3.4插入3.5分组调用 一、sed的工作流程 sed概述 sed编辑器时一种流编辑器&#xff0c;流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。 sed编辑器可以根据命令来处理…

(转载)从0开始学matlab(第3天)—多维数组

正如我们所看到的&#xff0c;MATLAB 的数组可能是一维或多维的。一维的数组可以形象地看作一系列的数垂直地罗列起来&#xff0c;用一个下标就可以调用数组中的元素&#xff08;如图 a&#xff09;。这样的数组适用于一个变量的函数&#xff0c;例如在规定的时间间隔后一系列…

C语言实战 - 贪吃蛇(图像界面)

由于本人精力有限&#xff0c;暂时先把素材和代码放上&#xff0c;等以后有空再补教程。 目录 效果预览 准备工作 EasyX图形库 音频素材 代码编写 Transfer.h文件 game.cpp文件 main.c文件 效果预览 先来看一下最终成品效果 贪吃蛇图形界面 准备工作 EasyX图形库 这…

Stable Diffusion云端部署只需三步, 不吃电脑配置, 模型快速部署

牙叔教程 简单易懂 我是小白, 小白跟我一步一步做就可以了, 鼠标点两下就OK了, 学点新东西, 好吗? 不想学的就走吧, 离我远点. Stable Diffusion是什么 Stable diffusion是一个基于Latent Diffusion Models&#xff08;潜在扩散模型&#xff0c;LDMs&#xff09;的文图生成…

瑞吉外卖 - 后台系统登陆功能(3)

某马瑞吉外卖单体架构项目完整开发文档&#xff0c;基于 Spring Boot 2.7.11 JDK 11。预计 5 月 20 日前更新完成&#xff0c;有需要的胖友记得一键三连&#xff0c;关注主页 “瑞吉外卖” 专栏获取最新文章。 相关资料&#xff1a;https://pan.baidu.com/s/1rO1Vytcp67mcw-PD…

Baumer工业相机堡盟工业相机IO介绍与配置

Baumer工业相机堡盟工业相机IO介绍与配置 Baumer工业相机Baumer工业相机IO的作用Baumer工业相机IO的作用Baumer工业相机IO上点连 Baumer工业相机 Baumer工业相机堡盟相机是一种高性能、高质量的工业相机&#xff0c;可用于各种应用场景&#xff0c;如物体检测、计数和识别、运…

Android基础复习:Service组件详解

Android基础复习&#xff1a;Service组件详解 概况 Service组件是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动&#xff0c;而且即使用户切换到其他应用&#xff0c;服务仍将在后台继续运行。此外&#xff0c;组件可通过绑定到服务与之进…

【动手学深度学习】现代卷积神经网络汇总

文章目录 1 LeNet2 AlexNet3 VGG4 NiN5 GoogLeNet6 ResNet7 DenseNet 本文为作者阅读学习李沐老师《动手学深度学习》一书的阶段性读书总结&#xff0c;原书地址为&#xff1a;Dive into Deep Learning。 1 LeNet 网络结构 实现代码 net nn.Sequential(nn.Conv2d(1, 6, kern…

springboot+vue社区医院管理服务系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的社区医院管理服务系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1…

从C出发 32 --- 自定义数据类型(上)

字节 指的就是 byte , 而一个 byte 占用 8 位&#xff0c; 在 C 语言里面有没有 直接提供 表示 8 位的数据类型&#xff1f; char 最小的整型&#xff0c;就可以表示 8 位的数据类型 char 的取值范围 -128 - 127 一个字节的取值范围是 0 - 25…

RK3308B部署mobilenetv2_ssdlite

目录 1. 在PC端运行mobilenetv2_ssdlite模型1.1 安装NCNN和Opencv1.1.1 安装NCNN1.1.2 安装Opencv 1.2 运行mobilenetv2_ssdlite模型 2. 交叉编译部署到RK3308B板子上并运行模型2.1 交叉编译NCNN和Opencv2.1.1 交叉编译Opencv2.1.2 交叉编译ONNX 2.2 交叉编译mobilenetv2_ssdli…

总结850

学习目标&#xff1a; 月目标&#xff1a;5月&#xff08;张宇强化前10讲&#xff0c;背诵15篇短文&#xff0c;熟词僻义300词基础词&#xff09; 周目标&#xff1a;张宇强化前3讲并完成相应的习题并记录&#xff0c;英语背3篇文章并回诵 每日必复习&#xff08;5分钟&#…

深度学习环境配置系列文章(五):配置Docker深度学习开发环境

深度学习环境配置系列文章目录 第一章 专业名称和配置方案介绍 第二章 Anaconda配置Python和PyTorch 第三章 配置VS Code和Jupyter的Python环境 第四章 配置Windows11和Linux双系统 第五章 配置Docker深度学习开发环境 第五章文章目录 深度学习环境配置系列文章目录前言一, Do…

AI工具分享第二期:11款国内外AI绘画提示词工具整理

工具整理自未来百科AI工具箱&#xff0c;更多提示词工具可自行寻找 Midjourney中文教程 Midjourney 学习导航 PromptHero 描述 通过 DALL-E、Stable Diffusion、Midjourney 等 AI 模型搜索数以百万计的艺术图像… PromptDen AI 在线社区促使爱好者联系、协作和分享想法。 …

支付系统设计三:渠道网关设计07-后置处理

文章目录 前言一、订单数据更新1. 领域模型更新服务工厂2. 聚合创建工厂2.1 数据库更新服务2.2 聚合创建工厂 二、限流渠道入队三、异步通知1. 判断是否需要通知2. 组装异步通知报文3. 获取异步通知协议类型3. 异步通知 总结 前言 本篇将继业务处理之后的后置处理逻辑进行介绍&…

瑞吉外卖 - 后台系统退出功能(4)

某马瑞吉外卖单体架构项目完整开发文档&#xff0c;基于 Spring Boot 2.7.11 JDK 11。预计 5 月 20 日前更新完成&#xff0c;有需要的胖友记得一键三连&#xff0c;关注主页 “瑞吉外卖” 专栏获取最新文章。 相关资料&#xff1a;https://pan.baidu.com/s/1rO1Vytcp67mcw-PD…

瑞吉外卖 - 项目介绍(1)

某马瑞吉外卖单体架构项目完整开发文档&#xff0c;基于 Spring Boot 2.7.11 JDK 11。预计 5 月 20 日前更新完成&#xff0c;有需要的胖友记得一键三连&#xff0c;关注主页 “瑞吉外卖” 专栏获取最新文章。 相关资料&#xff1a;https://pan.baidu.com/s/1rO1Vytcp67mcw-PD…