【字符设备驱动开发–IMX6ULL】(一)简介

【字符设备驱动开发–IMX6ULL】(一)简介

一、Linux驱动与裸机开发区别

1.裸机驱动开发回顾

​ 1、底层,跟寄存器打交道,有些MCU提供了库。

spi.c:主机驱动(换成任何一个设备之后只需要调用此文件里面的函数即可)

image-20250106215038846

**w25q.c:**设备驱动(根据具体的设备来写的驱动)

image-20250106215223475

2.Linux驱动开发思维

​ 1、Linux下驱动开发直接操作寄存器不现实。

​ 2、根据Linux下的各种驱动框架进行开发。一定要满足框架,也就是Linux下各种驱动框架的掌握。

​ 3、驱动最终表现就是/dev/xxx文件。打开、关闭、读写、。。。

​ 4、现在新的内核支持设备树,这个一个.dts文件,此文件 描述了板子的设备信息。

3.Linux驱动开发分类

​ linux驱动分为三大类:

​ 1、字符设备驱动,最多的。

​ 2、块设备驱动,存储

​ 3、网络设备驱动,

​ 一个设备不说是一定只属于某一个类型。比如USB WIFI,SDIO WIFI,属于网络设备驱动,因为他又有USB和SDIO,因此也属于字符设备驱动。

二、字符设备开发基础

1.应用程序和驱动的交互原理

1.驱动就是获取外设、或者传感器数据,控制外设。数据会提交给应用程序。Linux驱动编译既要编写一个驱动,还要我们编写一个简单的测试应用程序,APP。

单片机下驱动和应用都是放到一个文件里面,也就是杂糅到一起。Linux下驱动和应用是完全分开的。

2.用户空间(用户态)和内核空间(内核态):

Linux操作系统内核和驱动程序运行在内核空间、应用程序运行在用户空间。

3.应用程序想要访问内核资源,怎么办,有三种方法:系统调用、异常(中断)和陷入。

应用程序不会直接调用系统调用,而是通过API函数来间接的调用系统调用,比如POSIX、API和C库等。unix类操作系统中最常用的编程接口就是POSIX。

应用 程序使用open函数 打开一个设备文件。

4.每个系统调用都有一个系统调用号。

系统调用处于内核空间,应用程序无法直接访问,因此需要“陷入“到内核,方法就是软中断。陷入内核以后还要指定系统调用号。

image-20250107164936038

2.字符设备驱动开发流程

1,Linux里面一切皆文件,驱动设备表现就是一个/dev/下的文件,/dev/led。应用程序调用open函数打开设备,比如led。应用程序通过write函数向/dev/led写数据,比如写1表示打开,写0表示关闭。如果要关闭设备那么就是close函数。

2,编写驱动的 时候也需要编写驱动对应的open、close,write函数。字符设备驱动file_operations

1、驱动最终是被应用调用的,在写驱动的时候要考虑应用开发的便利性。

2、驱动是分驱动框架的,要按照驱动框架来编写,对于字符设备驱动来说,重点编写应用程序对应的open、close、read、write等函数。

三、字符设备驱动简介

字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI,LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。

在详细的学习字符设备驱动架构之前,我们先来简单的了解一下 Linux 下的应用程序是如何调用驱动程序的,Linux 应用程序对驱动程序的调用如图所示:

image-20250116163923224

在 Linux 中一切皆为文件,驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应用程序通过对这个名为“/dev/xxx”(xxx 是具体的驱动文件名字)的文件进行相应的操作即可实现对硬件的操作。

比如现在有个叫做/dev/led 的驱动文件,此文件是 led 灯的驱动文件。

  • 1.应用程序使用 open 函数来打开文件/dev/led
  • 2.使用完成以后使用 close 函数关闭/dev/led 这个文件。open和 close 就是打开和关闭 led 驱动的函数,
  • 3.如果要点亮或关闭 led,那么就使用 write 函数来操作,也就是向此驱动写入数据,这个数据就是要关闭还是要打开 led 的控制参数。
  • 4.如果要获取led 灯的状态,就用 read 函数从驱动中读取相应的状态。

应用程序运行在用户空间,而 Linux 驱动属于内核的一部分,因此驱动运行于内核空间。当我们在用户空间想要实现对内核的操作,比如使用 open 函数打开/dev/led 这个驱动,因为用户空间不能直接对内核进行操作,因此必须使用一个叫做“系统调用”的方法来实现从用户空间“陷入”到内核空间,这样才能实现对底层驱动的操作。

open、close、write 和 read 等这些函数是由 C 库提供的,在 Linux 系统中,系统调用作为 C 库的一部分。当我们调用 open 函数的
时候流程如图所示:

image-20250116164450394

