首页 > 编程知识 正文

dstt内核下载,linux中ioremap函数

时间:2023-05-06 01:30:10 阅读:18570 作者:4554

大多数外围设备是通过读写设备上的寄存器完成的。 通常分为控制寄存器、状态寄存器、数据寄存器三大类,外围设备的寄存器通常是连续寻址的。 根据CPU体系结构,CPU有两种方法来寻址IO端口。

(1) I/O映射方式(I/O-mapped ) ) ) )。

通常,与X86处理器一样,为外围设备实现称为“I/O地址空间”或“I/O端口空间”的单独的地址空间,CPU通过专用I/O指令(例如X86的IN和OUT指令)在该空间中

)2)内存映射方式(内存映射) )。

RISC命令系统上的CPU(arm、PowerPC等)通常只实现一个物理地址空间,而外围设备上的I/O端口是内存的一部分。 在这种情况下,CPU可以像存储器单元一样访问外围设备I/O端口,而无需设置专用的外围设备I/O命令。

但是,两者的硬件实现差异对软件是完全透明的,驱动程序开发人员可以将内存映射方式的I/O端口和外围存储器统一视为“I/O存储器”资源。

通常,在系统运行时,外围设备I/O内存资源的物理地址是已知的,由硬件设计决定。 但是,CPU通常没有为这些已知外围设备I/O内存资源的物理地址预定义虚拟地址范围。 驱动程序必须映射到核心虚拟地址空间,然后根据映射获得的核心虚拟地址范围访问这些I/O内存资源,而不是直接从物理地址访问I/O内存资源。 Linux在io.h头文件中声明了一个函数ioremap ),用于将I/O内存资源的物理地址映射到核心虚拟地址空间(3GB-4GB )。 原型如下。

void * io remap (unsigned long phys _ addr,unsigned long size,unsigned long flags );

iounmap函数用于取消ioremap ()的映射,原型为:

语音地图(语音* Addr;

这两个函数都在mm/ioremap.c文件中实现。

将I/O内存资源的物理地址映射到核心虚拟地址后,理论上可以像RAM一样直接读写I/O内存资源。 为了保证驱动程序跨平台的可移植性,必须使用Linux上的特定函数访问I/O内存资源。 不应该用指向核心虚拟地址的指针访问。 在x86平台上,读/写I/O函数如下所示:

#definereadb(addr ) (* ) * (电压无符号化char * ) _io_virt ) )

#definereadw(addr ) (* ) * (电压不统一短整型* ) _io_virt ) )

#定义索引(addr ) (* ) *(volatile unsigned int * ) _io_virt ) )

#definewriteb(b,addr ) (* ) *(volatile unsigned char * ) _ io _ virt (addr )=(b ) )

#definewritew(b,addr ) (* ) *(volatile unsigned short * ) _ io _ virt (addr )=(b ) )

#definewritel(b,addr ) (* ) *(volatile unsigned int * ) _io_virt ) (addr )=(b ) )

#definememset_io(a,b,c ) memset ) _io_virt ),b ),c ) )

#definememcpy_fromio(a,b,c ) memcpy ) (a )、_io_virt )、(c ) )

#definememcpy_toio(a,b,c ) memcpy ) _io_virt,(b,) c ) )

最后,特别强调驱动程序中mmap函数的实现方法。 在mmap中映射设备意味着将用户空间中的一些地址与设备内存相关联,并且实际上是对设备的访问,只要程序在分配的地址范围内进行读取或写入。

笔者在Linux源代码中进行了包含“ioremap”文本的搜索,发现实际出现的ioremap位置相当少。 为此,笔者寻找I/O操作物理地址转换为虚拟地址的真实位置,发现Linux中有一个词可以代替ioremap,但这个转换过程是必不可少的。

例如,让我们再讨论一下名为S3C2410的ARM芯片实时时钟(RTC )驱动中的一小部分。

staticvoidget_RTC_time(intALM,struct rtc_time *rtc_tm ) ) ) ) )。

