Skip to content

interrupts are not passed to userspace (e.g. poll()) when an interrupts occurs on a gpio pin on a raspberry pi 4 #5159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
folkertvanheusden opened this issue Sep 1, 2022 · 15 comments

Comments

@folkertvanheusden
Copy link
Contributor

Describe the bug

If I run this on an rpi 1 or 2, then I see interrupts come in (the printf is invoked). On at least an rpi 3, I see no interrupts.

Steps to reproduce the behaviour

note that this example requires libpigpio-dev:

#include <pigpio.h>
#include <stdio.h>
#include <unistd.h>

void func(int gpio_n, int level, uint32_t tick, void *modemptr)
{
        printf("%d %d %d\n", gpio_n, level, tick);
}

int main(int argc, char *argv[])
{
        int gpio_n = 2;
        gpioInitialise();
    gpioSetMode(gpio_n, PI_INPUT);
    gpioSetISRFuncEx(gpio_n, EITHER_EDGE, 0, func, NULL);

    for(;;)
            sleep(1);

    return 0;
}

Device (s)

Raspberry Pi 4 Mod. B

System

Raspberry Pi reference 2022-01-28
Generated using pi-gen, https://github.com/RPi-Distro/pi-gen, f01430c9d8f67a4b9719cc00e74a2079d3834d5d, stage2

Aug 9 2022 13:46:06
Copyright (c) 2012 Broadcom
version 273b410636cf8854ca35af91fd738a3d5f8b39b6 (clean) (release) (start)

5.15.56-v7+

Logs

No response

Additional context

No response

@folkertvanheusden folkertvanheusden changed the title poll() on /sys/class/gpioXX/value does not trigger when an interrupt occurs on raspberry pi 4 interrupts are not passed to userspace (e.g. poll()) when an interrupts occurs on a gpio pin on a raspberry pi 4 Sep 1, 2022
@lategoodbye
Copy link
Contributor

@folkertvanheusden Did you already contacted the author of libpigpio-dev before?

@folkertvanheusden
Copy link
Contributor Author

folkertvanheusden commented Sep 4, 2022

@lategoodbye I did. And I verified with strace that it may not be related to libpigpio e.g. it performs the right steps (set input to in, set trigger to edge, perform poll() on fd).

@lategoodbye
Copy link
Contributor

lategoodbye commented Sep 4, 2022

I'm not expert in libpigpio. How does it interact with the GPIOs (SOC register level or via /sys/class/gpio)?
Are other GPIOs affected not just GPIO 2?
How do you trigger the interrupt?
Did it worked with an older kernel version?

@folkertvanheusden
Copy link
Contributor Author

folkertvanheusden commented Sep 4, 2022

@lategoodbye via /sys/class/gpio

