libgpiod freezes board

I’m using libgpiod v1.6.x on the Beagleboard Black (Debian Bullseye IoT Image 2023-09-02, 5.10.168-ti-r71) to poll gpio lines for edge events from some peripherals (ICM20948 and SX1276). However, it seems that some GPIO pins cannot be used and make the board crash. I am currently using:
P9.12 for ICM20948 and P8.41 through P8.46 for SX1276. Pin P9.12 always works, but the pins on P8 cause crashes sometimes. In particular, pins P8.41 and P8.42 can cause the crash. Previously I tried this setup but using pins P8.11,12,14,15,16,17 with the same result: often pins P8.15 and 16 would cause a crash.

In /boot/uEnv.txt I have disabled video, audio, wireless and adc.

The code I use is:

static int open_chips_and_lines(struct gpiod_chip** chips, struct gpiod_line** lines){
    int num_chips = 0;
    int ret = 0;
    uint8_t chip_opened_bits = 0;
    uint32_t line_count[GPIO_CHIPS] = {0};
    for(int i = 0; i < TOTAL_INTERRUPT_LINES; i++){
        uint32_t pin = INTC_PINS[i];
        uint32_t chip = GET_GPIO_CHIP(pin);
        uint32_t line = GET_GPIO_LINE(pin);
        uint32_t edge = INTC_EDGES[i];

        BBBLOG(debug, 1, NULL, "Opening Chip%u/Line%u:\n", chip, line);
        if(!((chip_opened_bits & (1 << chip)) >> chip)){
            chips[chip] = gpiod_chip_open_by_number(chip);
            BBBLOG(error, !chips[chip], return -1, "Open Chip %u failed\n", chip);
            num_chips++;
        }

        BBBLOG(debug, 1, NULL, "Requesting Line\n");
        lines[i] = gpiod_chip_get_line(chips[chip], line);
        BBBLOG(error, !lines[i], return -1, "Open Line Chip%u/Line%u failed\n", chip, line);

        BBBLOG(debug, 1, NULL, "Requesting edge events\n");
        if(edge == GPIOD_LINE_EVENT_RISING_EDGE){
            ret = gpiod_line_request_rising_edge_events(lines[i], CONSUMER);
        } else {
            ret = gpiod_line_request_falling_edge_events(lines[i], CONSUMER);
        }
        BBBLOG(error, ret < 0, return -1, "Requesting line events for Chip%u/Line%u failed\n", chip, line);

        line_count[chip]++;
        usleep(1000000);
    }
    return num_chips;
}

where INTC_PINS and INTC_EDGES are arrays given at compile-time containing the pins and edge types to monitor. The board freezes when requesting edge events (gpiod_line_request_rising_edge_events) and needs to be reset using the reset button.

Am I simply using libgpiod wrong, or are there hardware limitations I am not aware of?

Edit: a simple working example is found below in this comment of mine.

1 Like

Hello,

How did you install it?

  1. sudo apt install libgpiod-dev?

or…

  1. Installing it yourself?

Seth

P.S. I had some issues with the git repo and installing it to make it work on the BBB once a while back. I use the stable revision they have in the Bullseye repos or Bookworm repos.

There is a book called the BeagleBone-Cookbook you can find online which will be at docs.beagleboard.org and there are ideas about gpiod and libgpiod-dev that may help too.

1 Like

How did you install it?

First through sudo apt install libgpiod-dev, but I encountered this freezing issue and thought maybe upgrading to the latest version manually it would be solved, but the same problem persisted after purging libgpiod 1.6.x and installing 2.0.x manually. I now reverted back to 1.6.x again by installing though apt.

I noticed that when I add some delays between opening the chip, requesting the line and requesting edge events, and all the lines are on the same chip, that it sometimes works, but not always which is weird. For example I used P8.40-46 (all on gpiochip2) and added some usleep statements and it worked a few times.

Here is another example that sometimes works, but mostly doesn’t:

#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

volatile int RUN = 1;

void inthandler(int _){RUN = 0;}

#define CONSUMER        "Consumer"