其中关于 C 库以及如何通过系统调用“陷入”到内核空间这个我们不用去管,我们重点关注的是应用程序和具体的驱动,应用程序使用到的函数在具体驱动程序中都有与之对应的函数,
比如应用程序中调用了 open 这个函数,那么在驱动程序中也得有一个名为 open 的函数。每一个系统调用,在驱动中都有与之对应的一个驱动函数,在 Linux 内核文件 include/linux/fs.h 中有个叫做 file_operations 的结构体,此结构体就是 Linux 内核驱动操作函数集合,内容如下所示:

一般在1550行左右

struct file_operations {
//owner 拥有该结构体的模块的指针,一般设置为 THIS_MODULE。struct module *owner;
//llseek 函数用于修改文件当前的读写位置。loff_t (*llseek) (struct file *, loff_t, int);
//read 函数用于读取设备文件。ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//write 函数用于向设备文件写入(发送)数据。ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*iterate) (struct file *, struct dir_context *);
//poll 是个轮询函数,用于查询设备是否可以进行非阻塞的读写。unsigned int (*poll) (struct file *, struct poll_table_struct *);
//unlocked_ioctl 函数提供对于设备的控制功能,与应用程序中的 ioctl 函数对应。long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//compat_ioctl 函数与 unlocked_ioctl 函数功能一样,区别在于在 64 位系统上,
//32 位的应用程序调用将会使用此函数。在 32 位的系统上运行 32 位的应用程序调用的是unlocked_ioctl。long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
/*mmap 函数用于将设备的内存映射到进程空间中(也就是用户空间),一般帧缓冲设备会使用此函数,
比如 LCD 驱动的显存,将帧缓冲(LCD 显存)映射到用户空间中以后应用程序就可以直接操作显存了,
这样就不用在用户空间和内核空间之间来回复制。*/int (*mmap) (struct file *, struct vm_area_struct *);
//open 函数用于打开设备文件。int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);
//release 函数用于释放(关闭)设备文件,与应用程序中的 close 函数对应。int (*release) (struct inode *, struct file *);
//fasync 函数用于刷新待处理的数据,用于将缓冲区中的数据刷新到磁盘中。int (*fsync) (struct file *, loff_t, loff_t, int datasync);
//aio_fsync 函数与 fasync 函数的功能类似,只是 aio_fsync 是异步刷新待处理的数据。int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);int (*show_fdinfo)(struct seq_file *m, struct file *f);
};

在字符设备驱动开发中最常用的函数已经标上注释,关于其他的函数大家可以查阅相关文档。

我们在字符设备驱动开发中最主要的工作就是实现上面这些函数,不一定全部都要实现,
但是像 open、release、write、read 等都是需要实现的,当然了,具体需要实现哪些函数还是要看具体的驱动要求。

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

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

相关文章

YOLOv8+ Deepsort+Pyqt5车速检测系统

该系统通过YOLOv8进行高效的目标检测与分割,结合DeepSORT算法完成目标的实时跟踪,并利用GPU加速技术提升处理速度。系统支持模块化设计,可导入其他权重文件以适应不同场景需求,同时提供自定义配置选项,如显示标签和保存…

蓝桥杯嵌入式学习笔记

用博客来记录一下参加蓝桥杯嵌入式第十六届省赛的学习经历 工具环境准备cubemx配置外部高速时钟使能设置串口时钟配置项目配置 keil配置烧录方式注意代码规范头文件配置 模块ledcubemx配置keil代码实现点亮一只灯实现具体操作的灯,以及点亮还是熄灭 按键cubemx配置k…

ARCGIS PRO SDK VB2022 图层要素类类型判断

arcgis pro 常见要素类类型有以下几种: FeatureLayer ——要素图层(矢量数据) RasterLayer ——栅格图层 MapImageLayer ——地图图像图层 VectorTileLayer ——矢量切片图层 SceneLayer …

【hadoop】远程调试环境

根据上一节,我们已经安装完成hadoop伪分布式环境 hadoop集群环境配置_jdk1.8 441-CSDN博客 还没安装的小伙伴可以看看这个帖子 这一节我们要实现使用vscode进行远程连接,并且完成java配置与测试 目录 vscode 配置远程 安装java插件 新建java项目 …

Java版Manus实现来了,Spring AI Alibaba发布开源OpenManus实现

此次官方发布的 Spring AI Alibaba OpenManus 实现,包含完整的多智能体任务规划、思考与执行流程,可以让开发者体验 Java 版本的多智能体效果。它能够根据用户的问题进行分析,操作浏览器,执行代码等来完成复杂任务等。 项目源码及…

【Linux网络与网络编程】02.初识Socket编程

1. 数据传输的目的 前一篇文章中我们讲解了网络传输的流程,那么网络传输的目的是什么呢?难道我们只是将数据从一台主机传输到另一台主机吗? 当然不是的!因为数据是给人用的。比如:聊天是人在聊天,下载是人…

