mmap for I/O access

Jan 07, 2008 6 Replies

My call to mmap is causing a Segmentation fault. Is this the right way to do it?



I have some general-purpose I/O on this Embest ARM-based board. According to the manual for the board, the control register and the data register are at absolute physical addresses



0x56000010 and 0x56000014 respectively.

So here is how I tried to test my access to these registers:



#include #include #include #include #include



main() { int fm = open("/dev/mem",O_RDWR); printf("handle to /dev/mem = %08x\r\n",fm);



char *mptr = (char*)mmap( (void*)0, 16, //..16 bytes PROT_READ | PROT_WRITE, MAP_SHARED, fm, 0x56000010); //..starting with Port B CON reg



printf("mptr = %08x", mptr);



printf("GPBCON = %08x\r\n", *((int*)(mptr+0))); printf("GPBDAT = %08x\r\n", *((int*)(mptr+4))); }



When I run this program, the open() returns 4, and then the Segmentation Fault occurs during the execution of mmap, because there was no printf telling what mptr is. Here is the output:


handle to /dec/mem = 00000004


: [] lr : [] Tainted: P



sp : bffffdd8 ip : bffff7d8 fp : bffffea4



r10: 40139280 r9 : 000084a0 r8 : 00000001



r7 : 4000ee5c r6 : 0000830c r5 : bffffec4 r4 : 40020c34



r3 : ffffffff r2 : 0000000f r1 : 00008823 r0 : 0000883c



Flags: NzCv IRQs on FIQs on Mode USER_32 Segment user



Control: C000317F Table: 33E90000 DAC: 00000015



Segmentation fault



- - - - - - - - - - - - - - - - - - - - - - - - - - - I am running as root, so I should be able to do this, right?


Robert Scott Ypsilanti, Michigan


It should work more or less as you have guessed, but there are a few things to fix :

- you need to map to an offset which is a multiple of the page size, usually 4K. Get the page size with a call to getpagesize(2).

- The size you're mapping to also needs to be a multiple of the page size.

I usually write a little wrapper function which allows the user to map arbitrary addresses, computing the offset and alignment for you behind the scenes.

- check that you're mapping to the physical address as actually presented on the CPU's bus, and not the mapped or translated address supported by the CPU. I don't know what way this is set up on ARM. Ask your hardware engineer or check the hardware guide for the board.

- I would strongly recommend that you ass the O_SYNC flag to the open(2) call. This will tell the OS to use an uncached mapping. Not using O_SYNC may cause weird effects.

Thanks. It worked like a charm. Yes, my getpagesize() did return 4K. Now I'm happily turning LEDs on and off.

Robert Scott Ypsilanti, Michigan

More reasonable: (you want mptr to point to an int right?) int *mptr = mmap(

And then: printf("GPBCON = %08x\r\n", mptr[0] ); printf("GPBCON = %08x\r\n", mptr[1] );

For some one who tries to be so political correct to cast the (void *) that mmap returns, it is strange to see here a 4, instead of sizeof(int) .

Maybe you managed to confuse the compiler. It is certainly worthwhile to leave the second line out, see what happens.

--

Albert van der Horst, UTRECHT,THE NETHERLANDS Economic growth -- like all pyramid schemes -- ultimately falters. albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Not necessarily. The I/O defined at 0x56000000 includes some 32-bit and some

16-bit registers, and even some 8-bit ones. And the documentation gives all the addresses in terms of byte addresses. So if I want to make my code look like it corresponds exactly with the documentation, then I want address arithmetic to work on bytes. The variable mptr is never used directly in my application. I use it in macros, like:

#define gpb_DAT (*(short int*)(mptr+0x14))

Right. This was only a test. In the real application I use the macros like the one shown above.

No need. The problem of the segmentation fault was solved by the posting of Geronimo on 1/7/2008. The problem was that I was not using a page boundary and a size equal to a multiple of the page size (4K) in my call to mmap.

Robert Scott Ypsilanti, Michigan

the

it

In your situation, I like to use a struct with the __packed__ attribute (as supported by GCC and other compilers). This lets you see registers of different sizes directly by accessing members of a struct. But the outcome is much the same.

Using structures yes, but don't use the __packed__ attribute. This could change the way the compiler generates code to access the members in it (for example bytewise access on larger members that are not aligned for this type of architecture). Best way for maintenance would be to create a few inline functions with the access width in their name like in8, out8, in16, out16 aso. that are working with simple address offsets. Its also more portable, because some architectures do some kind of access optimization (reordering) in background so the volatile attribute does not help this case.

JB

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required