嵌入式Linux下SRAM驱动程序的开发原理及应用

2019-07-12 16:23发布

摘要: 嵌入式Linux是目前操作系统领域中的一个热点。其要点与难点是驱动程序的开发。本文简要论述了基于现代公司嵌入式Arm处理器芯片的嵌入式Linux的SRAM驱动程序的开发原理及流程。 关键词:嵌入式Linux;Arm;驱动程序;SRAM;设备文件 1. Linux 设备驱动程序的概念: 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件, 应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1) 对设备初始化和释放。 2) 把数据从内核传送到硬件和从硬件读取数据。 3) 读取应用程序传送给设备文件的数据和回送应用程序请求的数据。 4) 检测和处理设备出现的错误。 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备[1]。 本文将以字符型设备-SRAM的设备驱动程序为例,简要论述了基于现代公司嵌入式Arm处理器芯片-HMS30C7202的嵌入式Linux的驱动程序的开发原理及流程。 2. Arm处理器HMS30C7202简介:       Hynix HMS30C7202(32位RISC 微处理器)是由ARM720T内核[3]和其他一些外围接口器件组成,具有高性能低功耗的特点,片内资源非常丰富,具有极高的集成度,非常适用于嵌入式系统应用。       HMS30C7202 微处理器有如下特性:       内核运行速率可达70 MHz;8KB 综合指令/数据的cache;具有内存管理单元MMU;支持小端操作系统;2KB SRAM 可用于内部buffer [4]。       芯片内集成了许多外围设备。其中存储器控制器包括 ROM, Flash, SRAM, SDRAM,图1显示了HMS30C7202与SRAM的硬件连结关系。 图1  HMS30C7202与SRAM的硬件连结 3. SRAM驱动程序的开发原理及具体应用 1) I/O内存的二次映射及释放 使用I/O内存时最普遍的的硬件和软件处理方式是这样的:设备对应于某些约定的物理地地,但是CPU并没有预先定义访问它们的虚拟地址。这些约定的物理地址可以是硬件连接到设备上的,也可以是在启动时由系统固件(如 BIOS)指定的。不管哪种方式,为了让软件可以访问I/O内存,必须有一种把虚拟地址赋予设备的方法。这个任务是由ioremap 函数完成的[2] 。 #include void *ioremap(unsigned long phys_addr, unsigned long size); void iounmap(void * addr); ioremap的作用是把一个物理内存地址点映射为一个内核指针,被映射数据的长度由size参数设定。本模块的功能是把一块SRAM区域二次映射到一个我们可以从驱动程序里访问的虚拟地址上去。 二次映射完成后,就可以直接对其中的数据进行读写了。本模块Iomap使用的是数据长度以long(长整数)为单位的函数。如下所示:     unsigned *readl(void *address);     unsigned *writel(unsigned value, void *address)     readl返回从地址address处读到的字节,而writel把数据写到指定地址去。writel还可以返回它刚才写的数据──如果需要这样做的话。     二次映射的区域必须在卸载模块时解除映射关系和释放。把ioremap函数返回的那个指针传递到iounmap函数可以解除映射关系,如下所示:     void iounmap(void *address);      一旦有了ioremap 和iounmap ,设备驱动程序就能访问任何I/O内存地址,而不管它是否直接映射到虚拟地址空间。     在本模块中ioremap和iounmap被包装成如下两个重要函数: (Iomap为本模块自定义数据结构,立刻会提到)其中iomap_setup将在ioctl方法中被调用,实现二次映射;iomap_remove将在模块卸载时调用,解除映射关系和释放二次映射的区域。 2) Iomap设备的定义及管理 Iomap设备被定义为下面这个结构:      结构定义里的base是SRAM区的起始地址(基地址),size是SRAM区的长度,ptr将用来保存ioremap函数的返回值。本模块用一个全局性的数组         Iomap *iomap_dev[IOMAP_MAX_DEVS];      (其中#define IOMAP_MAX_DEVS  16)来保存可能创建的设备,数组的下标就是设备的辅编号。这是需要管理多台设备时广泛使用的方法,它的具体应用并不复杂。全局数组iomap_dev里为每个可能会被访问的设备保存着一个指针。在设备每一个功能函数的入口点位置上,将要被操作的具体设备从数组里提取出来,如下所示:         Iomap *idev = iomap_dev[MINOR(inode->i_rdev)];       如果函数不直接使用一个inode节点作为参数,它也会从file文件结构里被间接地提取出来。文件结构里有一个指向与文件关联着的dentry项目("directory entry",目录项)的指针,而inode可以从这个结构里查到[2]。如下所示:       Iomap *idev = iomap_dev[MINOR(file->f_dentry->d_inode->i_rdev)];   3) 填充file_operations结构:     由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如 open, read, write, close ……, 注意,不是fopen, fread ……,但是如何把系统调用和驱动程序关联起来呢?这需要填充file_operations数据结构:       这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。下面就讨论本模块几个重要的函数,在这里我们称之为过程或方法。 4) 对Iomap设备操作的几个重要过程及方法 a) Iomap_read()过程:函数定义如下: static ssize_t iomap_read(struct file *file, char *buf, size_t count, loff_t *offset){} ;图2显示了Iomap_read()过程: 图2  Iomap_read()过程 b) Iomap_write()过程:函数定义如下: static ssize_t iomap_write(struct file *file, const char *buf, size_t count,loff_t *offset) {} 与Iomap_read()过程类似,不再重复。 c) Iomap_Open方法:函数定义如下: int iomap_open(struct inode *inode, struct file *file) {};图3显示了Iomap_Open方法: 图3  Iomap_Open方法 d) Iomap_release 方法:函数定义如下: int iomap_release(struct inode *inode, struct file *file) {} 减少设备使用计数并return 0; e) Iomap_ioctl方法:函数定义如下: static int iomap_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg) {};图4显示了Iomap_ioctl方法: 图4  Iomap_ioctl方法 5) Iomap设备注册及注销 int init_module(void) {}:注册iomap设备 res = register_chrdev(IOMAP_MAJOR, "iomap", &iomap_fops); 并为设备数组iomap_dev分配内存; void cleanup_module(void) {}:释放iomap_dev分配的内存,注销设备iomap。   4. 测试本模块 首先以根用户身份编译模块,然后创建两个设备文件,辅编号一个是0,另一个是1。再把模块插入到内核里去。 现在编辑一个小的测试程序,对上述两个设备文件/ dev/iomap0及/dev/iomap1进行操作。其中/ dev/iomap0基地址为0xc0000000, 大小为1MB; /dev/iomap1基地址为0xc0100000, 大小为1MB。设备文件以读写方式打开。 程序编译后运行,得到预期结果。 5. 小结: 本文论述了嵌入式Linux下SRAM驱动程序的开发原理及流程,并作了测试,得到了预期结果,这样嵌入式Linux就能得到更加广泛的应用。