驱动学习笔记
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 核心层负责处理这些事件
驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。
核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。
事件层:主要和用户空间进行交互。
代码编写
- 使用 input_allocate_device 函数申请一个 input_dev。
- 初始化 input_dev 的事件类型以及事件值。
- 使用 input_register_device 函数向 Linux 系统注册前面初始化好的 input_dev。
- 卸载input驱动的时候需要先使用input_unregister_device函数注销掉注册的input_dev, 然后使用 input_free_device 函数释放掉前面申请的 input_dev。
- 上报输入事件 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,也就是控制局域网络
- 每个单元都是独立的 CAN 节点
同一个 CAN 网络中所有单元的通信速度必须一致
1.CAN 的特点主要有一下几点:
①、多主控制: 根据标识符(Identifier 以下称为 ID)决定优先级,仲裁失利的单元则立刻停止发送而进行接收工作
2.CAN 电气属性
3.CAN协议
18、USB驱动
1.usb接口类型
方口usb B
mini usb
micro usb
usb TypeC
2.USB 电气特性
3.USB 拓扑结构
19、Linux 块设备驱动
前面我们都是在学习字符设备驱动
1.块设备 I/O 请求过程