devmem2成功时,mmap()失败(C / CPP)[Allwinner A20]

时间:2016-06-20 09:09:23

标签: c arm debian mmap

我试图通过将硬件映射到用户空间来访问硬件registers of an A20 SOM。在这种情况下,target是PIO,列在物理地址0x01C20800

正在使用官方的Olimex Debian7(wheezy)图像。内核Linux a20-olimex 3.4.90+

我能够通过在所述内存空间上使用devmem2 tool和Allwinner的文档来验证位置(使用devmem切换pinmode和level)。

另一方面,

调用mmap

*map = mmap(
        NULL,
        BLOCK_SIZE,    // = (4 * 1024)
        PROT_READ | PROT_WRITE,
        MAP_SHARED,
        *mem_fd,
        *addr_p       
);

mmap error: Invalid argument

失败

以下是代码的更完整版本:http://pastebin.com/mfEuVdbJ

不要担心指针,因为在0x01C28000访问UART0时,相同的代码可以正常工作。 虽然只有UART0(和UART4),它用作串行控制台。 我已经反编译了script.bin(尽管DTB仍在使用)但没有成功,因为那里启用了UART 0,7和8。

我也以root用户身份登录

我仍然会猜到与权限相关的内容,但我现在很丢失,因为devmem完全没有问题

> root@a20-olimex:~# devmem2 0x01c20800 w /dev/mem opened. Memory mapped
> at address 0xb6f85000.

2 个答案:

答案 0 :(得分:0)

如果你阅读了友好的手册

   EINVAL          Invalid argument (POSIX.1)

是错误代码。 (不是EPERM!)。所以我们查找具体的功能

   EINVAL We don't like addr, length, or offset (e.g., they are too large,
          or not aligned on a page boundary).

BLOCK_SIZE, // 1024 - ?

您需要sysconf(_SC_PAGE_SIZE)的倍数。在实践中它将是4096.我不会打扰完全通用的数学来计算它 - 如果你需要它你会找到例子。

答案 1 :(得分:0)

虽然sourcejedi并没有解决我的问题,但他给了我正确的方法。 我看了前面提到的devmem tool's source,发现mmap呼叫的地址被掩盖了

address & ~MAP_MASK获取整个页面,这与我的评论中的操作基本相同。

但是,要在映射完成后返回正确的位置,您必须重新添加掩码

<强> final_address = mapped_address + (target_address & MAP_MASK);

这导致以下代码(基于OP's pastebin

其中 MAP_MASK = (sysconf(_SC_PAGE_SIZE) - 1) 在这种情况下 4095

int map_peripheral(unsigned long *addr_p, int *mem_fd, void **map, volatile unsigned int **addr)
{
    if (!(*addr_p)) {
        printf("Called map_peripheral with uninitilized struct.\n");
        return -1;
    }

    // Open /dev/mem
    if ((*mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
        printf("Failed to open /dev/mem, try checking permissions.\n");
        return -1;
    }

    *map = mmap(
        NULL,
        MAP_SIZE,
        PROT_READ | PROT_WRITE,
        MAP_SHARED,
        *mem_fd,                    // file descriptor to physical memory      virtual file '/dev/mem'

        *addr_p & ~MAP_MASK         // address in physical map to be exposed
/************* magic is here **************************************/
    );

    if (*map == MAP_FAILED) {
        perror("mmap error");
        return -1;
    }

    *addr = (volatile unsigned int *)(*map + (*addr_p & MAP_MASK));
/************* and here ******************************************/
    return 0;
}