Page 1 of 1

Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Tue May 31, 2016 10:02 pm
by mattlewis
I'm attempting to access /dev/gpiomem in Java via a thin JNI wrapper that returns a DirectByteBuffer to a mmap'ed address and simply cannot get it to work.
I've uploaded my very rough source code here: https://github.com/mattjlewis/diozero/t ... mmap-tests.
Rough outline of C code:

Code: Select all

fdMem = open("/dev/gpiomem", O_RDWR | O_SYNC | O_CLOEXEC);
gpioAddr = (uint32_t *)mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fdMem, gpioBase);
return (*env)->NewDirectByteBuffer(env, gpioAddr, BLOCK_SIZE);
C output:

Code: Select all

&gpioAddr[0]=0x76f92000, gpioAddr[0]=0x21200900
&gpioAddr[1]=0x76f92004, gpioAddr[1]=0x24024
&gpioAddr[2]=0x76f92008, gpioAddr[2]=0x0
&gpioAddr[3]=0x76f9200c, gpioAddr[3]=0x1000048
&gpioAddr[4]=0x76f92010, gpioAddr[4]=0x2422400c
&gpioAddr[5]=0x76f92014, gpioAddr[5]=0x924
&gpioAddr[6]=0x76f92018, gpioAddr[6]=0x0
&gpioAddr[7]=0x76f9201c, gpioAddr[7]=0x6770696f
&gpioAddr[8]=0x76f92020, gpioAddr[8]=0x6770696f
&gpioAddr[9]=0x76f92024, gpioAddr[9]=0x6770696f
&gpioAddr[10]=0x76f92028, gpioAddr[10]=0x6770696f
&gpioAddr[11]=0x76f9202c, gpioAddr[11]=0x6770696f
&gpioAddr[12]=0x76f92030, gpioAddr[12]=0x6770696f
&gpioAddr[13]=0x76f92034, gpioAddr[13]=0xb000c1ff
&gpioAddr[14]=0x76f92038, gpioAddr[14]=0x3e5c5c
&gpioAddr[15]=0x76f9203c, gpioAddr[15]=0x0
&gpioAddr[16]=0x76f92040, gpioAddr[16]=0x0
&gpioAddr[17]=0x76f92044, gpioAddr[17]=0x0
&gpioAddr[18]=0x76f92048, gpioAddr[18]=0x0
&gpioAddr[19]=0x76f9204c, gpioAddr[19]=0x0
Java output:

Code: Select all

gpioReg[0]=0x67706900
gpioReg[1]=0x67706924
gpioReg[2]=0x67706900
gpioReg[3]=0x67706948
gpioReg[4]=0x6770690c
gpioReg[5]=0x67706924
gpioReg[6]=0x67706900
gpioReg[7]=0x6770696f
gpioReg[8]=0x6770696f
gpioReg[9]=0x6770696f
gpioReg[10]=0x6770696f
gpioReg[11]=0x6770696f
gpioReg[12]=0x6770696f
gpioReg[13]=0x677069ff
gpioReg[14]=0x6770695c
gpioReg[15]=0x67706900
gpioReg[16]=0x67706900
gpioReg[17]=0x67706900
gpioReg[18]=0x67706900
gpioReg[19]=0x67706900
I think I can correctly read and write values in C (following code snippets from pigpio and wiringPi), however, the values I see in Java are quite different (thank you JGPIO for pointing out the last byte is always correct, the first 3 bytes are always 0x677069).

Any ideas would be greatly appreciated.

Regards,
Matt

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Wed Jun 01, 2016 8:48 am
by joan
In http://abyz.co.uk/rpi/pigpio/examples.h ... _tiny_gpio I do the mmap slightly differently.

Code: Select all

