ARM_Linux驱动开发——字符设备驱动开发(上)

 

目录

一、Linux驱动开发思维

二、Linux驱动开发分类

三、“ ARM_Linux驱动开发——字符设备驱动开发 ”

字符设备驱动简介


前言

在分享Linux驱动开发之前,我想带大家首先回顾一下裸机驱动开发和Linux驱动开发的区别。

1、运行环境和操作系统:

裸机驱动开发:在裸机开发中,通常是针对没有操作系统支持的嵌入式系统或特定的硬件平台开发驱动程序。这些系统通常直接操作硬件资源,没有操作系统的抽象层。

Linux驱动开发:Linux驱动开发则是针对运行Linux操作系统的计算机或嵌入式系统开发。Linux作为一个开源操作系统,提供了广泛的设备支持和抽象接口,开发者可以利用Linux内核提供的接口来开发设备驱动。

2、开发流程和工具链:

裸机驱动开发:开发者需要了解硬件的具体细节,通常使用特定的开发工具链(如ARM Cortex-M系列开发可以使用Keil、IAR等),编写底层的驱动代码,直接操作硬件寄存器和控制器。

Linux驱动开发:需要熟悉Linux内核的架构和API,使用C语言或者特定的Linux设备驱动框架(如Platform Driver、USB Driver等)进行开发。Linux提供了丰富的设备驱动开发文档和示例代码,以及调试工具。

3、硬件抽象层和接口标准化:

裸机驱动开发:由于没有操作系统提供的标准接口,开发者需要自行处理硬件之间的通信和资源管理。每种硬件平台的驱动开发可能具有很高的定制性和特定性。

Linux驱动开发:Linux内核提供了一套标准的设备驱动开发框架和API,开发者可以利用这些框架来开发驱动,这样的驱动通常更具有通用性和可移植性。

4、调试和测试:

裸机驱动开发:调试通常需要通过硬件调试器或者仿真器进行,对于实际硬件的测试需要特定的硬件平台。

Linux驱动开发:Linux内核提供了丰富的调试工具和接口,如printk日志、sysfs接口等,同时也可以使用标准的Linux调试工具(如gdb、strace等)来进行驱动调试和性能分析。

总之,裸机驱动开发更加依赖于具体的硬件平台和细节,开发的驱动程序更加定制化和特定化;而Linux驱动开发则更多依赖于操作系统提供的标准接口和抽象层,开发的驱动程序具有更好的可移植性和通用性。 

一、Linux驱动开发思维

1、首先需要跳出曾经的裸机开发思维,因为Linux下的驱动开发直接操作寄存器已经不现实了;

2、开发者需要根据Linux下的各种驱动框架进行开发。即必须满足驱动开发的框架;

3、驱动最终的执行形式就是:/dev/xxx文件、open、close、write、read……

4、新的驱动支持设备树,这是一个.dts文件,此文件描述的是设备信息。


二、Linux驱动开发分类

Linux驱动开发可以按照不同的分类进行归类,主要包括以下几类:

1、字符设备驱动(Character Device Drivers):

这类驱动程序处理以字符流方式进行数据传输的设备,如串口、终端、声音卡等。通常通过文件操作接口(open, read, write, close)来与用户空间进行通信。

2、块设备驱动(Block Device Drivers):

这类驱动程序用于管理块设备,如硬盘、闪存存储器等,它们以固定大小的块为单位进行数据传输。块设备驱动通常需要实现块设备的缓存、IO调度等功能。

3、网络设备驱动(Network Device Drivers):

网络设备驱动用于控制网络接口卡(NIC),如以太网卡。这类驱动程序处理网络数据包的收发、协议栈的处理等。

4、USB设备驱动(USB Device Drivers):

USB设备驱动用于控制连接到Linux系统的USB设备,如USB存储设备、USB网卡等。这类驱动程序需要处理USB协议栈、设备的插拔事件等。

5、文件系统驱动(Filesystem Drivers):

文件系统驱动用于支持特定类型的文件系统,如ext4、NTFS等。这类驱动程序负责管理存储设备上的文件和目录结构。

6、总线设备驱动(Bus Drivers):

总线设备驱动用于管理系统总线,如PCI总线、SPI总线、I2C总线等。这类驱动程序负责枚举、初始化和管理连接到总线上的设备。

7、平台设备驱动(Platform Drivers):

平台设备驱动用于支持特定硬件平台上的设备,如嵌入式系统中的特定传感器、LED控制器等。

