Accessing /dev/mem through Java

Viewed 93

I'm trying to use Raspberry PI's PWM from Java, without using WiringPi/Pi4j or any other library. So far I learned that the only way to control the PWMs is by memory-mapping the /dev/mem file and setting appropriate registers there. That's how WiringPi does it (see here) and also people ask about it pretty often on the Raspberry Pi StackExchange.

I'm facing a certain problem that seems to be irrelevant to the Raspberry, that's why I'm asking the question on the general SO forum. I tested the below symptom positive on my Ubuntu laptop.

So, I'm trying with this simple piece of code with seems to be a minimal example that reproduces the problem, runnnig with sudo:

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class Main {
    public static void main(String[] args) {
        try {
            RandomAccessFile file = new RandomAccessFile("/dev/mem", "rw");
            long size = 12345; // Anything greater than 0 - it seems to be irrelevant.
            MappedByteBuffer out = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, size);

            // Here I plan to make some writes to the registers.
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

I'm not even trying to read any data or change the internal pointer position. I'm getting the following stack trace, originating from the call to map(...):

java.io.IOException: Invalid argument
    at java.base/sun.nio.ch.FileDispatcherImpl.truncate0(Native Method)
    at java.base/sun.nio.ch.FileDispatcherImpl.truncate(FileDispatcherImpl.java:86)
    at java.base/sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:987)
    at Main.main(Main.java:10)

When debugging the code, I noticed this piece in the implementation of map(...) (in sun/nio/ch/FileChannelImpl.java):

filesize = nd.size(fd);

and the file size is calculated to hava a size of 0. I don't know how to debug deeper because it seems to be already a native call, through JNI.

Checking the size of this file through the command-line reveals 1:

$ ll | grep mem
crw-r-----   1 root  kmem        1,   1 lip  2 13:12 mem

I checked the man page: https://man7.org/linux/man-pages/man4/mem.4.html, but it doesn't seem to address this specific prolem.

It got more interesting when I found out that the JDK version matters. It doesn't work on 11.0.1 from Oracle, and OpenJDK 14.0.1. However, it does work (doesn't crash) on 8.0.191 from Oracle. I have some problems with attaching sources of this JDK to my debugger, so I cannot debug it as deeply as with the other version. I also cannot use JDK 8 on my Raspberry, I have some problems installing it. I figured it's better to understand the problem better rather than sticking to a single JDK.

What am I doing wrong? It seems to be so easy to map /dev/mem with C/C++ or Python, I see the examples all around the RPI forum. I'd really like to try to stay pure Java as long as possible. In theory there's also a risk of a bug in the newer JDKs, but I want to get some second opinion before I file a bug, especially that I'm not proficient with low-level Linux.

Thanks, Peter

0 Answers
Related