int gpioInitialise(void)
{
   int fd;

   piRev = gpioHardwareRevision(); /* sets piModel and piRev */

   fd = open("/dev/gpiomem", O_RDWR | O_SYNC) ;

   if (fd < 0)
   {
      fprintf(stderr, "failed to open /dev/gpiomem\n");
      return -1;
   }

   gpioReg = (uint32_t *)mmap(NULL, 0xB4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

   close(fd);

   if (gpioReg == MAP_FAILED)
   {
      fprintf(stderr, "Bad, mmap failed\n");
      return -1;
   }
   return 0;
}
At the back of my mind I seem to remember the peripherals returning an abbreviation of their name, e.g. gpio = 0x6770696f, but for the life of me I can't remember the circumstances.

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Wed Jun 01, 2016 5:42 pm
by JGPIO
Well done. Passing 0 to the offset parameter to mmap, allows Java to provide the full correct register value.

Many, many thanks to you.

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Wed Jun 01, 2016 7:55 pm
by mattlewis
That is strange, it would indeed appear to work when accessing /dev/mem with zero offset. Next Java question - how do you then access the GPIO registers given the large offset...

Matt

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Wed Jun 01, 2016 8:01 pm
by JGPIO
The address passed to the NewDirectByteBuffer doesn't have to be the start of the mapped memory - you can add the offset and pass that in, but it's not working right for me at the moment.

Other questions arise.

I can't yet find any documentation explaining why a zero offset to mmap works.

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Wed Jun 01, 2016 8:02 pm
by joan
mattlewis wrote:That is strange, it would indeed appear to work when accessing /dev/mem with zero offset. Next Java question - how do you then access the GPIO registers given the large offset...

Matt
Passing 0 should only work for /dev/gpiomem. The driver is a special case and always returns a pointer to the base address of the GPIO peripheral.

For /dev/mem you need to pass the base address of the peripheral.

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Wed Jun 01, 2016 9:24 pm
by mattlewis
Yes, I was hoping that /dev/gpiomem would work with a 0 offset however it gives the same strange output as when passing the offset value and using /dev/mem. Annoying

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Fri Jun 10, 2016 10:57 pm
by JGPIO
Solved it!

Having created a test-bed in isolation, and making it so that I could configure every relevant parameter to mmap and NewDirectByteBuffer, I determined that a 0 offset to mmap does not have any effect on this problem at all.

I resorted to studying the code of the DirectByteBuffer class but this didn't produce any real possible solutions, although I did come to realise that the class itself uses a JNI call (albeit directly into the JVM presumably), and therefore this approach would unlikely be faster.

The solution to the problem is two fold:

1) Use native byte ordering: buf.order(ByteOrder.nativeOrder());
2) Access the ByteBuffer as an IntBuffer: IntBuffer intbuf = buf.asIntBuffer();

Why it's necessary to use a IntBuffer is not yet clear (the getInt() methods of ByteBuffer should suffice).

I can now reliably control the GPIO pins using a ByteBuffer returned by NewDirectByteBuffer.

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Fri Jun 10, 2016 11:07 pm
by JGPIO
I was wrong!

Initial speed tests show my B+ performs a set/clr loop using a Java ByteBuffer at just short of 5Mhz!

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Sat Jun 11, 2016 6:19 am
by mattlewis
I'm impressed with your perseverance, nice work!

Do you have plans for a high performance native Java device library? I'd be happy to help if so.

Matt

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Sat Jun 11, 2016 8:02 am
by JGPIO
Thanks!

I merely need to retrofit my existing work with the ByteBuffer solution. Should take less than an hour.

I'm guessing you would like a copy?

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Sat Jun 11, 2016 10:32 am
by mattlewis
That would be great thanks

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Sat Jun 11, 2016 12:01 pm
by JGPIO
14Mhz+? Surely not?

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Sat Jun 11, 2016 2:03 pm
by mattlewis
That's awesome. For my wrapper library diozero I'd like to provide both a maximum compatibility provider to support multiple devices as well as an ultra high performance Raspberry Pi specific one that has minimal dependencies. I was hoping JDK Device I/O would provide the former but seems that may not be the case. I'm currently using my thin pigpio JNI wrapper for the latter but it would be nice to implement more if it in Java to reduce other dependencies plus also be good to show that it can be done.
Matt

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Mon Jun 13, 2016 9:56 pm
by mattlewis
I've updated my mmap test project with basic GPIO control via /dev/gpiomem. My test app turns a GPIO on then off @ 11MHz on a Pi3, not too shabby :) Hats off to JGPIO for the IntBuffer trick.
As it uses /dev/gpiomem it can be run without sudo.
Code is here: https://github.com/mattjlewis/diozero/t ... mmap-tests.

Matt

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Tue Sep 06, 2016 7:26 pm
by JGPIO
Sorry it's been so long.

My Java code on my B+ will run a set/clr loop at 11MHz+, and when overclocked to Medium it will run at 15MHz+. My PI3 runs the same code at 50MHz+

I need to obtain independent verification of this.

Re: Java access to /dev/gpiomem via JNI NewDirectByteBuffer

Posted: Fri Sep 09, 2016 6:18 am
by mattlewis
Are you able to share the code?
I have a cheap USB scope, happy to check what that detects.
Matt