8、虚拟设备驱动(Virtual Device Drivers):

这类驱动程序不与物理硬件设备直接交互,而是创建虚拟设备,如虚拟网卡、虚拟磁盘等。

9、字符驱动和块驱动的子类:

这些驱动可能根据设备的特定需求进一步分类,例如音频设备驱动、输入设备驱动(键盘、鼠标)、显示设备驱动等。

Linux驱动开发涵盖了广泛的设备类型和功能,开发者根据具体的设备类型选择适合的驱动开发模块和接口。

三、“ ARM_Linux驱动开发——字符设备驱动开发 ”


字符设备驱动简介

字符设备就是一个一个字节,按照字节流来进行读写操作的设备,读写数据是分先后顺序的。常见的比如点灯、IIC、ISP……都是字符操作的设备,对于这些设备的驱动就叫做字符设备驱动。

了解字符设备驱动架构之前,我们可以先来看一下Linux下的应用程序是如何调用驱动程序的:

在Linux中,一切皆为文件。要实现对硬件的操作,需要应用程序对名为"/dev/xxx"的文件将进行操作。这个文件是由驱动加载成功后,会在"/dev"目录下生成相应的文件。

举一个简单的例子:如果你要操作点亮一个灯,你就可以使用open函数打开"/dev/Led"文件,使用完成以后用close函数关闭"/dev/Led"这个文件。需要点亮灯的话,则使用函数write函数来操作,即向此驱动写入数据,这个数据就是决定是否打开灯。如果要获取Led灯的状态,则使用read函数来从驱动中读取相应的状态。

应用程序是运行在用户空间,但是Linux驱动属于内核的一部分,所以驱动运行在内核空间。这样的话问题就来了,如果用户要直接对内核进行操作,直接通过用户空间来操作可行吗?

答案肯定是不行的!

用户空间是不能直接对内核进行操作的,他需要找一个中间人,这个中间人就是通过Linux驱动函数。即使用"系统调用"的方法来实现用户空间“侵入”到内核空间,这样才能实现对底层驱动的操作。

上面提到的open、close、write、read等这些驱动操作函数,都是由C库提供的。在Linux中,系统调用作为C库的一部分。

例如:调用open函数的时候流程如下:

(应用程序)step1:应用调用open()函数 ——> (C库)step2:C库中的open()函数 ——> (内核)step3:open()系统调用 ——> (具体驱动)step4: 驱动的open()函数

图片

那问题又来了,应用程序和具体的驱动又是如何联系起来的呢?

通过上面的讲解,我们能初步了解到应用程序使用到的函数在具体驱动程序中都有与之对应的函数, 比如应用程序中调用了 open 这个函数,那么在驱动程序中也得有一个名为 open 的函数。每一个系统调用,在驱动中都有与之对应的一个驱动函数,在 Linux 内核文件 include/linux/fs.h 中有个叫做 file_operations 的结构体,此结构体就是 Linux 内核驱动操作函数集合。

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);int (*iopoll)(struct kiocb *kiocb, bool spin);int (*iterate) (struct file *, struct dir_context *);int (*iterate_shared) (struct file *, struct dir_context *);__poll_t (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);unsigned long mmap_supported_flags;int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, 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 **, void **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMUunsigned (*mmap_capabilities)(struct file *);
#endifssize_t (*copy_file_range)(struct file *, loff_t, struct file *,loff_t, size_t, unsigned int);loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,struct file *file_out, loff_t pos_out,loff_t len, unsigned int remap_flags);int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;

下面我们对内核驱动操作函数进行初步解释:

  1. llseek:

    • 定义:文件定位函数,用于改变文件的读写位置。

    • 参数:struct file * 文件指针,loff_t 偏移量,int 偏移基准。

    • 返回值:loff_t 新的文件位置。

  2. read:

    • 定义:读取文件内容到用户空间。

    • 参数:struct file * 文件指针,char __user * 用户空间缓冲区,size_t 请求读取的字节数,loff_t * 读取位置的指针。

    • 返回值:ssize_t 实际读取的字节数,错误时返回负值。

  3. write:

    • 定义:将用户空间的数据写入文件。

    • 参数:struct file * 文件指针,const char __user * 用户空间缓冲区,size_t 请求写入的字节数,loff_t * 写入位置的指针。

    • 返回值:ssize_t 实际写入的字节数,错误时返回负值。

  4. read_iter:

    • 定义:迭代式地从文件读取数据到用户空间。

    • 参数:struct kiocb * 异步IO控制块,struct iov_iter * 数据迭代器。

    • 返回值:ssize_t 实际读取的字节数。

  5. write_iter:

    • 定义:迭代式地将用户空间的数据写入文件。

    • 参数:struct kiocb * 异步IO控制块,struct iov_iter * 数据迭代器。

    • 返回值:ssize_t 实际写入的字节数。

  6. iopoll:

    • 定义:用于异步IO中的轮询操作。

    • 参数:struct kiocb * 异步IO控制块,bool 是否自旋。

    • 返回值:int 操作结果。

  7. iterate:

    • 定义:在目录中迭代项。

    • 参数:struct file * 目录文件指针,struct dir_context * 目录上下文。

    • 返回值:int 操作结果。

  8. iterate_shared:

    • 定义:在共享目录中迭代项。

    • 参数:struct file * 目录文件指针,struct dir_context * 目录上下文。

    • 返回值:int 操作结果。

  9. poll:

    • 定义:注册文件的poll/epoll事件。

    • 参数:struct file * 文件指针,struct poll_table_struct * poll表。

    • 返回值:__poll_t 事件掩码。

关于字符设备驱动开发步骤,敬请关注下文更新——《ARM_Linux驱动开发——字符设备驱动开发(下)》!

 

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

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

相关文章

Apache Seata 高可用部署实践

本文来自 Apache Seata官方文档,欢迎访问官网,查看更多深度文章。 本文来自 Apache Seata官方文档,欢迎访问官网,查看更多深度文章。 Apache Seata 高可用部署实践 Seata 高可用部署实践 使用配置中心和数据库来实现 Seata 的高…

使用Spring Boot和自定义缓存注解优化应用性能

在现代应用开发中,缓存是提高系统性能和响应速度的关键技术之一。Spring Boot提供了强大的缓存支持,但有时我们需要更灵活的缓存控制。本文将介绍如何使用Spring Boot和自定义缓存注解来优化应用性能。 1. 为什么需要自定义缓存注解? Sprin…

AI算力革命:GPU租赁与算力市场的未来趋势

在数字化和智能化的时代背景下,人工智能(AI)的快速发展已成为全球科技领域的焦点。AI算力作为支撑其发展的关键因素,正以前所未有的速度改变着我们的生活和工作方式。其中,GPU租赁模式的兴起,不仅解决了AI算…

ASP.NET Web应用中的 Razor Pages/MVC/Web API/Blazor

如果希望使用ASP.NET Core创建新的 Web 应用程序,应该选择哪种方法?Razor Pages还是 MVC(模型-视图-控制器),又或者使用Web API Vue/React/......。 每种方法都有各自的优点和缺点。 什么是 MVC? 大多数服…

轻松设置:服务器域名配置全攻略

目录 前置条件 在阅读本篇内容之前,请先确保以下物料已准备好: 一台公网服务器,服务正常运行申请完成的域名,在对应域名服务商后台正常DNS解析域名备案完成可选条件:有https访问请求时,需要申请SSL证书 …

微信小程序的“小鱼在乎”日程计划系统-计算机毕业设计源码51307

摘要 本文介绍了一种基于微信小程序和Spring Boot后端服务的“小鱼在乎”日程计划系统。该系统结合了前端微信小程序的便捷交互与后端Spring Boot框架的稳健性能,为用户提供了一款功能全面、体验出色的日程管理工具。 “小鱼在乎”日程计划系统涵盖了多种功能&#…

DNS正向解析与反向解析实验

正向解析 安装bind软件 [rootlocalhost ~]# dnf install bind bind-utils -y修改主配置文件/etc/named.conf [rootlocalhost ~]# vim /etc/named.conf重启DNS服务(named) [rootlocalhost ~]# systemctl restart named编辑数据配置文件。在/var/named…

Opencv的基本操作(一)图像的读取显示存储及几何图形的绘制

文件的读取、显示、存取 cv2.imread(imagepath,IMREAD.xxx) 读取图像cv2.imshow(窗口名称,mat图片) 显示图像cv2.imwrite(保存的位置,img) 保存图像 # 1. 读取图像 原始图片路径,图片读取模式 cv2.imread(imagepath,IMREAD.xxx)cv2.IMREAD_COLOR 彩色模式读取 cv2…

Gemini for China 大更新,现已上架 Android APP!