{

spin_lock_IRQ(RTC_lock;

if(ALM==1) {

RTC _ TM-TM _ year=(统一字符图) ALMYEAR Msk_RTCYEAR;

rtc_tm-tm_mon=(unsigned char ) ALMMON Msk_RTCMON;

RTC_TM-TM_mday=(unsignedchar ) a

LMDAY & Msk_RTCDAY;

rtc_tm->tm_hour = (unsigned char)ALMHOUR & Msk_RTCHOUR;

rtc_tm->tm_min = (unsigned char)ALMMIN & Msk_RTCMIN;

rtc_tm->tm_sec = (unsigned char)ALMSEC & Msk_RTCSEC;

}

else {

read_rtc_bcd_time:

rtc_tm->tm_year = (unsigned char)BCDYEAR & Msk_RTCYEAR;

rtc_tm->tm_mon = (unsigned char)BCDMON & Msk_RTCMON;

rtc_tm->tm_mday = (unsigned char)BCDDAY & Msk_RTCDAY;

rtc_tm->tm_hour = (unsigned char)BCDHOUR & Msk_RTCHOUR;

rtc_tm->tm_min = (unsigned char)BCDMIN & Msk_RTCMIN;

rtc_tm->tm_sec = (unsigned char)BCDSEC & Msk_RTCSEC;

if (rtc_tm->tm_sec == 0) {

/* Re-read all BCD registers in case of BCDSEC is 0.

See RTC section at the manual for more info. */

goto read_rtc_bcd_time;

}

}

spin_unlock_irq(&rtc_lock);

BCD_TO_ttdjb(rtc_tm->tm_year);

BCD_TO_ttdjb(rtc_tm->tm_mon);

BCD_TO_ttdjb(rtc_tm->tm_mday);

BCD_TO_ttdjb(rtc_tm->tm_hour);

BCD_TO_ttdjb(rtc_tm->tm_min);

BCD_TO_ttdjb(rtc_tm->tm_sec);

/* The epoch of tm_year is 1900 */

rtc_tm->tm_year += RTC_LEAP_YEAR - 1900;

/* tm_mon starts at 0, but rtc month starts at 1 */

rtc_tm->tm_mon--;

}

I/O操作似乎就是对ALMYEAR、ALMMON、ALMDAY定义的寄存器进行操作,那这些宏究竟定义为什么呢?

#define ALMDAY bRTC(0x60)

#define ALMMON bRTC(0x64)

#define ALMYEAR bRTC(0x68)

其中借助了宏bRTC,这个宏定义为:

#define bRTC(Nb) __REG(0x57000000 + (Nb))

其中又借助了宏__REG,而__REG又定义为:

# define __REG(x) io_p2v(x)

最后的io_p2v才是真正"玩"虚拟地址和物理地址转换的地方:

#define io_p2v(x) ((x) | 0xa0000000)

与__REG对应的有个__PREG:

# define __PREG(x) io_v2p(x)

与io_p2v对应的有个io_v2p:

#define io_v2p(x) ((x) & ~0xa0000000)

可见有没有出现ioremap是次要的,关键问题是有无虚拟地址和物理地址的转换!

下面的程序在启动的时候保留一段内存,然后使用ioremap将它映射到内核虚拟空间,同时又用remap_page_range映射到用户虚拟空间,这样一来,内核和用户都能访问。如果在内核虚拟地址将这段内存初始化串"abcd",那么在用户虚拟地址能够读出来:

/************mmap_ioremap.c**************/

#include

#include

#include

#include

#include /* for mem_map_(un)reserve */

#include /* for virt_to_phys */

#include /* for kmalloc and kfree */

MODULE_PARM(mem_start, "i");

MODULE_PARM(mem_size, "i");

static int mem_start = 101, mem_size = 10;

static char *reserve_virt_addr;

static int major;

int mmapdrv_open(struct inode *inode, struct file *file);

int mmapdrv_release(struct inode *inode, struct file *file);

int mmapdrv_mmap(struct file *file, struct vm_area_struct *vma);

static struct file_operations mmapdrv_fops =

{

owner: THIS_MODULE, mmap: mmapdrv_mmap, open: mmapdrv_open, release:

mmapdrv_release,

};

int init_module(void)

{

if ((major = register_chrdev(0, "mmapdrv", &mmapdrv_fops)) < 0)

{

printk("mmapdrv: unable to register character device/n");

return ( - EIO);

}

printk("mmap device major = %d/n", major);

printk("high memory physical address 0x%ldM/n", virt_to_phys(high_memory) /

1024 / 1024);

reserve_virt_addr = ioremap(mem_start *1024 * 1024, mem_size *1024 * 1024);

printk("reserve_virt_addr = 0x%lx/n", (unsigned long)reserve_virt_addr);

if (reserve_virt_addr)

{

int i;

for (i = 0; i < mem_size *1024 * 1024; i += 4)

{

reserve_virt_addr[i] = 'a';

reserve_virt_addr[i + 1] = 'b';

reserve_virt_addr[i + 2] = 'c';

reserve_virt_addr[i + 3] = 'd';

}

}

else

{

unregister_chrdev(major, "mmapdrv");

return - ENODEV;

}

return 0;

}

/* remove the module */

void cleanup_module(void)

{

if (reserve_virt_addr)

iounmap(reserve_virt_addr);

unregister_chrdev(major, "mmapdrv");

return ;

}

int mmapdrv_open(struct inode *inode, struct file *file)

{

MOD_INC_USE_COUNT;

return (0);

}

int mmapdrv_release(struct inode *inode, struct file *file)

{

MOD_DEC_USE_COUNT;

return (0);

}

int mmapdrv_mmap(struct file *file, struct vm_area_struct *vma)

{

unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;

unsigned long size = vma->vm_end - vma->vm_start;

if (size > mem_size *1024 * 1024)

{

printk("size too big/n");

return ( - ENXIO);

}

offset = offset + mem_start * 1024 * 1024;

/* we do not want to have this area swapped out, lock it */

vma->vm_flags |= VM_LOCKED;

if (remap_page_range(vma, vma->vm_start, offset, size, PAGE_SHARED))

{

printk("remap page range failed/n");

return - ENXIO;

}

return (0);

}

remap_page_range函数的功能是构造用于映射一段物理地址的新页表,实现了内核空间与用户空间的映射,其原型如下:

int remap_page_range(vma_area_struct *vma, unsigned long from, unsigned long to, unsigned long size, pgprot_tprot);

使用mmap最典型的例子是显示卡的驱动,将显存空间直接从内核映射到用户空间将可提供显存的读写效率。

(在内核驱动程序的初始化阶段,通过ioremap()将物理地址映射到内核虚拟空间;在驱动程序的mmap系统调用中,使用remap_page_range()将该块ROM映射到用户虚拟空间。这样内核空间和用户空间都能访问这段被映射后的虚拟地址。)

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。