Linux驱动开发笔记

疑问
file_operation中每个操作函数的形参中inode的作用

设备树中compatible属性中厂商和型号如何填写

file_operation定义了Linux内核驱动的所有的操作函数,每个操作函数与一个系统调用对应,对于字符设备来说,常用的函数有:llseekreadwritepool等等,这些操作函数都要需要完成实现。

为实现“高内聚,低耦合”的软件设计理念,Linux驱动采用了内核加载的方式,将驱动编译成模块(Linux 下模块扩展名为.ko),在Linux 内核启动以后使用“insmod”命令加载驱动模块,用“rmmod”命令卸载具体驱动模块,相应地需要注册模块加载函数和模块卸载函数。

module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数

对于字符设备驱动而言,当驱动模块加载成功以后需要注册字符设备,同样,卸载驱动模块的时候也需要注销掉字符设备。输入命令“cat /proc/devices”可以查看当前已经被使用掉的设备号

wangchenxiao@sdc-uvdise057:~$ cat /proc/devices
Character devices:1 mem····
Block devices:2 fd····

Linux 中每个设备都有一个设备号,设备号由主设备号次设备号两部分组成,主设备号表示某一个具体的驱动,次设备号表示使用这个驱动的各个设备,设备号由dev_t数据类型描述,是一个32位的数据类型,中高 12 位为主设备号,其范围为 0~4095,所低 20 位为次设备号
Linux中每个设备号是独一无二的,申请新的设备号不能与已有的设备号重复,为避免冲突,使用alloc_chrdev_regiond unregister_chrdev_region动态申请和注销设备号

1.使用insmodmodprobe加载驱动模块。可以通过lsmod查看当前系统中存在的模块,模块加载后会注册设备,然后可以通过cat /proc/devices查看当前系统中存在的设备。
2.使用mknod创建设备对应的设备节点文件。通过ls /dev查看当前系统中的设备节点文件。设备节点文件是设备在用户空间中的实现,在应用程序中可以通过设备节点文件操作该设备。
3.不再使用该设备后,通过rmmod卸载设备。

MMU(内存管理单元)完成虚拟内存到物理内存的映射和内存保护的功能。可以通过ioremap完成物理内存地址到虚拟内存地址的映射,相应在卸载内存时需要通过unremap释放虚拟内存的映射,在获得指向虚拟内存地址的指针后,可以通过readbreadwreadlwritebwritewwritel对内存进行读写操作

设备树

设备树通过树形数据结构描述板级设备信息,将板级信息从内核中分离出来,提升Linux驱动的灵活性。
通常SOC可以制作多个板子,SOC的信息对于这些板子来说是相同的,因此可以类似于C语言中的头文件,将这些共同的信息提取出来作为一个通用的dtsi文件,其他的dts文件引用该文件,并针对不同的设备进行相应的修改即可。
设备树compatible属性用于设备和驱动的绑定,指定该设备对应的驱动,而驱动模块中的of_device_id列表定义了该驱动对应的设备,设备首先按照先后顺序在内核里查找驱动模块,如果完成匹配则调用驱动的probe函数,完成初始化工作。在根节点中,compatible属性用于指定设备和SOC名称,Linux内核中由DT_MACHINE_START和MACHINE_END所定义的machine_desc 结构体中有个dt_compat 成员变量,其中保存了内核支持的设备,如果设备树中根节点下的compatible属性与dt_compat匹配,则表示Linux内核支持该设备,该开发板可以正常启动Linux内核

pinctrl和gpio子系统

疑问:

用途:在裸机驱动开发中,设备的引脚和gpio通过设置相应的寄存器进行配置,在Linux驱动开发中引入了pinctrl和gpio子系统,通过在设备树中添加节点并完成引脚复用,电气等相应属性的配置,即可在驱动程序中通过提供的API函数完成引脚和gpio驱动的开发工作。

在设备树的外设节点下创建pinctrl节点,例如

pinctrl_led: ledgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */>;
};

fsl,pins属性通过宏定义MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 将GPIO1_IO03复用为GPIO1_IO03,并设置电气属性为0X10B0。

