mmap() 失败而 devmem2 成功 (C/CPP) [Allwinner A20]
mmap() fails while devmem2 succeeds (C/CPP) [Allwinner A20]
我正在尝试通过将硬件 registers of an A20 SOM 映射到用户 space 来访问它们。在这种情况下,目标是 PIO,列在物理地址 0x01C20800
.
正在使用官方 Olimex Debian7 (wheezy) 映像。内核 Linux a20-olimex 3.4.90+
我能够通过使用 devmem2 tool 和 Allwinner 关于所述内存的文档 space 来验证位置(使用 devmem 切换引脚模式和级别)。
另一方面,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.
如果你阅读了友好的手册
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。我不会费心用完全通用的数学来计算它 - 如果需要,您会找到示例。
虽然 没有解决我的问题,但他给了我正确的方法。
我看了前面提到的 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;
}
我正在尝试通过将硬件 registers of an A20 SOM 映射到用户 space 来访问它们。在这种情况下,目标是 PIO,列在物理地址 0x01C20800
.
正在使用官方 Olimex Debian7 (wheezy) 映像。内核 Linux a20-olimex 3.4.90+
我能够通过使用 devmem2 tool 和 Allwinner 关于所述内存的文档 space 来验证位置(使用 devmem 切换引脚模式和级别)。
另一方面,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.
如果你阅读了友好的手册
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。我不会费心用完全通用的数学来计算它 - 如果需要,您会找到示例。
虽然
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;
}