对于2.6.25以上内核,直接调用
vma->vm_flags |= VM_SHARED | VM_RESERVED;
io_remap_pfn_range(vma, vma->vm_start, phy_addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
但是2.6.18内核就必须按照下面的步骤来完成映射[luther.gliethtttp].
对于2.6.18内核使用如下方法的驱动实例可以参考2.6.18.5内核源码
sound/oss/i810_audio.c|1044| SetPageReserved(page);
alloc_dmabuf {
/* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */
pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);
for (page = virt_to_page(rawbuf); page <= pend; page++)
SetPageReserved(page);
}
dealloc_dmabuf {
for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)
ClearPageReserved(page);
}
static int i810_mmap(struct file *file, struct vm_area_struct *vma)
{
if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT,
size, vma->vm_page_prot))
}
remap_pfn_range 通过你的页帧号来建立页表, 并映射到用户空间!
一般情况是你的驱动分配一块内存,然后在驱动的mmap中使用这块内存的 物理地址转成页帧号, 再调用remap_pfn_range!
假设1你是通过kmalloc(),get_free_pages()等分配的,这种内存页是不能通过remap_pfn_range()映射出去的,要对每个页面调用SetPageReserverd()标记为“保留”才可以,virt_to_phys()函数只是得到其物理地
址,remap_pfn_range()中的第三个参数是要求物理页便的“帧”号,即pfn,所以你的phys还要“> >
PAGE_SHIFT”操作
假设2你是通过vmalloc分配得来的,同上,不同的是要用vmalloc_to_pfn
3,用kmalloc,get_free_pages,vmalloc分配的物理内存页面最好还是不要用remap_pfn_page方法,建议使用VMA的nopage方法
4,对于这样的设备内存,最好对调用pgprot_nocached(vma-> vm_page_prot)后传给remap_pfn_range,防止处理器缓存
5,你写的是framebuffer驱动,最好是用fb_mmap(),可扩展
static int mmap_xxxxx(struct file *filp, struct vm_area_struct *vma)
{
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long page, pos;
if(size > MMAP_MEM_SIZE)
return -EINVAL;
printk( "start=0x%08x offset=0x%08x\n", start, offset );
printk( "kernel msg=%s\n", mmap_buf );
pos = (unsigned long)mmap_buf + offset;
page = virt_to_phys( pos ) >> PAGE_SHIFT ;
if ( remap_pfn_range( vma, start, page, size, PAGE_SHARED )) {
return -EAGAIN;
}
else {
printk( "remap_pfn_range %u\n success\n", page );
}
vma->vm_flags &= ~VM_IO;
vma->vm_flags |= VM_RESERVED;
return 0;
}
还有就是
offset参数右移PAGE_SHIFT
就是得到页帧号, 前提是offset 是内核物理地址!
kmalloc, get_free_pages都可以通过 virt_to_phy取得
vmalloc 可以通过page_address( vmalloc_to_page( addr ) )取得
阅读(2725) | 评论(0) | 转发(0) |