再设备树的根节点下创建gpio对应的外设节点

gpioled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
}

pinctrl-0属性设置了该节点对应的pintrcl节点,led-gpio属性设置了该设备对应的gpio节点信息,通过该属性可以获得GPIO编号

在驱动文件中
在设备结构体中定义GPIO编号
在初始化函数中根据of_find_node_by_path获得设备节点,通过of_get_named_gpio获得设备gpio编号,设置gpio输出输入属性
在写入函数中,通过gpio_set_value设置gpio的输出值。

Linux并发与竞争

原子变量:该变量的赋值和读取为原子操作
自旋锁:加锁后其他线程无法访问临界区,会处于忙等状态,因此适用于临界区耗时不长的场景。
信号量/互斥体:其他线程无法访问临界区时会进入休眠状态,进行上下文切换,适用于临界区较为耗时的场景。
同时只允许一个进程打开设备
通过在设备结构体中设置原子变量,初始化时设置该设备的原子变量为1,打开设备时原子变量减一,并判断是否小于零,若小于零则打开失败,关闭设备时原子变量加一。
在设备结构体中定义整形变量表示该设备是否使用,并定义自旋锁保护该变量,在初始化函数中初始化自旋锁,在打开设备时变量加一,如果变量大于零打开失败,关闭设备时变量减一,注意在对变量进行读写时需要通过自旋锁进行保护。
在设备结构体中定义信号量,在初始化函数中初始化信号量为1,在打开函数中获取信号量,若此时信号量为0则打开失败,在关闭函数中释放信号量。
在设备结构体中定义互斥体,在设备初始化函数中初始化互斥体,在打开设备函数中获取互斥体,如果获取互斥体失败则打开文件失败,在关闭函数中释放互斥体。

Linux内核定时器

Linux通过硬件定时器湖区时钟源,该时钟源的频率被称为系统频率或节拍率,该频率可以在编译LInux内核的时候设置。Linux 内核使用全局变量 jiffies 来记录系统从启动以来的系统节拍数,系统启动的时候会将 jiffies 初始化为 0
在设备结构体中定义timer_list定时器结构体,在初始化函数中初始化定时器,添加超时时间和处理函数,并注册定时器,开始定时功能,在关闭函数中删除定时器。

Linux中断

裸机中断系统?
设备树中通过interrupt-parent指定相应的gpio中断控制器,通过interrupts设置引脚号和中断出发标志。gpio中断控制器节点中,通过interrupts设置该gpio中断源信息
在驱动文件的设备结构体中定义中断IO描述结构体,其中包含gpio、终端号、中断服务函数,名字等信息。通过of_get_named_gpio提取gpio号,然后根据irq_of_parse_and_map从设备树中获取中断号,完成中断处理函数后,通过request_irq完成中断的申请。在关闭函数中,释放中断号

阻塞和非阻塞IO

当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源,那么阻塞式 IO 就会将应用程序对应的线程挂起,直到设备资源可以获取为止。对于非阻塞 IO,应用程序对应的线程不会挂起,它要么一直轮询等待,直到设备资源可以使用,要么就直接放弃,设备驱动文件的默认读取方式就是阻塞式的,若要通过非阻塞方式打开,则需要再open函数中传入O_NONBLOCK

通过等待队列实现阻塞IO
在设备结构体中添加等待队列头结构体

平台设备驱动

在Linux内核中包含了大量驱动代码,因此必须提高驱动的可重用性。
驱动的分离:采用驱动、总线、设备信息模型,将设备信息从设备驱动中剥离开来,驱动使用标准方法去获取到设备信息,后根据获取到的设备信息来初始化设备,即于驱动只负责驱动,设备只负责设备,想办法将两者进行匹配即可。
在这里插入图片描述
驱动的分层:分层的目的也是为了在不同的层处理不同的内容。
定义platform_driver结构体,在其中设置驱动名称name,表示/sys/bus/platform/drivers/目录下的驱动名称,设备树匹配表of_match_table,用于和设备树中的compatible属性匹配,probe函数用于设备和驱动完成匹配时的执行,remove函数。
在初始化函数中通过platform_driver_register注册platform_driver结构体,在卸载函数中通过platform_driver_unregister卸载platform_driver结构体,
在probe函数中完成初始化,在remove函数中完成卸载。