电脑连不上手机热点会出现的小bug

一、问题展示 注意: 不要打开 隐藏热点 否则他就会在电脑上 找不到自己的热点 二、解决办法 把隐藏热点打开即可

CUDA专题3:为什么GPU能改变计算?深度剖析架构、CUDA®与可扩展编程

1. 简介 1.1. 使用 GPU 的优势 图形处理器(GPU)在相近的成本和功耗范围内,能够提供比中央处理器(CPU)更高的指令吞吐量和内存带宽。许多应用程序利用这些优势,在 GPU 上的运行速度远超 CPU(参见《GPU 应用》)。其他计算设备(如 FPGA)虽然能效也很高,但其编程灵活性…

Linux输入系统应用编程

什么是输入系统 Linux 输入系统是处理用户输入设备(如键盘、鼠标、触摸屏、游戏手柄等)的软件架构。在应用编程层面,它提供了与这些输入设备交互的接口。 主要组成部分 输入设备驱动层:直接与硬件交互的驱动程序 输入核心层:内核中的输入子…

StarRocks BE宕机排查

StarRocks BE宕机排查 排查是否OOM dmesg -T|grep -i oom #排查是否oom原因: 2.X版本OOM原因 BE 的配置文件 (be.conf) 中 mem_limit 配置不合理,需要配置mem_limit(机器总内存-其他服务占用内存-1~2g(系统预留)) 比如机器内存40G,上面有…

逻辑回归(Logistic Regression)模型的概率预测函数

以二分类问题为例,常见的损失函数有 负对数似然损失(neg log-likelihood loss),交叉熵损失(cross entropy loss),deviance loss指数损失(exponential loss)。 前三者虽然名字不同,但却具有相同的表达形式。此外,neg …

函数式组件中的渲染函数 JSX

在 Vue.js 和 React 等现代前端框架中,函数式组件已成为一种非常流行的设计模式。函数式组件是一种没有内部状态和生命周期方法的组件,其主要功能是接受 props 并渲染 UI。随着这些框架的演进,渲染函数和 JSX(JavaScript XML&…

Android 动态设置默认Launcher(默认应用 电话-短信-浏览器-主屏幕应用))

Android 动态设置默认Launcher(默认应用 电话-短信-浏览器-主屏幕应用)) 文章目录 场景需求参考资料思路期待效果 实现方案源码流程分析和思路实现DefaultAppActivityHandheldDefaultAppFragmentHandheldDefaultAppPreferenceFragmentDefaultAppChildFragmentDefaul…

Qt下载模板到本地文件内容丢失问题

上源码 关键点已标注在源码中 A, B… // 保存的文件路径后缀QString dateTime Myapp::getCurrentTimeDescYMDHms().replace(" ", "").replace("-", "").replace(":", "");// 临时文件名称QString newFileName Q…

【数学建模】动态规划算法(Dynamic Programming,简称DP)详解与应用

动态规划算法详解与应用 文章目录 动态规划算法详解与应用引言动态规划的基本概念动态规划的设计步骤经典动态规划问题1. 斐波那契数列2. 背包问题3. 最长公共子序列(LCS) 动态规划的优化技巧动态规划的应用领域总结 引言 动态规划(Dynamic Programming,简称DP)是一…

蓝桥杯备考------>双指针(滑动窗口)

来看哈我们这道例题 我们第一种想法应该就是暴力求解,枚举每个子数组 当我们枚举第一个数的时候,我们要从第一个数开始挨个枚举每个结尾 如图,以第一个数开头的最长不重复数我们就枚举完了 然后我们让两个指针全部到第二个数 再枚举第二个…

python实现股票数据可视化

最近在做一个涉及到股票数据清洗及预测的项目,项目中需要用到可视化股票数据这一功能,这里我与大家分享一下股票数据可视化的一些基本方法。 股票数据获取 目前,我已知的使用python来获取股票数据方式有以下三种: 爬虫获取,实现…

【15】Selenium 爬取实战

一、selenium适用场景 二、爬取目标 三、爬取列表页 (1)初始化 (2)加载列表页 (3)解析列表页 (4)main 四、爬取详情页 (1)加载详情页 (2…

如何封装一个上传文件组件

#今天用el-upload感到很多不方便,遂决定自己封装一个。注:本文不提供表面的按钮样式和文件上传成功后的样式,需要自己创建。本文仅介绍逻辑函数# 1,准备几个表面用来指引上传的元素 2,创造统一的隐藏文件上传输入框&…

【计网】数据包

期末复习自用的,处理得比较草率,复习的同学或者想看基础的同学可以看看,大佬的话可以不用浪费时间在我的水文上了 1.数据包的定义: 数据包是网络通信中的基本单元,它包含了通过网络传输的所有必要信息。数据包的结构…