2.1 驱动结构
在Linux中,设备也是作为文件来访问的。VFS(虚拟文件系统)为各种不同的文件系统提供了统一的访问接口,通过这些接口,应用程序可以直接使用open、read和IOctl等系统调用对设备进行访问和控制。
本例中,把HPI作为一个外围设备,其驱动主要实现对设备的打开、关闭、内存映射、视频数据缓冲区管理和物理内存切换等功能。根据原理图,可以确定HPI四个寄存器对应的物理地址,在驱动初始化过程中,调用ioremap_uncache函数把物理地址映射为内核虚拟地址,在驱动层通过内核虚拟地址访问HPI的4个寄存器。
存储器映射I/O把HPI驱动分配的数据空间直接映射到应用程序的虚拟地址空间,应用程序直接访问该空间,避免了用read/write系统调用导致的视频数据二次拷贝。在内核里,由驱动分配一定的缓存,当应用程序不能及时处理DSP发送过来的视频数据,可以缓存这些数据;当应用程序处理完一帧图像时,采用Linux的物理内存切换技术,把下一帧数据所在的物理地址重映射到应用程序的同一虚拟地址,这样,应用程序不用频繁调用mmap函数映射内存。
2.2 存储器映射I/O
一般情况下,当应用程序用read/write读写设备数据时,该设备的驱动先将设备数据从设备上采样到内核缓冲区,再从内核缓冲区拷贝到应用程序缓冲区,数据经过了两次拷贝。当数据量比较小时,如一些控制命令或状态信息,对系统性能几乎没有影响。但是,如果一次传输的数据量比较大,比如视频显卡上的实时视频图像,两次拷贝将大大影响系统的数据处理效率。这时,可采用存储器映射I/O技术,在内核层存储器映射I/O由函数remap_page_range完成。
由remap_page_range函数的原型可以知道,该函数的意义在于通过将特定物理地址映射到进程虚拟地址,进程可以访问特定的物理地址,而这在普通情况下是不可能的。在本例中,当进程调用mmap函数进行存储映射时,内核会调用驱动注册的hpi_mmap函数,传入的参数之一包括进程虚拟地址。在hpi_mmap函数里,调用remap_page_range完成从缓冲区物理地址到进程虚拟地址的映射。hpi_mmap函数实现如下: