Linux 驱动之 Input 子系统
概述
按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备,按键和键盘就是代表按键信息,鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同。为此 input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层,最终给用户空间提供可访问的设备节点,input 子系统框架如图 所示:
驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。
核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。
事件层:主要和用户空间进行交互。
数据结构
Input 子系统实现的数据结构如下:原图
input_dev:
input_dev 结构体用于描述一个 input 设备,例如按键、鼠标、键盘等,我们编写设备驱动主要就是构造这个结构体。该结构体包含设备类型,消息类型,事件 code 等信息。该数据结构在内核里会挂载到一个全局链表 input_dev_list,所有注册的 input 设备都会挂载到该链表中
input_handler:
input_handler 用于表示一个 input 事件,用于上报 input 事 ...
Linux 驱动之 Regmap 子系统
概述
内核版本 3.1 中引入了 Regmap API,用于统一内核开发人员访问 SPI/IIC 设备的方式,无论是 SPI 设备还是 IIC 设备,只需要初始化,配置 Regmap 就可以通过 Regmap 读写设备。Regmap 子系统主要提供如下两种功能:
第一是为 IIC/SPI/MMIO 等提供统一的访问接口,linux 中大量的 iic 和 spi 设备就可以通过统一的接口进行访问尤其对于那些同时支持 iic 和 spi 接口的设备。
第二是提供缓存访问机制用于加速设备访问设备,对于支持缓存的设备这将大大加快设备的当问速度。
下面看一下 Regmapz 子系统在驱动中的位置,如下:原图
数据结构
regmap 子系统的实现比较简单,数据结构也是很简单的,整体结构如下:原图
regmap_bus:
regmap_bus 数据结构用于对具体的 bus 进行封装,例如对 IIC 或 SPI 总线进行封装,这些 bus 下可以挂载很多设备,他们都从属于一个总线那么就有很多共性操作,对这个结构的封装就用 regmap_bus 结构。该结构主要提供同步读写、异步读写、格式化 ...
Linux 驱动之 Pinctrl 子系统
Pinctrl 概述
关于 pinctrl 主要可以归结为两类设置,其中一类是功能选择,即一组 gpio 是用于 iic 还是 uart 还是就作为普通 gpio 来用,另一类是 gpio 的特性配置,即上拉、下拉、驱动能力和速率的配置。而 pinctrl 主要负责这两类配置的管理工作。总结起来 pinctrl 主要完成以下三种功能:
引脚枚举与命名 (Enumerating and naming)
引脚复用 (Multiplexing):比如用作 GPIO、I2C 或其他功能
引脚配置 (Configuration):比如上拉、下来、open drain、驱动强度等
dts 配置
以全志平台的写法如下(虚构的):
123456789101112131415uart0_pins: uart0-pins { pins = "PA4", "PA5"; function = "uart0";};uart0_sleep_pins: uart0-pins { pins = "PA4", "PA5"; function = "gpio";};...&uart0 ...
Linux 驱动之 GPIO 子系统
GPIO 概述
以前学习 stm32 的时候看到手册里有关于 gpio 的电路图,如下:
保护二极体:
IO 引脚上下两边两个二极体用于防止引脚外部过高、过低的电压输入。当引脚电压高于 VDD 时,上方的二极体导通;当引脚电压低于 VSS 时,下方的二极体导通,防止不正常电压引入晶片导致晶片烧毁
P-MOS 管和 N-MOS 管:
由 P-MOS 管和 N-MOS 管组成的单元电路使得 GPIO 具有“推挽输出”和“开漏输出”的模式
TTL 肖特基触发器:
信号经过触发器后,模拟信号转化为 0 和 1 的数字信号。但是,当 GPIO 引脚作为 ADC 采集电压的输入通道时,用其“模拟输入”功能,此时信号不再经过触发器进行 TTL 电平转换。ADC 外设要采集到的原始的模拟信号
STM32 的 GPIO 支持 4 种输入模式(浮空输入、上拉输入、下拉输入、模拟输入)和 4 种输出模式(开漏输出、开漏复用输出、推挽输出、推挽复用输出)
浮空输入模式:
上拉输入模式:
下拉输入模式:
模拟输入模式:
开漏输出模式:
开漏输出模式下,通过设定位设定/清除寄存器或者输出寄存 ...
Linux 驱动之 SPI 子系统
Linux SPI 驱动框架
对于 SPI 的驱动框架与 I2C 是大致一致的,也分为两层,控制器驱动程序层叫 spi_controller ,主要提供 transfer 函数,进行 spi 协议的收发。
另一层是设备驱动层,基于 spi_bus_type,在 driver 里则使用 spi_read、spi_writer 等函数,最终也会调用到 controller->transfer 函数进行发送接收。
数据结构
整体数据结构如下:原图
spi_controller 结构体用于描述一个 spi 控制器,可以类比 iic 的 i2c_adapt。
spi_device 结构体用于描述一个 spi 设备,可以类比于 i2c_client 结构体。
spi_device 结构里有个 mode 很重要下面是其详细描述,SPI 模式主要有两个特征:
CPOL:这是时钟极性:
0:初始时钟状态为低电平,第一个时钟边沿是上升沿
1:初始时钟状态为高电平,第一个时钟边沿是下降沿
CPHA:这是时钟相位,选择在哪个边沿采样数据:
0:数据在下降沿锁存,输出在上升沿改变
1: ...
Linux 驱动之 PWM
PWM 概述
Ton:信号高电平持续时间
Toff:信号底电平持续时间
Period: 完整 pwm 周期
Duty cycle:pwm 信号周期内保持为 ON 的时间百分比
Linux 下 PWM 驱动
数据结构
pwm 的数据结构如下图所示:原图
pwm_chip 结构体用于抽象 soc 的 pwm 控制器
pwm_device 用来抽象设备
pwm_state 表示 pwm 设备的周期、占空比、极性等信息
pwm ops 结构体提供一系列回调函数。这些回调函数的操作对象是具体的 pwm device
实现
pwm 驱动分为 consumer 和 provider。其中 consumer 提供接口如下:
123static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) //配置 pwm device 的频率、占空比。static inline int pwm_enable(struct pwm_device *pwm) ...
Linux 驱动之 IIC 子系统
IIC 协议
写操作
流程如下:
主芯片要发出一个 start 信号
然后发出一个设备地址(用来确定是往哪一个芯片写数据),方向(读/写,0 表示写,1 表示读)
从设备回应(用来确定这个设备是否存在),然后就可以传输数据
主设备发送一个字节数据给从设备,并等待回应
每传输一字节数据,接收方要有一个回应信号(确定数据是否接受完成),然后再传输下一个数据
数据发送完之后,主芯片就会发送一个停止信号
下图:白色背景表示"主→从",灰色背景表示"从→主"
读操作
流程如下:
主芯片要发出一个 start 信号
然后发出一个设备地址(用来确定是往哪一个芯片写数据),方向(读/写,0 表示写,1 表示读)
从设备回应(用来确定这个设备是否存在),然后就可以传输数据
从设备发送一个字节数据给主设备,并等待回应
每传输一字节数据,接收方要有一个回应信号(确定数据是否接受完成),然后再传输下一个数据
数据发送完之后,主芯片就会发送一个停止信号
下图:白色背景表示"主→从",灰色背景表示"从→主"
IIC 信号
IIC 协议中数据传输的单位是字节,也就是 8 位。但是要用到 9 个时钟:前 ...
Linux 驱动之设备树
dtb 数据解析
内核启动阶段获得 dtb 位置指针
以 arm64 为例,内核启动如下:
1234567891011__HEAD_head:#ifdef CONFIG_EFI add x13, x18, #0x16 b stext#else b stext // branch to kernel start, magic .long 0 // reserved#endif...
在开启 UEFI 支持时,add x13, x18, #0x16 这个 code 实际上是为了满足 EFI 格式的”MZ”头。如果使用 UEFI 来启动 kernel, 会识别出来并走 UEFI 启动的流程,如果是普通的启动过程如使用 uboot 的 booti 进行引导,那么第一条指令就是一条 dummy 指令,第二条就跳转到 stext 运行了。
x0 寄存器保存的是 dtb 里 blob 块的物理地址,x0 寄存器内容由 uboot 设置,uboot 将 dtb 地址传递给内核,跳转到 stext,如下:
123456789101112131415161718192021222324ENT ...
Linux 驱动之文件系统
seq_file
概述
seq_file 只是在普通文件 read 中加入了内核缓冲的功能,从而实现顺序多次遍历读取大数据量的简单接口,seq_file 一般只提供只读接口。
123456struct seq_operations { void * (*start) (struct seq_file *m, loff_t *pos); void (*stop) (struct seq_file *m, void *v); void * (*next) (struct seq_file *m, void *v, loff_t *pos); int (*show) (struct seq_file *m, void *v);};
核心功能
seq_file 接口可通过 <linux/seq_file.h> 获得。 seq_file 包含三个方面:
An iterator interface which lets a virtual file implementation step through the objects it is presenting
Some utili ...
Linux 驱动之设备驱动模型
kobject
概述
Kobjects 有一个 name 和一个引用计数,还具有父指针(允许将对象排列成层次结构)
ktype 是嵌入在 kobject 结构体中的对象。 每个 kobject 结构都需要一个相应的 ktype。 ktype 控制 kobject 在创建和销毁时的行为
kset 是一组 kobjects(kset 本身包含一个 kobject), 这些 kobject 可以拥有相同的 ktype,kset 也是 sysfs 中的一个子目录。Ksets 可以支持 kobjects 的“热插拔”(uevent 事件)
kobject 的数据结构和实现
下图展示了 kobject、ktype 和 kset 三者数据结构之间的关系:原图
sysfs_ops: show 是回调函数,在读取具有该 kobj_type 的所有属性时调用他。缓冲区长度始终是 PAGE_SIZE,在成功时返回写入缓冲区的数据大小,失败返回错误码。写入的时候调用 store 函数,其参数 buf 最大为 PAGE_SIZE,成功返回写入数据大小,失败返回错误码
uevent_ops: 是此 ks ...