When trying to read input_events from /dev/input/event16
I noticed that the size of the buffer I'm reading into may cause an exception. Here is the code I wrote:
public static void main(String[] args) throws IOException{
FileInputStream is = new FileInputStream("/dev/input/event16");
byte[] three_bytes = new byte[3];
byte[] twentyfour_bytes = new byte[24];
is.read(three_bytes); // fails
is.read(twentyfour_bytes); // does not fail
}
My initial experiments suggest that the buffer needs capacity for at least one full input_event
struct. But I could not find out why.
The problem is the line is.read(three_bytes);
causes the follwoing exception:
Exception in thread "main" java.io.IOException: Invalid argument
at java.base/java.io.FileInputStream.readBytes(Native Method)
at java.base/java.io.FileInputStream.read(FileInputStream.java:249)
at main.Test.main(Test.java:11)
I would like to figure out why the line is.read(three_bytes);
throws the exception while is.read(twentyfour_bytes);
reads the data as expected
I would like to figure out why the line
is.read(three_bytes);
throws the exception whileis.read(twentyfour_bytes);
reads the data as expected.
For a start, /dev/input/event16
is not a regular file. It is a device file, and device files often don't behave like regular files.
In this case, the /dev/input/event*
device files are for reading events from input devices. When you perform a read
syscall on them, they will return one or more complete events. These are binary data whose format is given by the following C struct
:
struct input_event {
struct timeval time;
unsigned short type;
unsigned short code;
unsigned int value;
};
The size of that struct is (presumably) 24 bytes on a typical 64bit Linux system, though.
My initial experiments suggest that the buffer needs capacity for at least one full
input_event
struct. But I could not find out why.
The behavior of the event devices is documented in Linux Input drivers v1.0, section 5. It states that a read
will always give a whole number of input_event
structs.
It follows that the read
syscall provides a buffer that is less than sizeof(input_event)
, the kernel cannot return anything. Apparently, this causes the read
syscall to fail with errno value EINVAL
. (IMO, this is a reasonable design choice, and consistent with the documented meaning of EINVAL
.)
So, in Java, when you call read(three_bytes)
, that will map to a read
syscall with a read size of 3 bytes which fails; see above. The syscall failure is signaled to the Java application by throw an IOException
.