Linux驱动学习笔记

驱动学习笔记

1、字符设备驱动

Linux 驱动有两种运行方式

第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启 动的时候就会自动运行驱动程序。

第二种就是将驱动编译成模块(Linux 下模块扩展名为.ko),在 Linux 内核启动以后使用“insmod”命令加载驱动模块。

 

2、Linux 并发与竞争

3、Linux 内核定时器

高节拍率会提高系统时间精度,如果采用 100Hz 的节拍率,时间精度就是 10ms

高节拍率会导致中断的产生更加频繁,频繁的中断会加剧系统的负担。但是现在的处理器性能都很强大,所以采用 1000Hz 的系统节拍率并不会增加太大的负载压力。

  • init_timer
  • add_timer 向内核注册定时器以后, 定时器就会开始运行
  • del_timer
  • del_timer_sync 会等待其他处理器使用完定时器再删除
  • mod_timer 修改定时值,如果定时器还没有激活的话,会激活

4、中断上下部

上半部与下半部

上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。

下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。

使用参考:

①、如果要处理的内容不希望被其他中断打断,那么可以放到上半部。

②、如果要处理的任务对时间敏感,可以放到上半部。

③、如果要处理的任务与硬件有关,可以放到上半部

④、除了上述三点以外的其他任务,优先考虑放到下半部。

上半部机制

request_irq 申请中断的时候注册的中断服务函数属于中断处理的上半部

  • request_irq 可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用
  • free_irq
  • 中断处理函数 irqreturn_t (*irq_handler_t) (int, void *)
  • 中断使能与禁止函数 enable_irq disable_irq
    • disable_irq_nosync 函数调用以后立即返回,不会等待当前中断处理程序执行完毕
    • local_irq_enable 用于使能当前处理器中断系统,local_irq_disable 用于禁止当前处理器中断系统

下半部机制

获取中断号和获取字符设备号的函数不一样!!!

5、阻塞和非阻塞 IO

阻塞式 IO 就会将应用程序对应的线程挂起(休眠),直到设备资源可以获取为止。

非阻塞 IO,应用程序对应的线程不会挂起,它要么一直轮询等待,直到设备资源可以使用,要么就直接放弃。

 

6、异步通知

7、platform 设备驱动

驱动分离与分层

8、MISC 驱动

  • misc 的意思是混合、杂项的。当我们板子上的某 些外设无法进行分类的时候就可以使用 MISC 驱动
  • MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 platform 总线驱动中,实现复杂的驱动
  • 所有的 MISC 设备驱动的主设备号都为 10

9、INPUT 子系统

输入设备本质上还是字符设备,只是在此基础上套上了 input 框架,用户只需要负责上报输入事件,比如按键值、坐标等信息,input 核心层负责处理这些事件

驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。

核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。

事件层:主要和用户空间进行交互。

代码编写

  1. 使用 input_allocate_device 函数申请一个 input_dev。
  2. 初始化 input_dev 的事件类型以及事件值。
  3. 使用 input_register_device 函数向 Linux 系统注册前面初始化好的 input_dev。
  4. 卸载input驱动的时候需要先使用input_unregister_device函数注销掉注册的input_dev, 然后使用 input_free_device 函数释放掉前面申请的 input_dev。
  5. 上报输入事件 input_event

10、LCD驱动

Linux 内核将所有的 Framebuffer 抽象为一个叫做 fb_info 的结构体,fb_info 结构体包含了 Framebuffer 设备的完整属性和操作集合,因此每一个 Framebuffer 设备都必须有一个 fb_info

换言之就是,LCD 的驱动就是构建 fb_info,并且向系统注册 fb_info的过程

11、RTC驱动

RTC 设备驱动是一个标准的字符设备驱动

Linux 内核将 RTC 设备抽象为 rtc_device 结构体,因此 RTC 设备驱动就是申请并初始化rtc_device,准备好RTC设备驱动函数集 rtc_class_ops 。最后将 rtc_device 注册到 Linux 内核里面

12、I2C 驱动

I2C 总线驱动

与platform不同的是,platform 是虚拟出来的一条总线,目的是为了实现总线、设备、驱动框架。对于 I2C 而言,不需要虚拟出一条总线,直接使用 I2C 总线即可。

  • I2C 总线驱动,或者说 I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数
  • 完成以后通过 i2c_add_numbered_adapter 或 i2c_add_adapter 这两个函数向系统注册设置好的 i2c_adapter

一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的。因此 I2C 总线驱动对我们这些 SOC 使用者来说是被屏蔽掉的,我们只要专注于 I2C 设备驱动即可

I2C 设备驱动

i2c_client 就是描述设备信息的,i2c_driver 描述驱动内容,类似于 platform_driver。

  • 一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个 i2c_client。
  • 重点工作就是构建 i2c_driver,构建完成以后需要向 Linux 内核注册这个 i2c_driver。i2c_driver 注册函数为 int i2c_register_driver
  • I2C 设备和驱动的匹配过程是由 I2C 核心来完成的

13、SPI 驱动

SPI 驱动框架和 I2C 很类似,都分为主机控制器驱动和设备驱动

主机控制器驱动

transfer 函数,和 i2c_algorithm 中的 master_xfer 函数一样,控制器数据传输函数。

SPI 主机驱动的核心就是申请 spi_master,然后初始化 spi_master,最后向 Linux 内核注册spi_master。

和 I2C 适配器驱动一样,SPI 主机驱动一般都是 SOC 厂商去编写的,所以我们作为 SOC 的使用者,这一部分的驱动就不用操心了

SPI 设备驱动

  • 我们在编写 SPI 设备驱动的时候需要实现 spi_driver
  • spi_driver 和 i2c_driver、platform_driver 基本一样,当 SPI 设备和驱动匹配成功以后 probe 函数就会执行。
  • spi_driver 初始化完成以后需要向 Linux 内核注册,spi_driver 注册函数为 spi_register_driver

14、RS232/485/GPS 驱动

串口分为 TTL 和 RS232。不管是什么样的接口电平,其驱动程序都是一样的

串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也已经由 NXP 官方已经编写好了,我们真正要做的就是在设备树中添加所要使用的串口节点信息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成 /dev/ttymxcX(X=0….n)文件。

15、电容触摸屏驱动

  

老版本的 linux 内核是不支持多点电容触摸的(Multi-touch,简称 MT)。

TypeA时序

TypeB时序

使用“devm_”前缀的函数申请到的资源可以由系统自动释放,不需要我们手动处理。

16、音频驱动

音频编解码芯片,英文名字就是 Audio CODEC

采样率和采样位数就是衡量一款音频 CODEC 最重要的指标

I2S 总线接口

  • I2S 接口需要 3 根信号线(如果需要实现收和发,那么就要 4 根信号线,收和发分别使用一根信号线)

随着技术的发展,在统一的 I2S 接口下,出现了不同的数据格式,根据 DATA 数据相对于 LRCK 和 SCLK 位置的不同,出现了 LeftJustified(左对齐)和 RightJustified(右对齐)两种格式

17、CAN 驱动

CAN 的全称为 Controller Area Network,也就是控制局域网络

  1. 每个单元都是独立的 CAN 节点

同一个 CAN 网络中所有单元的通信速度必须一致

1.CAN 的特点主要有一下几点:

①、多主控制: 根据标识符(Identifier 以下称为 ID)决定优先级,仲裁失利的单元则立刻停止发送而进行接收工作

②、系统的柔软性: 没有类似于“地址”的信息。因此增加单元时其他单元不需要改变
③、通信速度快,距离远
④、具有错误检测、错误通知和错误恢复功能
⑤、故障封闭功能
⑥、连接节点多

2.CAN 电气属性

CAN 总线上没有节点传输数据的时候一直处于隐性状态

3.CAN协议

CAN 协议提供了 5 种帧格式来传输数据

18、USB驱动

1.usb接口类型

USB A

方口usb B

mini usb

micro usb

usb TypeC

2.USB 电气特性

3.USB 拓扑结构

USB 是主从结构, 分为 HOST( 主机 ) 和从机 ( DEVICE)
USB OTG:既可以做 HOST 又可以做 DEVICE
支持 OTG 模式的 USB 接口一般都是 Mini USB Micro USB 等这些带有 ID 线的接口
ID 线的高低电平表示 USB 口工作在 HOST 还是 DEVICE 模式:
ID=1 OTG 设备工作在从机模式。
ID=0 OTG 设备工作在主机模式。