(I'm not an expert in libpigpio either but I dug a bit in it)

It's also with other GPIOs. I also checked one without pull-up.

I trigger the interrupt via hardware (a switch connected to the gpio pin).

Older kernel: I'll see if I can check that out. I'll keep you posted.

@folkertvanheusden
Copy link
Contributor Author

folkertvanheusden commented Sep 4, 2022

GPIO 2

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <poll.h>

int main(int argc, char *argv[])
{
	char str[256];
	struct timeval tv;
	struct pollfd pfd;
	int fd, gpio;
	char buf[8];

	system("echo 2    > /sys/class/gpio/export");
	system("echo in   > /sys/class/gpio/gpio2/direction");
	system("echo both > /sys/class/gpio/gpio2/edge");

	gpio = 2;

	sprintf(str, "/sys/class/gpio/gpio%d/value", gpio);

	if ((fd = open(str, O_RDONLY)) < 0)
	{
		fprintf(stderr, "Failed, gpio %d not exported.\n", gpio);
		exit(1);
	}

	pfd.fd = fd;

	pfd.events = POLLPRI | POLLERR;

	lseek(fd, 0, SEEK_SET);    /* consume any prior interrupt */
	read(fd, buf, sizeof buf);

	poll(&pfd, 1, -1);         /* wait for interrupt */

	lseek(fd, 0, SEEK_SET);    /* consume interrupt */
	read(fd, buf, sizeof buf);

	exit(0);
}

This works fine on "4.19.75-v7l+ # 1270 SMP Tue Sep 24 18:51:41 BST 2019".

It does NOT work on "5.15.61-v7l+ # 1579 SMP Fri Aug 26 11:13:03 BST 2022".

@lategoodbye
Copy link
Contributor

lategoodbye commented Sep 4, 2022

Just to make it clear:

Kernel 4.19.75: Rpi 1, 2, 3 & 4 works
Kernel 5.15.61: Rpi 1 & 2 works, but 3 & 4 doesn't

correct?

@folkertvanheusden
Copy link
Contributor Author

Just to make it clear:

Kernel 4.19.75: Rpi 1, 2, 3 & 4 works
Kernel 5.15.61: Rpi 1 & 2 works, but 3 & 4 doesn't

correct?

Yes!

@folkertvanheusden
Copy link
Contributor Author

Well, that's not accurate: 4.19.75 has been tested on my rpi4 and there it works; i have not tested it on an rpi1.

@folkertvanheusden
Copy link
Contributor Author

Hi,

Update:

  • 5.15.32-v7l+ # 1538 SMP Thu Mar 31 19:39:41 BST 2022 **works fine on an RPI4 **

So somewhere between 5.15.32 and 5.15.61 it broke.

@pelwell
Copy link
Contributor

pelwell commented Sep 5, 2022

It's working fine for me on a Pi 4 running 5.15.61.

I've modified the test program so you can pass in a GPIO number, but that doesn't change the fundamental behaviour - your version also works for me. In the spirit of test code it is not defensively written, but it does avoid the I/O error caused by re-exporting the same GPIO.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <poll.h>

int main(int argc, char *argv[])
{
	char str[256];
	struct stat statbuf;
	struct pollfd pfd;
	int fd, gpio;
	char buf[8];

	gpio = 2;

	if (argc > 1)
		gpio = atoi(argv[1]);

	sprintf(str, "/sys/class/gpio/gpio%d/direction", gpio);
	if (stat(str, &statbuf) != 0)
	{
		sprintf(str, "echo %d    > /sys/class/gpio/export", gpio);
		system(str);
	}
	sprintf(str, "echo in   > /sys/class/gpio/gpio%d/direction", gpio);
	system(str);
	sprintf(str, "echo both > /sys/class/gpio/gpio%d/edge", gpio);
	system(str);

	sprintf(str, "/sys/class/gpio/gpio%d/value", gpio);

	if ((fd = open(str, O_RDONLY)) < 0)
	{
		fprintf(stderr, "Failed, gpio %d not exported.\n", gpio);
		exit(1);
	}

	pfd.fd = fd;

	pfd.events = POLLPRI | POLLERR;

	lseek(fd, 0, SEEK_SET);    /* consume any prior interrupt */
	memset(buf, 0, sizeof(buf));
	read(fd, buf, sizeof buf);
	strtok(buf, " \r\n");

	printf("Waiting for a change on GPIO %d (currently %s)\n", gpio, buf);
	poll(&pfd, 1, -1);         /* wait for interrupt */

	lseek(fd, 0, SEEK_SET);    /* consume interrupt */
	read(fd, buf, sizeof buf);

	exit(0);
}

@folkertvanheusden
Copy link
Contributor Author

folkertvanheusden commented Sep 5, 2022

Hi @pelwell,

I've been testing all morning and started to see that sometimes it does work and sometimes it doesn't - with 5.15.56-v7+.
So I took a scoop and measured.

rpi3-001-start
rpi3-002-recv-packet
rpi3-003-after-libinit

In rpi3-001-start.png you see that after a fresh boot, the voltage on that pin (between the rpi and the tranceiver) is 0V.
When a packet comes in (rpi3-002-recv-packet.png), the pin is pulled up to 3.28V.
Now it gets strange: when I reset and re-initialised the tranceiver and reconfigured the rpi-part, the voltage suddenly goes to 0.84V! If then another packet comes in, the voltage again goes to 3.28V but the rpi doesn't "see" this.

So what I'm thinking now:

  • either the batch of tranceivers I've got is buggy causing that 0.84V voltage - but why isn't that a problem with at least the rpi1? (I noticed suddenly that I was using the rpi2 this morning so that one is problematic as well: only the rpi1 seems to be rock solid).
  • and/or the pin is set to floating somewhere?

@pelwell
Copy link
Contributor

pelwell commented Sep 5, 2022

GPIO 2 has an external hardware pull-up resistor - it's intended for I2C use - so it will not float. You can use raspi-gpio get 2 (or sudo grep -w 2 /sys/kernel/debug/gpio) to get a snapshot of the actual pin level.

For the record, I repeated the test on the latest BRANCH=stable build - due to appear in a new RPiOS image in the near future - and it works as expected:

raspberrypi$ uname -a
Linux raspberrypi 5.15.61-v7l+ #1579 SMP Fri Aug 26 11:13:03 BST 2022 armv7l GNU/Linux
pi@raspberrypi:~ $ sudo ./polltest
Waiting for a change on GPIO 2 (currently 1)
pi@raspberrypi:~ $ 

@folkertvanheusden
Copy link
Contributor Author

GPIO 2 has an external hardware pull-up resistor - it's intended for I2C use - so it will not float. You can use raspi-gpio get 2 (or sudo grep -w 2 /sys/kernel/debug/gpio) to get a snapshot of the actual pin level.

Hmmm I did not know that. Means I have to retest as with the scope I was using gpio 22 instead.

Do you know if I can set gpio 22 to pull down explicitly? Not via /sys/class/gpio it looks like?

@pelwell
Copy link
Contributor

pelwell commented Sep 5, 2022

raspi-gpio set 22 ip pd will make it an input, enabling the pull-down resistor. ... pu enables the pull-up instead, and ... pn disables the pulls.

@folkertvanheusden
Copy link
Contributor Author

F.w.i.w.:

  • the tranceiver I work with pulls a 'DIO0'-pin high when it receives a packet
  • when you reset its IRQ-register, the DIO0 goes down

Now it looks like if the library I use for that tranceiver does not reset that IRQ-flag before (re-)setting the tranceiver to receive and the gpio-pin to input, that the voltage stays at 0.84V for some reason.
This needs further testing from my side, but it looks promising.

I think I can conclude this is a problem with that library and not neccessarily with the kernel(?) so I suggest we can close this issue?

@pelwell pelwell closed this as completed Sep 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants