Linux内核SPI子系统驱动框架详解

news2024/11/18 21:51:11

目录

1 spi子系统整体架构图

2 SPI控制器驱动和SPI设备驱动软件架构

3 SPI控制器驱动的整理流程

4 SPI发送数据过程

5 SPI设备驱动

6spidev万能驱动

7 费曼学习法:我录制了一个SPI子系统驱动框架讲解视频

参考文献:


1 spi子系统整体架构图

如上图所示是我画的一个spi子系统的整体架构图主要分为用户空间,内核空间 和硬件三大部分,

  • 用户空间就是我们的应用程序,
  • 内核空间又分为设备驱动层、SPI核心层和SPI控制器驱动层,其中设备驱动层就是具体的SPI设备驱动,这个一般是由普通的驱动工程师负责,然后SPI核心层是内核自带的代码,SPI核心层起到一个承上启下的作用,往下给控制器驱动层提供控制器驱动的注册函数,往上提供标准的SPI收发API以及设备注册函数。然后是SPI控制器驱动层,SPI控制器驱动一般是由芯片原厂编写。
  • 硬件空间就是具体的硬件,

当我们在应用程序中调用比如write函数的时候,其实调用的就是spi设备驱动注册进去的file_operations结构体里面的write函数,也就是图中的spidev_write函数,这个spidev_write函数进一步调用的就是spi_write函数,这个spi_write函数就就是在SPI核心层定义的,然后spi_write函数进一步调用的就是SPI控制器驱动程序中的spi_sync函数。

2 SPI控制器驱动和SPI设备驱动软件架构

如上图所示是spi控制器和spi设备的软件架构图,左边是SPI控制器驱动软件结构,右边是SPI设备的软件结构,

  • SPI控制器驱动,SPI控制器驱动和SPI控制器设备是挂载到platform_bus_type上的,其中SPI控制器是platform_driver驱动,SPI控制器设备是platform_device类型结构体,内核中的of_platform_default_populate(NULL, NULL, parent);函数会解析设备树中的SPI控制器节点,转换成platform_device结构体,然后会将platform_device增加到内核的设备链表中,platform_driver结构体会被注册到内核的驱动链表中,当增加设备或者驱动的时候,会调用platform_bus_type中的match函数,根据compatible属性进行匹配,当匹配成功后,会调用驱动里面的spi_imx_probe函数,在probe函数里面分配一个spi_master结构体,设置spi_master结构体,调用spi_register_master(spi_master_get(master));注册结构体,然后具体在spi_register_master(spi_master_get(master));函数里面首先会用device_add函数将master->dev注册到内核中,然后还会调用of_register_spi_devices(master);这个函数增加spi_device的,增加spi_device的函数是在register master的时候做的。因为像I2C SPI节点下面的子节点都是由I2C SPI来管理的。然后调用spi_master_initialize_queue(master);,这个函数内部是设置了一些传送函数,后面会重点分析spi_master_initialize_queue(master);。
  • SPI设备驱动,首先of_register_spi_devices(struct spi_master *master)函数会解析设备树中的SPI下面的子节点,将子节点转换为spi_device,然后增加到spi_bus_type,另外spi_driver驱动也会被注册到spi_bus_type中,当增加设备或者驱动时,会调用spi_bus_type中的match函数,根据compatible属性进行匹配,匹配成功后probe函数就会被调用,然后再probe函数里面会分配、设置、注册一个file_operation结构体,在这个结构体中就包含了具体的设备的读写函数。

3 SPI控制器驱动的整理流程

