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