DMA——直接内存访问
允许设备在没有CPU干预的情况下访问主存储器RAM,使CPU完成其他任务。当需要传输数据块时,处理器向DMA控制器提供源地址和目标地址以及总字节数,然后DMA控制器会自动将数据从源传输到目标,而不会占用CPU周期。剩余字节数为0时,块传输结束
DMA映射
- DMA映射包括分配DMA缓冲区和为其生成总线地址
- 映射分两种类型:一致DMA映射和流式DMA映射
- 一致DMA映射:可多次传输,会自动解决缓存一致性问题,存在于驱动程序的整个生命周期
- 流式DMA映射:会自动解决缓存一致性问题,可以在每次传输之间调用几个函数。在DMA传输完成时立即取消映射
- 流式映射的限制:
(1)映射需要使用已分配的缓冲区
(2)映射可以接受几个分散的不连续缓冲区
(3)映射的缓冲区属于设备而不属于CPU。CPU使用缓冲区之前应该首先解除映射,这是为了缓存
(4)对于写入事物(CPU到设备),驱动程序应该在映射之前将数据放入缓冲区
(5)必须指定数据移动的方向,只能基于该方向使用数据
流式映射的种类
(1)单缓冲区映射:只允许单页映射
(2)分散/聚集映射:传递多个缓冲区(分散在内存中)
从设备DMA步骤
- 分配DMA从通道
- 设置从设备和控制器的特定参数
- 获取事务的描述符
- 提交事务
- 发起挂起的请求并等待回调通知
注:DMA通道——I/O数据传输的高速公路
Linux设备模型
- 类的概念,对相同类型的设备或提供相同功能的设备进行分组(如鼠标和键盘都是输入设备)
- 通过sysfs虚拟文件系统与用户空间通信,以便用户空间管理和枚举设备及其公开的属性
- 管理对象声明周期,使用引用计数
- 电源管理,已处理设备关闭的程序
- 代码可重用性。类和框架提供接口,任何向它们注册的驱动程序都必须遵守
- LDM在内核中引入了面向对象的编程风格
- LDM上层依赖于总线、设备驱动程序和设备
总线
- 总线是设备和处理器之间的通道链路
- 总线控制器:管理总线并将其协议输出到设备的硬件实体
总线数据结构
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs;
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
struct subsys_private *p;
struct lock_class_key lock_key;
};
- match:回调,每当新设备或驱动程序添加到总线中时调用它,让总线确定某个设备能否由指定的驱动程序或其他逻辑处理,以及指定的驱动程序是否支持某个设备。对于枚举设备(PCIE,USB),验证通过将驱动程序支持的设备ID与指定设备的设备ID进行比较来实现
- probe:回调,在新设备或驱动程序添加到总线并匹配后调用,
- remove:当设备移除总线时调用
- suspend:总线上的设备需要进入睡眠模式时调用
- resume:总线上的设备退出睡眠模式时调用
- pm:总线电源管理
- drv_groups:表示总线上设备驱动程序的默认属性,传递给该字段的属性会被发送给总线上注册的每个驱动程序
- dev_groups:表示总线上设备的默认属性,传递给该字段的属性会被发送给总线上注册的每个设备
- bus_groups:保存一组默认属性,当总线注册到内核时,自动添加这组默认属性