Search code examples
androidcembeddedjava-native-interfacegpio

How to open GPIO chip on Android?


Trying to access GPIO lines on an embedded system running Android. Using the GPIO character device interface (not the sysfs which is obsolete and does not seem to be on the target system). Writing in C (with JNI wrapper) since I assume can't do in Java.

PROBLEM: Call to open() always fails (returns -1)

On an emulator errno is ENOENT (not found) which makes sense since surely it does not have GPIO setup. On my board I get EACCESS (access denied) for a path I know does exist. Further, I get ENOENT for a path that is known not to exist. So, it seems that open finds a path and then denies access.

QUESTION: Is GPIO possible in Android? Or is Android locked so tight that this is not possible? And if possible, how do I do it?

The C code with JNI wrapping:

JNIEXPORT jstring JNICALL Java_com_example_test_1jni_MainActivity_openGpioChip(JNIEnv* env, jobject _this, jstring path) {
    const char* pathStr = (*env)->GetStringUTFChars(env, path, NULL);
    int file = open(pathStr, O_RDWR | O_CLOEXEC);
    const char* message[265];
    if (file < 0) {
        const char* errmsg = "??";
        switch (errno) {
            case ENOENT: errmsg = "ENOENT (not found)"; break;
            case EACCES: errmsg = "EACCES (access denied)"; break;
        }
        sprintf(message, "open failed - errno: %d %s", errno, errmsg);
    } else {
        sprintf(message, "open succeeded");
    }
    return (*env)->NewStringUTF(env, message);
}

Here's the Java code that calls the JNI function.

private void testForSO() {
    String resultMessage1 = openGpioChip("/dev/gpiochip0");
    String resultMessage2 = openGpioChip("/dev/notthere");
    Log.d("test", resultMessage1);
    Log.d("test", resultMessage2);
}

Via a terminal to the device, I need root priv to access /dev/gpiochip0 (like to cat). That's odd since I read that GPIO character access is 'user mode'. That means it shouldn't require root access, right?

I set file perm to 777 for /dev and /dev/gpiochip0. But that didn't change anything.

This project used to use Android Things for I/O ... but that's gone now. We're trying to update it to latest Android which means no Things.

I find very little information about using GPIO character device. There is much more on the now-obsolete tech: sysfs.

I've looked at gpiod. It's rather large and it's confusing that it was re-written recently and most docs are for v1; not v2. Anyway, it uses this open command "open(path, O_RDWR | O_CLOEXEC)" in both v1 and v2.

The gpiod tools are not on the target SOM. I don't know how I would get them on there. And I don't think that would help me.

  • Android version: 12
  • SDK (compile,min,target): 31
  • target system: Variscite IMX8

Solution

  • After much research, experimentation and a hint from Variscite I have a workaround. Do both of the following:

    Set permissive SELinux mode: setenforce 0

    Set file perms on GPIO chip paths: chmod +rw /dev/gpiochip*

    I actually guessed at the chmod some weeks ago, but since I didn't know that I also have to do setenforce I didn't realize the chmod was actually part of the answer.

    This a workaround since both things reset at startup. So, I still need to find a way to setup this stuff for a production system.