如上图所示是SPI控制器的驱动,当增加设备或者驱动时,会调用platform_bus_type中match函数,然后根据compatible属性进行匹配,匹配成功之后probe函数被调用,然后再probe函数中其实主要就是做了分配、设置、注册一个结构体,具体来看在probe函数中

  • 首先master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data));分配了一个结构体,然后解析设备书中的控制器的相关信息,比如片选,比如寄存器地址,然后把这些东西赋值给master。
  • 然后设置了很多函数,比如spi_imx_setupxfer;函数,这个函数其实就是设置发送接收函数,从上图可以看到这个函数经过一系列的调用,最终调用writel(val, spi_imx->base + MXC_CSPITXDATA); 这就是直接写寄存器的函数了。
  • 还有比如spi_imx_transfer;;函数,这个函数其实最终调用的就是上面spi_imx_setupxfer设置的发送接收函数。
  • 然后调用rspi_bitbang_start(&spi_imx->bitbang);在这个函数里面首先也是设置了一些函数,比如spi_bitbang_transfer_one;,其实这个函数最终的调用的就是前面spi_imx_setupxfer设置的函数,然后在rspi_bitbang_start(&spi_imx->bitbang);函数里面又调用了spi_register_master(spi_master_get(master));函数,在spi_register_master(spi_master_get(master));函数中主要又调用了device_add函数将设备增加到内核中,然后调用了spi_master_initialize_queue(master);接下来专门用一张图重点看一下spi_master_initialize_queue(master);做了什么。

spi_register_master(spi_master_get(master));函数中主要调用了device_add函数将设备增加到内核中,然后调用了spi_master_initialize_queue(master);接下来重点看一下spi_master_initialize_queue(master)做了什么。

  • 在spi_master_initialize_queue(master)里面首先是赋值了master->transfer = spi_queued_transfer这个函数,spi_queued_transfer里面其实是一个传送函数,里面首先会把消息添加到queue的队列,然后检查是否需要启动消息处理:如果master->busy为假且need_pump为真,那么使用kthread_queue_work将master->pump_messages添加到工作队列,以便在稍后的某个时间点处理。
  • 然后master->transfer_one_message = spi_transfer_one_message,这里是设置了spi_transfer_one_message函数,这个函数进一步看其实就是master->transfer_one(master, msg->spi, xfer);,那也就是上一张图中的master->transfer_one = spi_bitbang_transfer_one;
  • 然后spi_init_queue(master);函数,  spi_init_queue函数先初始化kthread_worker,为kthread_worker创建一个内核线程来处理work,随后初始化kthread_work,设置work执行函数,work执行函数为spi_pump_messages。
  • 然后是spi_start_queue(master)函数,spi_start_queue就相对简单了,只是唤醒该工作线程而已;自此,队列化的相关工作已经完成,系统等待message请求被发起,然后在工作线程中处理message的传送工作。

4 SPI发送数据过程

我们发送数据的时候,最小单位是spi_transfer,我们将发送数据buf赋值给spi_transfer,然后spi_transfer又会被放到spi_message里面,然后调用__spi_sync函数进行发送,然后在__spi_sync函数里面,

  • 设置spi_message完成后回调函数,回调函数中仅仅调用complete函数,唤醒等待唤醒的线程。意思就是,当发送spi_message的工作线程完成发送后,唤醒在等待的spi发送完成的spi_sync函数所在的线程。
  • 调用__spi_queued_transfer(spi, message, false)函数,这个函数里面就是将数据添加到发送队列中,检查是否需要启动消息处理:如果master->busy为假且need_pump为真,那么使用kthread_queue_work将master->pump_messages添加到工作队列,以便在稍后的某个时间点处理。
  • __spi_pump_messages(master, false);函数,__spi_pump_messages(master, false),false意思是标记不在工作线程中执行该函数。调用该函数,把spi_message发出去,该函数中会判断当前状态,如果可以直接发,则直接在当前线程中发送,如果不能直接发,则唤醒工作线程,稍后发送。
  • wait_for_completion(&done);,睡眠等待发送完成函数释放的完成信号量,当接收到信号量时,证明发送完成唤醒并结束spi_sync函数。

5 SPI设备驱动