misc驱动

misc为杂项驱动,主设备号为10。misc_register会完成申请设备号、初始化cdev、添加cdev、创建类、创建设备等操作,misc_registe会完成删除cdev、注销设备号、删除设备、删除类等操作

input子系统

Linux为输入设备创建的框架,统分为 input 驱动层、input 核心层、input 事件处理层。
在这里插入图片描述
首先在设备结构体定义一个input_dev指针,然后通过 input_allocate_device函数申请一个input_dev结构体,然后设置该结构体的名称,事件类型和事件值,然后通过input_register_device向Linux内核注册结构体,卸载驱动模块时需要通过input_unregister_deviceinput_free_device注销和删除设备。然后向input_eventLinux上报事件

LCD
驱动文件基本无需更改,可以在设备树中修改相应的参数

I2C驱动

I2C驱动分为总线驱动和设备驱动。I2C总线驱动也被称为I2C控制器驱动或I2C适配器驱动,SOC的I2C总线驱动一般由半导体厂商编写,不需用户编写。I2C设备驱动针对不同的设备进行编写,是I2C驱动的重点。
与platform_driver驱动框架类似,首先需要定义i2c_driver,并定义其中的probe和remove函数,以及of_match_table设备树匹配列表,并完成设备树匹配列表中的compatible属性的定义,在驱动模块初始化函数中通过i2c_add_driver注册i2c_driver,在驱动模块卸载函数中通过i2c_del_driver注销i2c_driver,在probe函数中完成初始化工作,在remove函数中完成卸载工作。
在设备结构体中定义void*类型的private_data类型的指针,在probe函数中指向i2c_client结构体,之后即可通过设备结构体中的private_data指针获取client结构体,client结构体包含了芯片地址和adapter结构体。i2c_client 就是描述设备信息,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_client。Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter,其中包含i2c_algorithm,定义了C 适
配器与 IIC 设备进行通信的方法。i2c_msg定义了I2C通信的消息,其中定义了从机地址、消息数据、消息长度、标志位。
I2C读数据需要定义两个i2c_msg,第一个i2c_msg描述了读取寄存器的首地址,第二个i2c_msg描述了读取寄存器的数据,I2C写数据需要定义一个256长度的u8数组,首先保存寄存器的首地址,然后保存需要发送的数据,最后调用i2c_transfer完成数据的读写操作。

SPI驱动

SPI驱动和I2C驱动类似,分为主机驱动和设备驱动,其中主机驱动一般由SOC厂商编写,因此通常需要实现设备驱动。
首先需要定义SPI驱动结构体spi_driver,定义设备树匹配列表,在其中定义compatible属性,定义probe函数,在其中设置spi_device的mode属性,并通过spi_setup完成设备的初始化工作,定义remove函数,完成设备的卸载工作,在初始化函数中通过spi_register_driver注册spi_driver,在卸载函数中通过spi_unregister_driver注销spi_driver。
spi_transfer定义了SPI传输中的数据,其中tx_buf定义了发送数据,rx_buf定义了接收数据,len定义了长度,然后需要定义spi_message,并通过spi_message_init完成初始化,通过spi_message_add_tail将spi_transfer添加至spi_message中,最后通过spi_sync发送
对于SPI读取数据来说,首先要片选拉低,发送需要读取的寄存器地址,然后读取数据,最后拉高片选;对于SPI写数据来说,首先片选拉低,发送需要读取的寄存器地址,然后发送数据,最后拉高片选。

UART驱动框架

串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,通常由SOC厂商完成编写,只需要在设备树中添加串口节点信息,系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动。

电容触摸屏驱动

