首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

FPGA嵌入式系统的Linux设备驱动程序开发(2)

FPGA嵌入式系统的Linux设备驱动程序开发(2)

驱动程序中相应代码为:
regs_res = platform_get_resource(pdev,IORESOURCE_MEM, 0);
irq_res = platform_get_resource(pdev,IORESOURCE_IRQ, 0);

在获取资源成功后,驱动程序会申请内核空间和I/O 空间,将物理地址映射到虚拟地址以及申请中断
等。在Linux 内核空间申请内存的主要函数是kmalloc()、kzalloc()。由这两个函数申请的内存位于物理内存映射区域,在物理上也是连续的。它们与真实的物理地址只有一个固定的偏移,存在较简单的转换关系。

驱动程序中与内存申请有关的程序代码:
xgpio_inst = kmalloc(sizeof(struct xgpio_instance),GFP_KERNEL);
miscdev = kmalloc(sizeof(struct miscdevice),GFP_KERNEL);

第一个参数是分配的空间大小,第二个标志表示是在内核空间的进程中申请内存。GFP_KERNEL 标志申请内存时,若暂时不能满足,则进程会睡眠引起阻塞。使用kmalloc()、kzalloc()申请的内存要用kfree()释放。
2.1.1 申请I/O 内存空间和映射物理内存:
GPIO 设备控制器有一组寄存器用于读写设备和获取设备状态,即控制寄存器、状态寄存器和数据寄
存器。这些寄存器位于I/O 内存空间[3]。首先需要调用request_mem_region()申请资源,接着将寄存器地址通过ioremap()将物理地址映射到内核空间虚拟地址,之后才可以调用编程接口访问这些设备的寄存器。访问完成后用iounmap()对申请的内核虚拟地址进行释放,并释放申请的I/O 内存资源。GPIO 控制器驱动程序的相关代码如下:
/*申请I/O 内存资源 */
request_mem_region(regs_res->start, remap_size,DRIVER_NAME);
ioremap(regs_res->start, remap_size);
/*映射物理地知道虚拟地址*/

2.1.2 申请中断:
request_irq(irq_res->start,xgpio_interrupt,0,"XGPIO", xgpio_inst)

irq_res->start 是要申请的硬件中断号;xgpio_interrupt 是向系统登记的中断处理函数,是一个
回调函数,中断发生时,系统调用这个函数,dev_id参数(即xgpio_inst)将被传递。

2.1.3 释放虚拟地址和内存资源
static int xgpio_remove(struct platform_device*pdev) { iounmap(xgpio_inst->base_address);
release_mem_region(xgpio_inst->phys_addr,xgpio_inst->remap_size);
kfree(xgpio_inst);
return 0; /*success*/}

2.2 GPIO 控制器的字符型设备接口实现
在Linux 的文件操作系统调用中,字符型设备一般涉及到打开,读写和关闭文件等操作。在控制器驱动程序中要给内核提供file_operations 结构,才能为设备驱动提供用户调用的接口,定义如下:
static struct file_operations xgpio_fops = {
.owner = THIS_MODULE,
.read = xgpio_read,
.open = xgpio_open,
.release = xgpio_release,};

当系统启动后,GPIO 控制器被初始化,申请资源和内核I/O 内存空间。用户调用open 函数打开GPIO设备时,系统调用了xgpio_open()函数,主要完成使能中断等功能。在打开设备后,返回一个文件指针,可以用这个文件指针对设备进行一系列操作。当用户调用read()函数对控制器进行读取的时候,系统调用了xgpio_read()函数,读取GPIO 设备数据寄存器的值。当用户调用close()函数关闭GPIO 设备时,系统调了用xgpio_release()函数,禁止中断。
2.2.1 GPIO 控制器open()函数的实现
在打开GPIO 控制器后,依据GPIO 数据文档,向GPIO 全局中断使能寄存器GIER 写入0x80000000,向中断使能寄存器IER 写入0x00000003 来使能中断:xgpio_open()函数实现代码如下
static int xgpio_open(struct inode *inode, struct file*file)
{
XIo_Out32((int) xgpioinst->v_addr +
XGPIO_GIER_OFFSET, 0x80000000); /* 全局中断使能 */
XIo_Out32((int) xgpioinst->v_addr +
XGPIO_IER_OFFSET, 0x00000003); /* 使能GPIO中断 */
return 0;
}

2.2.2 GPIO 控制器read()函数的实现
当用户空间调用read()函数的时,调用put_user函数实现内核空间数据到应用程序的传递,将内核空间传递给用户空间的数据。xgpio_read()函数实现代码如下:
static ssize_t xgpio_read(struct file *file, char *buf,size_t count, loff_t * ppos)
{
if(put_user(gpio_value, (int*)buf))
return - EFAULT;
else
return sizeof(unsigned int);
}

2.2.3 GPIO 控制器close()函数的实现
在关闭GPIO 控制器后,向GPIO 全局中断使能寄存器GIER 写入0x0,向中断使能寄存器IER 写入0x0来禁止中断。xgpio_release()函数实现代码如下:
static int xgpio_release(struct inode *inode, struct file *file)
{
XIo_Out32((int) xgpioinst->v_addr +XGPIO_GIER_OFFSET, 0x0); /* 禁止全局中断*/
XIo_Out32((int) xgpioinst->v_addr +XGPIO_IER_OFFSET, 0x0); /* 禁止GPIO 中断*/
return 0;
}

3 设备驱动添加到嵌入式Linux内核中
嵌入式Linux 设备驱动程序编写完成后,需要将驱动程序加到内核中[4],这要求修改嵌入式Linux 的源代码,然后重新编译内核。步骤如下:

3.1 将设备驱动文件拷贝到/linux/driver/char 目录下
3.2 在/linux/driver/char 目录下Makefile 中增加如下代码
obj-$(CONFIG_ XMU_GPIO) +=xgpio;

在/linux/driver/char 目录下Kconfig 中增加如下代码:
Config XMU_GPIO
tristate “XMU_GPIO”
depends on XILINX_DRIVERS
select XILINX_EDK
help
This option enables support for Xilinx GPIO.

3.3 重新编译内核,进入Linux 目录,执行以下代码
#make menuconfig

在Character Devices-->中找到<>XMU_GPIO 选中为加载模块的形式:<*>XMU_GPIO,然后保存退出。
#make
这样得到的内核包含了用户的设备驱动程序。
4 GPIO驱动程序的测试
在应用程序中利用函数open() 系统调用xgpio_open()函数来使能GPIO 中断,当中断发生时,执行中断处理程序;应用程序执行read()函数时,系统调用了xgpio_read()函数读取GPIO 数据寄存器的值;当应用程序执行close()函数时,系统调用xgpio_release()函数,屏蔽GPIO 中断。此时,驱动程序测试结束。

5 结语
Linux2.6 内核引入的平台设备机制,使得内核对设备的管理更加简便。本文介绍了基于PowerPC 架构的嵌入式Linux 平台设备驱动的一般设计方法。在基于FPGA 的嵌入式系统中,外设通过GPIO 的IP 核与CPU 的互连,因此,本文介绍的设备驱动程序的设计方法,具有的一定的通用性,对底层驱动程序开发人员有较好的参考价值。此外,在Linux 系统中,字符设备和块设备都被映射到文件系统的文件和目录,很好地体现了“一切都是文件”的思想。所有的字符设备和块设备都被统一地呈现给用户,通过文件系统的调用接口read()、write()等函数即可访问字符设备和块设备[1]。
返回列表