19、Linux 块设备驱动

前面我们都是在学习字符设备驱动

块设备驱动要远比字符设备驱动复杂得多,不同类型的存储设备又对应不同的驱动子系统
块设备驱动相比字符设备驱动的主要区别如下:
①、块设备只能以块为单位进行读写访问,块是 linux 虚拟文件系统 (VFS) 基本的数据传输单位。字符设备是以字节为单位进行数据传输的,不需要缓冲。
②、块设备在结构上是可以进行随机访问的,对于这些设备的读写都是按块进行的,块设备使用缓冲区来暂时存放数据,等到条件成熟以后在一次性将缓冲区中的数据写入块设备中。这么做的目的为了提高块设备寿命。
块设备结构的不同其 I/O 算法也会不同,比如对于 EMMC SD 卡、 NAND Flash 这类没有任何机械设备的存储设备就可以任意读写任何的扇区( 块设备物理存储单元 )
块设备中最小的可寻址单元是扇区,一个扇区一般是 512 字节,有些设备的物理扇区可能不是 512 字节。不管物理扇区是多少,内核和块设备驱动之间的扇区都是 512 字节。

1.块设备 I/O 请求过程

内核将对块设备的读写都发送到请求队列 request_queue 中, request_queue 中是大量request(请求结构体 ) ,而 request 又包含了 bio bio 保存了读写相关数据,比如从块设备的哪个地址开始读取、读取的数据长度,读取到哪里,如果是写的话还包括要写入的数据等。

20、Linux 网络驱动

1.嵌入式下的网络硬件接口

本章节讨论的都是有线网络!
网卡是独立的硬件,如果电脑要上网就得买个网卡插上去,类似现在的显卡一样。
随着技术的不断发展,现在只需要一个芯片就可以实现有线网卡功能
嵌入式网络硬件分为两部分: MAC PHY
==================================================================
先不学网络驱动,学不进去- -

21、Regmap

regmap 将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用 SPI 或者 I2C 接口 API 函数,统一使用 regmapAPI 函数。
什么情况下会使用 regmap
①、硬件寄存器操作,比如选用通过 I2C/SPI 接口来读写设备的内部寄存器,或者需要读写 SOC 内部的硬件寄存器。
②、提高代码复用性和驱动一致性,简化驱动开发过程。
③、减少底层 I/O 操作次数,提高访问效率。

1.Regmap 驱动框架

regmap 框架分为三层:
①、底层物理总线: regmap 就是对不同的物理总线进行封装,目前 regmap 支持的物理总线有 i2c i3c spi mmio sccb sdw slimbus irq spmi w1
②、 regmap 核心层,用于实现 regmap ,我们不用关心具体实现。
③、 regmapAPI 抽象层, regmap 向驱动编写人员提供的 API 接口,驱动编写人员使用这些 API 接口来操作具体的芯片设备,也是驱动编写人员重点要掌握的。

2.regmap 结构体

Linux 内 核 将 regmap 框 架 抽 象 为 regmap 结 构 体,用结构体 regmap_config去初始化。 
regmap 框架的核心就是 regmap regmap_config 结构体

3.操作函数

I2C 和 SPI ,我们需要根据所使用的接口来选择合适的 regmap 初始化函数。
struct regmap * regmap_init_spi(struct spi_device *spi,
                     const struct regmap_config *config)

struct regmap * regmap_init_i2c(struct i2c_client *i2c,
                     const struct regmap_config *config)
int regmap_read(struct regmap *map, 
 unsigned int reg, 
 unsigned int *val)

int regmap_write(struct regmap *map, 
 unsigned int reg, 
 unsigned int val)

4.regmap_config 掩码设置

结构体 regmap_config 里面有三个关于掩码的成员变量: read_flag_mask write_flag_mask
通过 regmap 读取 SPI 内部寄存器的时候就会将寄存器地址与 read_flag_mask 进行或运算
整个过程不需要我们来操作,全部由 regmap 框架来完成的。

22、Linux IIO 驱动

Linux 内核为了管理这些日益增多的 ADC 类传感器,特地推出了 IIO 子系统
申请注册注销释放

23、Linux ADC 驱动

猜你喜欢

转载自blog.csdn.net/weever7/article/details/129093917