电容屏驱动框架
1、电容触摸屏是通过I2C接口与主机通信的,因此需要I2C驱动作为整体框架
2、触摸IC提供中断信号引脚来获得中断触摸信息,因此需要申请中断号,并注册中断处理函数
3、触摸屏幕会产生坐标、按下抬起信息,需要通过input子系统向Linux内核上报信息
4、需要通过MT协议上报触摸屏幕信息,一般来说,对于支持多点电容触摸的屏幕使用Type B类型的MT协议

首先在probe函数中,通过input_mt_init_slots对input_dev进行SLOT的初始化,并指定触摸点的数量。
申请中断号,并在Linux内核中注册中断处理函数。在中断处理函数中通过I2C读取寄存器数据,获得每个触摸点的信息,首先通过input_mt_slot上报触摸点的ID,然后通过input_mt_report_slot_state上报触摸类型,一般是MT_TOOL_FINGER,然后通过input_report_abs上报xy坐标信息。所有触摸点的信息上班完成后,通过input_mt_report_pointer_emulation上报追踪到的触摸点数量是否多余当前上报的触摸点数量,最后由input_sync宣布事件上报结束

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

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

相关文章

Java项目实战-查询用户列表接口服务搭建

概述 这里通过设计一个对用户进行增删改查的接口服务&#xff0c;来练习java项目工程化、Spring框架、Mybatis框架的实际应用 本项目目录 上一节初始化项目&#xff0c;已经controller层了&#xff0c;下方新建包&#xff1a;pojo、mapper、service pojo:所有的实体类都放这…

手机相机系统介绍

目录 一张照片是如何生成的? 相机的成像原理 相机硬件 颜色四要素 相机硬件三大块 模组结构 镜头 镜头光路 镜头常见参数 镜头-FOV&EFL 镜头-焦距 镜头-光圈 图像传感器 图像传感器-像素-底 RGB排布 图像传感器-Pattern & PDAF Sensor CMOS sensor …

Kafka的消息传递保证和一致性

前言 通过前面的文章&#xff0c;相信大家对Kafka有了一定的了解了&#xff0c;那接下来问题就来了&#xff0c;Kafka既然作为一个分布式的消息队列系统&#xff0c;那它会不会出现消息丢失或者重复消费的情况呢&#xff1f;今天咱们就来一探。 实现机制 Kafka采用了一系列机…

怎样找到NPM里面开源库下载地址

场景 最近帮忙找一个开源库地址。这里以vue/language-core为例子。 解决 https://registry.npmmirror.com/vue/language-core/1.8.13这里就是如下格式&#xff1a; https://registry.npmmirror.com/{包名}/{版本号}打开这个页面后&#xff0c;得到开源库下载地址&#xff0c…

Java基于SSM+JSP的服装定制系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 1 简介2 .技术栈3 分析4系统设计4.1 软件功能模块设计4.2.2 物理模型设计 5系统详细设计5.1系统功…

力扣:110. 平衡二叉树(Python3)

题目&#xff1a; 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff…

目前很火的养猫微信小程序源码带流量主+搭建教程

目前很火的养猫微信小程序源码带流量主搭建教程。 搭建教程 进入小程序我们下载开发者工具 开发者工具安装好了 我们就把前端源码导入进开发者工具中 这里的APPID我们填写自己的小程序APPID 修改siteinfo.js里的uniacid和acid 这两个ID在刚才后端添加的小程序那里看 在把…

2023年信创云管平台选哪家?咨询电话多少?

随着云计算和信创国产化的快速发展&#xff0c;越来越多企业需要支持信创系统的云管平台。但很多企业不知道市面上信创云管平台有哪些&#xff0c;也不知道选哪家&#xff1f;这里我们小编就给大家来回答一下。 2023年信创云管平台选哪家&#xff1f;咨询电话多少&#xff1f;…

九日集训 Leetcode 371.两整数之和

给你两个整数 a 和 b &#xff0c;不使用 运算符 和 - &#xff0c;计算并返回两整数之和。 示例 1&#xff1a; 输入&#xff1a;a 1, b 2 输出&#xff1a;3示例 2&#xff1a; 输入&#xff1a;a 2, b 3 输出&#xff1a;5提示&#xff1a; -1000 < a, b < 10…

