返回信息流如物理地址为0xe0000c08, 双字大小
linux下想写个用户程序对其进行控制访问,该如何操作?
有没有办法将地址映射到用户空间
这是一条镜像帖。来源:北邮人论坛 / embedded-system / #5094同步于 2009/6/14
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Embedded_System机器人发帖
已知物理地址,如何在用户空间对其进行访问
yihang
2009/6/14镜像同步11 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
【 在 jklbupt 的大作中提到: 】
: 内核层用remap_page_range
: 应用层用mmap
: 就可以了.
mmap需要设备支持吧,比较简单的方法是这个地址是什么寄存器就在其设备的ioctl函数里加个命令
不需要设备的支持.有物理地址空间就行了.如果是多个外部空间(或寄存器)用ioctl是访问不过来的.而mmap就不同了.映射的基地址加偏移就行了.
【 在 lester98 的大作中提到: 】
: mmap需要设备支持吧,比较简单的方法是这个地址是什么寄存器就在其设备的ioctl函数里加个命令
【 在 jklbupt 的大作中提到: 】
: 不需要设备的支持.有物理地址空间就行了.如果是多个外部空间(或寄存器)用ioctl是访问不过来的.而mmap就不同了.映射的基地址加偏移就行了.
lz问的显然是个可寻址的地方,只要是可寻址的地方不管外部空间还是寄存器,当然可以访问了,我说的设备支持是要有相应的设备文件,mmap不是有个参数是fd吗?要是专门去写个设备驱动直接ioctl就行了,哪里还需要mmap,想用mmap还需要为设备分配和注册resource,显然更麻烦
如物理地址为0xe0000c08, 双字大小 对于ioctl访问不是不行,要用ioctl不也要在驱动中设置,也要注册驱动? 看下边例子
繼 Linux programming 課程紀錄「小談 mmap() 與 VMA」後,今天在 Linux device driver 課程再聊到有關 VMA 與 mmap driver function 的重要觀念;重點紀錄如下。
當 user-space 呼叫 mmap() system call wrapper function 後,kernel 會在 process address space 裡建立新的 VMA,並在 callback mmap driver function 時將「該」VMA 傳遞給我們的驅動程式。
因此,在驅動程式裡,只需要利用 remap_page_range() 將 kernel-space 的 memory:
- I/O memory
- RAM(reserved pages)
- Virtual address space(reserved pages)
對應到該 VMA 即可。最後,寫出了以下的 skeleton code:
// refer to: bttv-dirver.c
int do_card_mmap(struct vm_area_struct *vma, char *adr, unsigned long size)
{
unsigned long start = (unsigned long)adr;
unsigned long page, pos;
pos = (unsigned long)BaseIOAddress;
#if 1 // page or the total size ?
while (size >0) {
page = pos;
if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
return -EAGAIN;
start+=PAGE_SIZE;
pos+=PAGE_SIZE;
size-=PAGE_SIZE;
}
#else
if (remap_page_range(vma, start, pos, size, PAGE_SHARED))
return -EAGAIN;
#endif
return 0;
}
// refer to: videodev.c
int card_mmap(struct file *filp, struct vm_area_struct *vma)
{
do_card_mmap(vma, (char *)vma->vm_start, (unsigned long)(vma->vm_end-vma->vm_start));
return 0;
}
/**************************************************/
struct file_operations card_fops = {
open:card_open,
release:card_release,
ioctl:card_ioctl,
mmap:card_mmap,
};
remap_page_range() 的原理是去修改 page table,但是要不要以 "page" 為最小單位做 page table 的修改(呼叫 'remap_page_range'),必須視情況而定!
只要注册个字符设备就可以上层应用调用mmap了,不也和你的方式一样,而且在应用层访问效率还高(特别是对于十几个以上的外设寄存器访问时), 建议版主有空可以研究下pc104板卡的驱动去,就明白了.
【 在 lester98 的大作中提到: 】
: lz问的显然是个可寻址的地方,只要是可寻址的地方不管外部空间还是寄存器,当然可以访问了,我说的设备支持是要有相应的设备文件,mmap不是有个参数是fd吗?要是专门去写个设备驱动直接ioctl就行了,哪里还需要mmap,想用mmap还需要为设备分配和注册resource,显然更麻烦
再转个贴子吧,以便大家交流!
http://hi.baidu.com/kaloshan/blog/item/d6b6c2d0c6dc208aa1ec9c13.html
利用mmap实现用户空间驱动的理解(转)2008-06-16 14:12利用mmap实现用户空间驱动的理解
在做有关9200嵌入linux系统的一个项目时,由于需要对底层的操作,所以遇到了mmap这个系统调用。对于底层的开发,正常思维是linux的驱动开发。不过由于对linux的接触时间短,知识有限,所以找到了mmap,绕过了驱动开发的阶段。不过,一直困惑于mmap的实现方式,它是怎么实现对底层的操作?更困惑于为什么用户空间就可以实现对底层的操作大家却要在内核空间开发驱动呢?对这些种种疑问,我在网上收集了些资料,发表一下自己的见解,不过感觉还是很不深刻。首先让大家看一个程序,程序很简单,对IO口的操作#include <sys/types.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <memory.h>
#define DEVICE_FILE_NAME "at91_gpio"
#define AT91_SYS 0xfffff000
#define AT91_SYS_2 0xfffc0000
#define AT91C_PIOB_PER 0x600
#define AT91C_PIOB_OER 0x610
#define AT91C_PIOC_CODR 0x634
#define AT91C_PIOC_SODR 0x630
#define AT91C_PIOA_ASR 0x470
#define AT91C_PIOA_PDR 0x404
#define AT91C_PIOA_PSR 0x408
#define AT91C_PIOA_ODR 0x414
#define AT91C_PIOA_IFDR 0x424
#define AT91C_PIOA_CODR 0x434
#define AT91C_PIOA_IDR 0x444
#define AT91C_PIOA_MDDR 0x454
#define AT91C_PIOA_PUER 0x464
#define AT91C_PIOA_OWDR 0x4A4
#define AT91C_PIOA_ABSR 0x478
#define INT *(volatile unsigned int*)
void * map_base;
FILE *f;
int n,fd;
int main(int argc,char *argv[])
{
if((fd=open("/dev/mem",O_RDWR|O_SYNC))==-1){
return(-1);
}
map_base=mmap(0,0xff,PROT_READ|PROT_WRITE,MAP_SHARED,\
fd,AT91_SYS);
INT (map_base+AT91C_PIOB_PER)=(1<<27);//使能寄存器
INT (map_base+AT91C_PIOB_OER)=(1<<27);//输出使能寄存器
while(1){
INT (map_base+AT91C_PIOB_CODR)=(1<<27);//IO口置高
sleep(1);
INT (map_base+AT91C_PIOB_SODR)=(1<<27);//IO口置低
sleep(1);
}
close(fd);
munmap(map_base,0xff);//解除映射关系
}
在这个程序中,首先打开了/dev/mem,然后进行了mmap,也就是对整个物理内存的映射。接下来的操作就根据芯片手册,找到相应的物理地址,对物理地址直接操作。
其中有几个参数需要说明:
PROT_READ:区域可读;
PROT_WRITE:区域可写;
MAP_SHARED:对映射区域的写入数据会复制回文件内, 而且允许其他映射该文件的进程共享。
书中对此图有些说明:在此图中,“起始地址”是mmap的返回值。在图中,映射存储区位于堆和栈之间:这属于实现细节,各种实现之间可能不同。看了此图以后,大家对mmap的实现方式可能就清楚多了,应该对上面那个程序有更多的理解了吧。我们把物理地址都映射到内存中,然后对内存中的地址进行操作,通过MAP_SHARED参数,对映射区域的写入数据复制回文件内,从而达到了对IO口的操作。对于MAP_SHARED区磁盘文件的更新,在写到存储映射区时按内核虚存算法自动进行。还有一点需要说明,因为映射文件的起动位移量受系统虚存页长度的限制,那么如果映射区的长度不是页长度的整数倍时,将如何呢?假定文件长12字节,系统页长为512字节,则系统通常提供512字节的映射区,其中后500字节被设为0。可以修改这500字节,但任何变动都不会在文件中反映出来。到此,就是我对第一问题的解释,即mmap实现方式的理解。而对于第二个问题,更困惑于为什么用户空间就可以实现对底层的操作大家却要在内核空间开发驱动呢?答案我在《Linux设备驱动第三版》中找到了答案,上面是这样描述的:有几个论据倾向于用户空间编程, 有时编写一个所谓的用户空间设备驱动对比钻研内核是一个明智的选择. 在本节, 我们讨论几个理由, 为什么你可能在用户空间编写驱动。
用户空间驱动的好处在于:
完整的 C 库可以连接. 驱动可以进行许多奇怪的任务, 不用依靠外面的程序(实现使用策略的工具程序, 常常随着驱动自身发布).
程序员可以在驱动代码上运行常用的调试器, 而不必走调试一个运行中的内核的弯路.
如果一个用户空间驱动挂起了, 你可简单地杀掉它. 驱动的问题不可能挂起整个系统, 除非被控制的硬件真的疯掉了.
用户内存是可交换的, 不象内核内存. 一个不常使用的却有很大一个驱动的设备不会占据别的程序可以用到的 RAM, 除了在它实际在用时.
一个精心设计的驱动程序仍然可以, 如同内核空间驱动, 允许对设备的并行存取.
如果你必须编写一个封闭源码的驱动, 用户空间的选项使你容易避免不明朗的许可的情况和改变的内核接口带来的问题.
但是用户空间的设备驱动的方法有几个缺点. 最重要的是:
中断在用户空间无法用. 在某些平台上有对这个限制的解决方法, 例如在 IA32 体系上的 vm86 系统调用.
只可能通过内存映射 /dev/mem 来使用 DMA, 而且只有特权用户可以这样做.
存取 I/O 端口只能在调用 ioperm 或者 iopl 之后. 此外, 不是所有的平台支持这些系统调用, 而存取/dev/port可能太慢而无效率. 这些系统调用和设备文件都要求特权用户.
响应时间慢, 因为需要上下文切换在客户和硬件之间传递信息或动作.
更不好的是, 如果驱动已被交换到硬盘, 响应时间会长到不可接受. 使用 mlock 系统调用可能会有帮助, 但是常常的你将需要锁住许多内存页, 因为一个用户空间程序依赖大量的库代码. mlock, 也, 限制在授权用户上.
最重要的设备不能在用户空间处理, 包括但不限于, 网络接口和块设备.
如你所见, 用户空间驱动不能做的事情毕竟太多. 感兴趣的应用程序还是存在: 例如, 对 SCSI 扫描器设备的支持( 由 SANE 包实现 )和 CD 刻录器 ( 由 cdrecord 和别的工具实现 ). 在两种情况下, 用户级别的设备情况依赖 "SCSI gneric" 内核驱动, 它输出了低层的 SCSI 功能给用户程序, 因此它们可以驱动它们自己的硬件.
一种在用户空间工作的情况可能是有意义的, 当你开始处理新的没有用过的硬件时. 这样你可以学习去管理你的硬件, 不必担心挂起整个系统. 一旦你完成了, 在一个内核模块中封装软件就会是一个简单操作了.
通过上面的描述,我的第二个困惑也得到了解决。由于用户空间的诸多限制,迫使我们不得不进行内核空间的设备驱动开发。不过在我的项目中,用mmap就可以解决所有问题了。呵呵,不知道我的解释是否能让大家明白,如果其他新的见解,请留言,谢谢!
谢谢 5楼,不是不想研究,而是事情很多很忙,还是用自己熟悉的方法解决不是更好?下面是我几分钟拷贝粘贴再修改得到的程序,不出意外的话是直接可用的
随便写了一下,物理地址对虚拟地址转换那一句可能跟平台有一点点关系,查查__REG()的定义替换就可以了
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <asm/irq.h>
#include <linux/delay.h>
#include <asm/arch/i2c-client.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/arch/gpio.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#define SIMPLE_CHAR_MAJOR 233
#define SIMPLE_CHAR_NAME "/dev/reg"
int simplechar_ioctl (struct inode * inode ,struct file * file, unsigned int cmd,
unsigned long arg)
{
int ret,paddr,vaddr,val;
ret= get_user(addr, (int *)arg);
vaddr= (*((volatile unsigned long *)io_p2v(addr)));
if(copy_to_user(arg, vaddr,2))
return -EFAULT;
else
return 0;
}
static struct file_operations simple_char_ops = {
.owner = THIS_MODULE,
.ioctl = SIMPLE_HELLO_ioctl,
};
static int __init simplechar_init(void)
{
int ret = -ENODEV;
ret = register_chrdev(SIMPLE_CHAR_MAJOR, "SIMPLE_CHAR_NAME", &simple_char_ops);
if( ret < 0 )
{
printk (" pxa270 init_module failed with %d\n ", ret);
unregister_chrdev (SIMPLE_CHAR_MAJOR, "SIMPLE_CHAR_NAME" );
return ret;
}
return 0;
}
static void __exit simplechar_exit(void)
{
unregister_chrdev(SIMPLE_CHAR_MAJOR, "SIMPLE_CHAR_NAME");
}
MODULE_AUTHOR("ruiva.liuxianyu");
MODULE_LICENSE("GPL");
module_init(simplechar_init);
module_exit(simplechar_exit);
【 在 jklbupt 的大作中提到: 】
: 再转个贴子吧,以便大家交流!
: http://hi.baidu.com/kaloshan/blog/item/d6b6c2d0c6dc208aa1ec9c13.html
: 利用mmap实现用户空间驱动的理解(转)2008-06-16 14:12利用mmap实现用户空间驱动的理解
: ...................
这个方法很好,不知道有没有研究过/dev/mem都包括什么空间?是不是所有可直接寻址的空间都可以访问?
据我所知可直接寻址的空间都可以访问,另外
ret= get_user(addr, (int *)arg);
vaddr= (*((volatile unsigned long *)io_p2v(addr)));
if(copy_to_user(arg, vaddr,2))
return -EFAULT;
else
return 0;
与mmap相比效率的问题就不用说了.