接下来看一下spi设备驱动,首先在前面spi控制器驱动中已经解析了spi子节点的内容转换成了spi_device了,然后我们这里编写spi设备驱动,然后当增加设备或者驱动的时候,会调用spi_bus_type里面的match函数,根据compatible属性进行匹配,匹配成功之后会得调用probe函数,在probe函数里面会注册字符设备,然后字符设备里面就有相应的write函数。

6spidev万能驱动

前面的方式,我们如果想操作一个spi外设,那么由驱动开发人员编写一个spi设备驱动,然后应用开发人员直接使用open/read/write就可以操作相应的spi设备,应用开发人员不需要知道spi设备的具体配置,就把spi设备当成一个文件进行处理。

但是在spidev.c里面的万能驱动采用的是另一种方式,所谓万能驱动,就是我们不需要编写spi_driver,而是在应用程序里面直接操作硬件,这种情况下要求应用编写人员需要去阅读芯片手册,需要知道怎么操作具体的spi外设。

7 费曼学习法:我录制了一个SPI子系统驱动框架讲解视频

 根据费曼学习法,把知识点讲出来能够加深对知识点的理解,于是我录制了一个spi子系统驱动框架的讲解视频。

参考文献:

正点原子驱动开发手册

韦东山老师驱动开发大全学习视频

Linux4.9.88内核源码

Linux SPI驱动框架(1)——核心层_linux spi驱动模型_绍兴小贵宁的博客-CSDN博客

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

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

相关文章

资管巨头贝莱德增持矿企股份,机构资金正在慢慢进入比特币经济……