智能生活从这里开始:数字孪生驱动的社区

数字孪生技术&#xff0c;这个近年来备受瞩目的名词&#xff0c;正迅速渗透到社区发展领域&#xff0c;改变着我们居住的方式、管理的方式以及与周围环境互动的方式。它不仅仅是一种概念&#xff0c;更是一种变革&#xff0c;下面我们将探讨数字孪生技术如何推动社区智能化发展…

力扣-169. 多数元素(C语言+分治递归)

1. 题目 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 2. 输入输出样例 示例1 输入&#xff1a;nums [3,2,3] 输出&#xff…

代码随想录算法训练营第四十九天 | 动态规划 part 10 | 买卖股票的最佳时机i、ii

目录 121. 买卖股票的最佳时机思路代码 122.买卖股票的最佳时机II思路代码 121. 买卖股票的最佳时机 Leetcode 思路 贪心&#xff1a;记录最低值&#xff0c;并且遍历股票逐个寻找股票卖出最大值 动态规划&#xff1a; dp[i][0] 表示第i天持有股票所得最多现金 dp[i][1] 表示…

黎明加水印微信小程序源码 支持流量主接入

黎明加水印微信小程序源码&#xff0c;支持流量主接入。支持从聊天记录选择文件、相机拍摄、直接选择文件 支持白底、黑底的隐形水印&#xff0c;制作后&#xff0c;通过增加蒙版方能看到水印 纯前端&#xff0c;可嵌入任何项目。 部署教程 1、解压后得到项目文件夹 3、把…

什么才是物联网领域最好的开发语言?

什么才是物联网领域最好的开发语言&#xff1f; 最好&#xff01;运行最快&#xff1f;开发最高效&#xff1f;最容易学习&#xff1f; 各有特点&#xff01; 采用C/C语言&#xff0c;运行最快&#xff0c;一般采用厂家提供的底层驱动支持包BSP&#xff0c;所有MCU都支持。如…

华为OD机考算法题:最小数量线段覆盖

目录 题目部分 解读与分析 代码实现 题目部分 题目最小数量线段覆盖难度难题目说明给定坐标轴&#xff08;一维坐标轴&#xff09;上的一组线段&#xff0c;线段的起点和终点均为整数并且长度不小于1&#xff0c;请你从中找到最少数量的线段&#xff0c;这些线段可以覆盖住…

【从0学习Solidity】36. 默克尔树 Merkle Tree

【从0学习Solidity】36. 默克尔树 Merkle Tree 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&#xf…

通俗易懂-OpenCV角点检测算法(Harris、Shi-Tomas算法实现)

目录 1 图像的特征 2&#xff0c;Harris角点检测 2.1 代码实现 2.2结果展示 3&#xff0c;Shi-Tomasi角点检测算法 3.1 &#xff0c; 代码实现 3.2结果展示 1 图像的特征 2&#xff0c;Harris角点检测 、 2.1 代码实现 import cv2 as cv import matplotlib.pyplot as …

QFrame类学习笔记

1、QFrame的作用 QFrame类继承于QWidget类&#xff0c;被QAbstractScrollArea, QLabel, QLCDNumber, QSplitter, QStackedWidget, and QToolBox等类继承。 QFrame作为许多基础控件的基类&#xff0c;提供许多成员方法给子类&#xff0c;实现子类的框架样式的设计。框架样式主要…

基于微信小程序的健身小助手打卡预约教学系统(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;用户的功能设计为&#xff1a;管理员的功能设计为&#xff1a;健身房的功能设计为&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获…

【数据结构】插入排序:直接插入排序、折半插入排序、希尔排序的学习知识总结

目录 1、排序的基本概念 2、直接插入排序 2.1 算法思想 2.2 代码实现 3、折半插入排序 3.1 算法思想 3.2 代码实现 4、希尔排序 4.1 算法思想 4..2 代码实现 1、排序的基本概念 排序是将一组数据按照预定的顺序排列的过程&#xff0c;排序的基本概念包括以下内容…