int main(int argc, char **argv)
{

        signal(SIGINT, inthandler);

        int chipnumber = 1;
        struct timespec ts = { 0, 1000000 };    // 1s Timeout for event
        struct gpiod_line_event event;
        #define NUMPINS (7)
        unsigned int pins[NUMPINS] = {60,61,44,45,46,47,48};
        unsigned int val;
        struct gpiod_chip *chip;
        struct gpiod_line *lines[NUMPINS] = {NULL};
        int i, ret;

        chip = gpiod_chip_open_by_number(chipnumber);
        if (!chip) {
                perror("Open chip failed\n");
                goto end;
        }
        printf("Opened Chip %u\n", chipnumber);
        usleep(10000);

        for(int j = 0; j < NUMPINS; j++){
                pins[j] = pins[j] % 32;
                lines[j] = gpiod_chip_get_line(chip, pins[j]);
                if (!lines[j]) {
                        printf("Get line %u failed\n", pins[j]);
                        goto close_chip;
                }
                printf("Opened Line %u\n", pins[j]);
                usleep(10000);
                ret = gpiod_line_request_rising_edge_events(lines[j], CONSUMER);
                if (ret < 0) {
                        printf("Request line %u as input failed\n", pins[j]);
                        goto release_line;
                }
                printf("Requested Edges for Line %u\n", pins[j]);
                usleep(10000);
        }
        /* Get */
        printf("Starting Monitor:\n");
        while(RUN) {

                    //printf("waiting...\n");
                    for(int j = 0; j < NUMPINS; j++){
                        ret = gpiod_line_event_wait(lines[j], &ts);
                        if(ret > 0){
                                ret = gpiod_line_event_read(lines[j], &event);
                                printf("ret: %d, event: %d\n", ret, event);
                                if (!ret){
                                        printf("event: %d timestamp: [%8ld.%09ld]\n",
                                        event.event_type, event.ts.tv_sec, event.ts.tv_nsec);
                                }
                                val = gpiod_line_get_value(lines[j]);
                                printf("%d\n", val);
                                usleep(1000);
                        }
                    }
        }

release_line:
        printf("Releasing lines\n");
        for(int j = 0; j < NUMPINS; j++){
                if(lines[j])
                        gpiod_line_release(lines[j]);
        }
close_chip:
        printf("Closing chip %u\n", chipnumber);
        gpiod_chip_close(chip);
end:
        return 0;
}

compile with gcc gpiod_tst.c -o gpiod_tst -lgpiod .
Sometimes it gets to the monitoring loop, but often it gets stuck requesting a line or edge events.

Out of curiosity have you tried just grabbing the lines and not requesting the edge events ?
For the board to freeze/lock up I would assume something wrong with the interrupt handling…

You don’t say what version OS/kernel you are runninng.

Can you put a tail on the syslog in the serial console ?

You don’t say what version OS/kernel you are runninng.

Im running Debian 11 Bullseye IoT image 5.10.168-ti-r71.

Out of curiosity have you tried just grabbing the lines and not requesting the edge events ?

I actually hadnt tried hat yet. I tried just now and it indeed ran without problems, but I am a bit weary of assuming it works now since many times the program has worked a few times before again ending up in freezes.

Can you put a tail on the syslog in the serial console ?

I’m sorry, what exactly do you mean by this?

assuming you have the serial console connected, login as root and run

tail -f /var/log/syslog

Not that is will probably show much, but who knows.

I suspect the issue is with interrupts. I don’t ever recall using edge events with libgpiod however.

Are any of the pins floating ?
If you only request events on a single pin does it work ?
Is it one pin in particular that causes the issue, or is it enabling events for multiple pins ?

assuming you have the serial console connected, login as root and run tail -f /var/log/syslog

I SSH into the device and whenever it freezes I cannot connect anymore. I retried to run the program with requesting edge events (and it froze). There doesnt seem to be anything related to gpio; Syslog shows the following:

...
Jan 11 15:59:56 BeagleBone systemd[1]: Created slice User Slice of UID 1000.
Jan 11 15:59:56 BeagleBone systemd[1]: Starting User Runtime Directory /run/user/1000...
Jan 11 15:59:56 BeagleBone systemd[1]: Finished User Runtime Directory /run/user/1000.
Jan 11 15:59:56 BeagleBone systemd[1]: Starting User Manager for UID 1000...
Jan 11 15:59:58 BeagleBone systemd[1086]: Queued start job for default target Main User Target.
Jan 11 15:59:58 BeagleBone systemd[1086]: Created slice User Application Slice.
Jan 11 15:59:58 BeagleBone systemd[1086]: Reached target Paths.
Jan 11 15:59:58 BeagleBone systemd[1086]: Reached target Timers.
Jan 11 15:59:58 BeagleBone systemd[1086]: Listening on GnuPG network certificate management daemon.
Jan 11 15:59:58 BeagleBone systemd[1086]: Listening on GnuPG cryptographic agent and passphrase cache (access for web browsers).
Jan 11 15:59:58 BeagleBone systemd[1086]: Listening on GnuPG cryptographic agent and passphrase cache (restricted).
Jan 11 15:59:58 BeagleBone systemd[1086]: Listening on GnuPG cryptographic agent (ssh-agent emulation).
Jan 11 15:59:58 BeagleBone systemd[1086]: Listening on GnuPG cryptographic agent and passphrase cache.
Jan 11 15:59:58 BeagleBone systemd[1086]: Reached target Sockets.
Jan 11 15:59:58 BeagleBone systemd[1086]: Reached target Basic System.
Jan 11 15:59:58 BeagleBone systemd[1]: Started User Manager for UID 1000.
Jan 11 15:59:58 BeagleBone systemd[1086]: Reached target Main User Target.
Jan 11 15:59:58 BeagleBone systemd[1086]: Startup finished in 1.438s.
Jan 11 15:59:58 BeagleBone systemd[1]: Started Session 1 of user debian.

Are any of the pins floating ?

All pins are connected to the required peripherals.

If you only request events on a single pin does it work ?

Usually yes. From what I’ve seen, it usually fails on either the 2nd pin I request, or the 2nd-to last or last.

Is it one pin in particular that causes the issue, or is it enabling events for multiple pins ?

I thought it was a particular pin, and some pins do seem t cause this issue more than others. For example, GPIO61 and GPIO49 have usually been the point where the freeze occurs, but it doesnt always happen.

Leave your ttl to usb adapter connected and go in that way next time it is crashed. It could still be running, just that something has killed networking.

Leave your ttl to usb adapter connected and go in that way next time it is crashed. It could still be running, just that something has killed networking.

I’ve tried this, but to no avail; everything is frozen and I cannot see anything or send any commands

Is it one pin in particular that causes the issue, or is it enabling events for multiple pins ?

After some fiddling around, I’ve found a set of pins that seemingly works. Why other pins don’t work, I can;t tell you. With the below program, I manually tried some pins and found the (non-exhaustive) set of 8 pins that works to be:
P8.11 ; P8.12 ; P8.14 ; P8.15 ; P8.16 ; P8.17 ; P9.41

And the (non-exhaustive) set of pins that will cause a freeze 99% of the time:
P8.26 ; P8.18 ; P9.23

Again, if someone could tell me why this happens I’d be very grateful. The code I used to run the tests is below.

Usage: ./program CHIPNUM0 NPINS PIN1 PIN2 … PINN CHIPNUM1 NPINS …
i.e. CHip number followed by # of pins on that chip followed by list of offsets, then this pattern can be repeated for other chips. Not the most use friendly but needed something quick and dirty to test this bad-pin theory.

#include <gpiod.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

volatile int RUN = 1;

void inthandler(int _){RUN = 0;}

#define CONSUMER        "Consumer"

#define MAXPINS (32)

int main(int argc, char **argv)
{

    signal(SIGINT, inthandler);


    struct timespec ts = { 0, 1000000 };    // 1s Timeout for event
    struct gpiod_line_event event;
    unsigned int chipnum = 0;
    unsigned int numpins = 0;
    unsigned int pins[4][MAXPINS] = {0};
    char tmp[32] = {0};

    unsigned int val;
    struct gpiod_chip *chip[4] = {NULL};
    struct gpiod_line *lines[4][MAXPINS] = {NULL};
    int ret = 0;
    int i = 1;
    while(i < argc){
        chipnum = strtoul(argv[i], &tmp, 10); i++;

        chip[chipnum] = gpiod_chip_open_by_number(chipnum);
        if (!chip[chipnum]) {
            perror("Open chip failed\n");
            goto release_line;
        }
        printf("Opened Chip %u\n", chipnum);
        usleep(10000);

        numpins = strtoul(argv[i], &tmp, 10); i++;
        for(int j = 0; j < numpins; j++){
            pins[chipnum][j] = strtoul(argv[i+j], &tmp, 10);
            lines[chipnum][j] = gpiod_chip_get_line(chip[chipnum], pins[chipnum][j]);
            if (!lines[chipnum][j]) {
                printf("Get line %u failed\n", pins[chipnum][j]);
                goto release_line;
            }
            printf("Opened Line %u\n", pins[chipnum][j]);
            usleep(10000);

            ret = gpiod_line_request_rising_edge_events(lines[chipnum][j], CONSUMER);
            if (ret < 0) {
                printf("Request edge events for line %u failed\n", pins[chipnum][j]);
                goto release_line;
            }
            printf("Requested Edges for Line %u\n", pins[chipnum][j]);
            usleep(10000);
        }
        i += numpins;
    }

    /* Get */
    printf("Starting Monitor:\n");
    while(RUN) {

        for(i = 0; i < 4; i++){
            if(chip[i] != NULL) {
                for(int j = 0; j < MAXPINS; j++){
                    if(lines[i][j]){
                        ret = gpiod_line_event_wait(lines[i][j], &ts);
                        if(ret > 0){
                            ret = gpiod_line_event_read(lines[i][j], &event);
                            printf("ret: %d, event: %d\n", ret, event);
                            if (!ret){
                                printf("event: %d timestamp: [%8ld.%09ld]\n",
                                       event.event_type, event.ts.tv_sec, event.ts.tv_nsec);
                            }
                            val = gpiod_line_get_value(lines[i][j]);
                            printf("%d\n", val);
                            usleep(1000);
                        }
                    }
                }
            }
        }
    }

    release_line:
    printf("Releasing lines\n");
    for(i = 0; i < 4; i++){
        if(chip[i] != NULL) {
            for(int j = 0; j < MAXPINS; j++){
                if(lines[i][j]){
                    gpiod_line_release(lines[i][j]);
                }
            }
            printf("Closing chip %d\n", i);
            gpiod_chip_close(chip[i]);
        }
    }

    return 0;
}

Seems like you might have a device tree issue.

Post the device tree file(s) you are using, I am not an expert on device trees however, with a little bit more info others on here will be able to help you out.

When you have a conflict you can have an issue like what your experiencing. If you bought the board new its fairly safe to assume the pins have not been blown out.

Also, add some more error messages when functions fail. Be sure to clearly identify what function called did not work. Add some printf statements for other stuff that would help you to debug.