在比特币减半前夕,比特币挖矿企业股价今年大幅上涨,甚至比BTC上涨幅度还高数倍,非常强势。而据富途最新股权披露数据显示,截至2023年8月31日,已经有3家机构投资者增持了加密挖矿服务商比特小鹿(Bitdeer&…

[面试] k8s面试题 2

文章目录 核心组件1.什么是 Kubernetes 中的控制器(Controller)?请提供一些常见的控制器类型。2.请解释一下 Kubernetes 中的 Ingress 是什么,以及它的作用。3.如何通过命令行在 Kubernetes 中创建一个 Pod?4.Stateful…

Jmeter配置性能监控插件

一、版本不兼容时,有报错 1、当jmeter版本比较高时,只需要从官网安装jmeter-plugins-manager-1.10.jar一个包 2、当jmeter版本较低时,安装JMeterPlugins-Extras-1.4.0.zip、JMeterPlugins-Standard-1.4.0.zip内两个jar包 3、服务器上传文件…

【链表】删除链表的中间节点-力扣2095题

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

Latex Overleaf 写作问题记录

Latex & Overleaf 写作问题记录 公式换行及排列整齐 \begin{equation} \begin{split}Y & a1\\&b2 \end{split} \end{equation}顶格 \noindent求和符号 求和符号(上下限上下排列) \sum\limlits求和符号(上下限右边排列&#…

数据库模块

这里写目录标题 一.数据库设计确定实体之间的关系创建数据表编写实体类二.封装数据库操作封装DButil针对文件的增删查改操作进行一个封装初始化数据库插入文件查询文件删除文件 一.数据库设计 确定实体之间的关系 因为我们要做的是一个文件搜索功能,我们这里的实体,就是文件,…

02-数据结构-线性表

线性表的特点: (1)存在惟一一个被称为"第一个"的数据元素 (2)存在惟一一个被称为"最后一个"的数据元素 (3)除第一个之外,集合中每一个数据元素均只有一个前驱 (4)除最后一个之外,集合中每个数据均只有一个后继 线性表是…

2023华为杯数学建模研赛思路分享——最全版本A题深度解析

问题回顾: WLAN网络信道接入机制建模 1. 背景 无线局域网(WLAN, wireless local area network)也即Wi-Fi广泛使用,提供低成本、高吞吐和便利的无线通信服务。基本服务集(BSS, basic service set)是WLAN的…

力扣:106. 从中序与后序遍历序列构造二叉树(Python3)

题目: 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可…

深入学习 Redis - 如何使用 Redis 作缓存?缓存更新策略?使用需要注意哪些问题(工作/重点)

目录 一、Redis 作为缓存 1.1、缓存的基本概念 1.1.1、理解 1.1.2、缓存存什么样的数据?二八定律 1.2、如何使用 redis 作为缓存 1.3、缓存更新策略(redis 内存淘汰机制 / 重点) 1.3.1、定期生成 1.3.2、实时生成 内存淘汰策略&#…

报式套接字通讯实例

报式套接字通讯实例 使用套接字通讯流程 被动端(先运行) 1、取得SOCKET 2、给SOCKET取得地址 3、收/发消息 4、关闭SOCKET 主动端 1、取得SOCKET 2、给SOCKET取得地址(可省略) 3、发/收消息 4、关闭SOCKET 各部分代码实现 pr…

exe文件运行后无输出直接闪退如何找解决办法

一.搜索栏搜事件查看器 二.点开windows日志下的应用程序 三.找到错误处 四.搜索异常代码 点开有错误的详细信息,直接用搜索引擎搜索这个异常代码能大致判断是什么问题,给了一个解决思路,不至于不知道到底哪里出了问题

prometheus+node+process-exporter+grafans

安装Prometheus 要在Ubuntu 18.04上安装Prometheus,您可以按照以下步骤进行: sudo apt-get update安装依赖: sudo apt-get install wget tar下载最新的Prometheus版本: wget https://github.com/prometheus/prometheus/releas…

月木学途开发 3.博客模块开发

概述 效果展示 数据库设计 专栏表 DROP TABLE IF EXISTS blog_column; CREATE TABLE blog_column (blogColumnId int(11) NOT NULL AUTO_INCREMENT,blogColumnName varchar(255) DEFAULT NULL,blogColumnCoverImg longtext,blogColumnIntroduction longtext,userId int(11) D…

LeetCode算法心得——连续的子数组和(前缀和+HashMap)

大家好,我是晴天学长,同余定理的应用加上hashmap的灵活应用,需要的小伙伴可以关注支持一下哦!后续会继续更新的。 1) .连续的子数组的和 题目描述: 给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断…

【 2023华为杯C题】大规模创新类竞赛评审方案研究(思路、代码......)

目录 1 题目概述 2 问题 3 极差的定义及标准分的计算方法 4 题目及数据下载 5 思路、代码下载...... 1 题目概述 现在创新类竞赛很多,其中规模较大的竞赛,一般采用两阶段(网评、现场评审)或三阶段(网评、现场评审…

pcl--第十节 点云曲面重建

曲面重建技术在逆向工程、数据可视化、机器视觉、虚拟现实、医疗技术等领域中得到了广泛的应用 。 例如,在汽车、航空等工业领域中,复杂外形产品的设计仍需要根据手工模型,采用逆向工程的手段建立产品的数字化模型,根据测量数据建…

[React] 自定义hooks设计模式

文章目录 1.自定义hooks设计1.1 自定义hooks1.2 设计一个自定义hooks1.3 自定义hooks的驱动条件1.4 自定义hooks的通用模式1.5 自定义hooks的条件限定 1.自定义hooks设计 react-hooks是react16.8以后,react新增的钩子API,目的是增加代码的可复用性&…

线性表应用(非递减合并、分解链表、删除线性表)

将两个非递减的有序链表合并为一个非递增的有序链表。要求结果链表仍使用原来两个链表的存储空间&#xff0c;不另外占用其它的存储空间。表中允许有重复的数据。 #include<iostream> using namespace std; typedef struct list {int data;list* next; }list,*linklist;…

Linux 系统移植(一)-- 系统组成

参考资料&#xff1a; linux系统移植篇&#xff08;一&#xff09;—— linux系统组成【野火Linux移植篇】1-uboot初识与编译/烧录步骤 文章目录 一、linux系统组成二、Uboot三、Linux内核四、设备树 本篇为Linux系统移植系列的第一篇文章&#xff0c;介绍了一个完整可运行的L…