官网:https://gemini.fostmar.online/ Android APP:https://gemini.fostmar.online/gemini_1.0.apk 一、Android APP 如果是 Android 设备,则会直接识别到并给下载链接。PC 直接对话即可。 二、聊天记录 现在 Gemini for China&#xff…

【论文笔记】UniST:通用预训练城市时空预测模型

目录 写在前面1. 通用时空模型的挑战与能力特性2. 构建通用时空模型UniST2.1 大规模时空预训练2.2 时空知识规则引导提示学习 3. UniST的实验与分析3.1 模型预测效果3.2其他实验分析 写在前面 文章标题:UniST: A Prompt-Empowered Universal Model for Urban Spati…

每日一题~ leetcode 402 (贪心+单调栈)

click me! 这个贪心的推导在leetcode上已经很明确了。 click me! 删除k个数,可以先考虑删除一个数。这也是一种常见的思路。(如果进行同样的操作多次,可以先只 考虑一次操作如何实现,或者他的影响。完成这一次操作后,…

Rabnud博士加入了一个社交圈。起初他有5个朋友。他注意到他的朋友数量以下面的方式增长。第1周少了1个朋友......

Rabnud博士加入了一个社交圈。起初他有5个朋友。他注意到他的朋友数量以下面的 方式增长。第1周少了1个朋友,剩下的朋友数量翻倍;第2周少了2个朋友,剩下的朋友数量 翻倍。一般而言,第N周少了N个朋友,剩下的朋友数量翻倍…

程序员下班为什么不关电脑?难道在偷偷加班?!

不管是周围的程序员朋友还是网上的很多程序员朋友,在下班后都是习惯不关电脑的,关上显示器,拿上手机,快乐下班! 那么,为什么程序员下班都不关电脑?难道他们在偷偷加班? 其实&#x…

锂电池寿命预测 | Matlab基于改进的遗传算法优化BP神经网络的锂离子电池健康状态SOH估计

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 主要流程如下: 1、首先提取“放电截止电压时间”作为锂电池间接健康因子; 2、然后引入改进的遗传算法对BP神经网络的模型参数进行优化。 3、最后 NASA 卓越预测中心的锂电池数据集 B0005、B0006、B0007对…

昇思25天学习打卡营第11天|MindSpore 助力下的 GPT2:数据集加载处理及模型全攻略

目录 环境配置 数据集下载和获取 数据集拆分 处理数据集 模型构建 ​​​​​​​模型训练 ​​​​​​​模型推理 环境配置 “%%capture captured_output”这一行指令通常旨在捕获后续整个代码块所产生的输出结果。首先,将已预装的 mindspore 库予以卸载。随后&a…

讲个SystemVerilog随机约束小坑

正文 记录个在写SystemVerilog随机约束时遇到的一个小坑,如果没有认真去查看随机结果是否符合预期,还真不容易发现。 为了方便讲述,写了如下示例代码。类cl_a里有个随机变量aa,初始值为222。在module top里对类cl_a例化并进行约…

最近你悟出来什么道理?

点击上方△腾阳 关注 转载请联系授权 大家伙,我是腾阳。 活了近30年的我,终于领悟到,人生的旅途是一场深刻而复杂的自我发现与灵魂成长的壮丽征途。 这不仅仅是对外在世界的探索,更是内心深处的一场革命,是灵魂从懵…

Educational Codeforces Round 167(Div.2) A~D

A.Catch the Coin(思维) 题意: Monocarp 参观了一家有街机柜的复古街机俱乐部。在那里,他对"抓硬币"游戏机产生了好奇。 游戏非常简单。屏幕上的坐标网格是这样的 X X X轴从左到右; Y Y Y轴从下往上&…

小白必看!推荐三本高质量python书籍,让你直接原地起飞

Python是一种多功能语言。它经常用作Web应用程序的脚本语言,嵌入到软件产品中,以及人工智能和系统任务管理。它既简单又强大,非常适合初学者和专业程序员。 python的自学书籍非常多,涉及基础入门、web开发、机器学习、数据分析、…

计网_计算机网络概述

2024.07.03:计算机网络概述 第1节 计算机网络概述 1.1 互连网与互联网1.1.1总结1.1.2 因特网(互联网)发展[自行了解] 1.2 计算机网络组成1.2.1 计算机网络组成方式11.2.2 计算机网络组成方式21.2.3 计算机网络组成方式3 1.3 三种交换方式1.3.1 电路交换(1) 电路交换…