Re: User Space GPIO Interrupt programming - GSoC-2018

Vladimir Goncharov <[email protected]>
Newsgroups gmane.os.freebsd.devel.arm
Message-ID <[email protected]>
Here it is.
There is struct gpioc_event with pin number and bintime which is send to
userspace.

Also I'm thinking about to implementation notofication without extra
reading from socket via struct kevent's extra fields (fflags/ext[4]),
looks like it is possible.


2020-11-27 14:09, Dr. Rolf Jansen пишет:
> Hello Vladimir,
> 
> yes please, I am interested. Please can show us your version of the
> GSoC-2018 diff file?
> 
> Best regards
> 
> Rolf
> 
>> Am 27.11.2020 um 05:02 schrieb Vladimir Goncharov <[email protected]
>> <mailto:[email protected]>>:
>>
>> Hello Rolf,
>>
>> I have implemented in kernel queue for generated GPIO interrupts with
>> timestamps, so now it's possible to catch all events even on busy
>> system. It's all for gpio_gsoc2018 code. In result I have no more
>> warnings in dmesg. Unfortunately it requires allocating memory on each
>> event to store pin number and timestamp. In case of unability allocating
>> memory it falls back to gpio_gsoc2018 method.
>> Does anybody interested in such patches?
>>
>>
>> 2020-11-27 04:18, Dr. Rolf Jansen пишет:
>>>> Am 26.11.2020 um 16:56 schrieb Ian Lepore <[email protected]
>>>> <mailto:[email protected]>>:
>>>>
>>>> On Tue, 2020-11-24 at 17:14 -0300, Dr. Rolf Jansen wrote:
>>>>> Hello
>>>>>
>>>>> Has anything of the GSoC-2018 efforts made it into the current code
>>>>> base?
>>>>>
>>>>>
>>>> https://wiki.freebsd.org/SummerOfCode2018Projects/UserSpaceGPIOinterrupts
>>>> <https://wiki.freebsd.org/SummerOfCode2018Projects/UserSpaceGPIOinterrupts>
>>>>>
>>>>> I installed the recent 13.0-CURRENT snapshot (2020-11-19) on a
>>>>> BeagleBone Black which was one of the implementation targets of said
>>>>> project, but when running the test tools, I either see cannot
>>>>> read/kevent/poll/aio_read - Operation not supported by device or
>>>>> Inappropriate ioctl for device.
>>>>>
>>>>> Perhaps I need to pull the project´s changes into the kernel by
>>>>> myself. However, before this I would like to ask whether it is worth
>>>>> the effort.
>>>>>
>>>>> Please, can anyone shed some light on this.
>>>>>
>>>>> Best regards
>>>>>
>>>>> Rolf
>>>>>
>>>>
>>>> I made some time this morning to review the gsoc2018 code.  It turns
>>>> out this code is very high quality, nearly ready to commit as-is.  The
>>>> main thing it needs is some style cleanup in its comment blocks, and
>>>> documentation.  I'd be inclined to commit the code first and write the
>>>> documentation over the next little while and commit it separately.
>>>>
>>>> If you'd like to give it a try, here's a diff that should apply and
>>>> build cleanly on freebsd 12 or 13:
>>>>
>>>> https://people.freebsd.org/~ian/gpio_gsoc2018.diff
>>>>
>>>> While there isn't any documentation yet, there is a test program (I
>>>> haven't run it yet) that demonstrates all the features:
>>>>
>>>> https://github.com/ckraemer/gsoc2018-utils/blob/master/src/gpioc_intr_test.c
>>>>
>>>> Right now the code will let you block waiting for a pin-change event
>>>> using select(), poll() or kevents, or to be notified via SIGIO, but
>>>> after being notified that something happened, you still have to call
>>>> read() to see which pin changed.  I think if the pin changes state
>>>> multiple times between calls to read(), you'll lose track of some
>>>> changes (I'm not positive of that, I don't understand the kevent stuff
>>>> well).
>>>>
>>>> I'd like to add some features so that you can configure it to track pin
>>>> changes in counting-mode and timestamping-mode.  In counting mode, when
>>>> you do a read() you would get back a pair of values, the pin number and
>>>> how many times its interrupt fired since the last read.  In
>>>> timestamping mode, every read would return a pin number and an
>>>> associated timespec giving the exact time the interrupt happened (there
>>>> would need to be a way to configure how many events it could buffer,
>>>> but I think even allowing a thousand buffered events would only use a
>>>> few kbytes of memory).
>>>
>>> I got it working as well, please see my other post from yesterday. I
>>> used gpioc_intr_test.c.
>>>
>>> I see hundreds of warning messages when I press the test button a few
>>> times. May these warnings be safely ignored. The kernel module of
>>> Oskar Holmund works quite nice as well (for what I need), and with
>>> that one, I don’t see warnings.
>>>
>>> The counting- and timestamping-mode for sure would be very useful.
>>> Perhaps by implementing this, there won’t be no unhandled interrupts
>>> anymore, and hence there won’t be any warnings either.
>>>
>>> Best regards
>>>
>>> Rolf
>>>
>>>
>> _______________________________________________
>> [email protected] <mailto:[email protected]> mailing list
>> https://lists.freebsd.org/mailman/listinfo/freebsd-arm
>> <https://lists.freebsd.org/mailman/listinfo/freebsd-arm>
>> To unsubscribe, send any mail to "[email protected]"
> 
> 
> 
> Dr. Rolf Jansen
> - -
> Rua Reginaldo de Lima, 98
> Parque São Diogo
> 09732-550, São Bernardo do Campo
> São Paulo - Brazil
> 
> Phone:0055-11/4317-0974
> Mobile:0055-11/9 8141-1465
> E-Mail:[email protected] <mailto:[email protected]>
> BLog:obsigna.com <http://obsigna.com/>
>

_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/freebsd-arm
To unsubscribe, send any mail to "[email protected]"
gpio.diff (text/plain, 23.1 KB)
diff -ru ./dev/gpio/gpiobus.c /usr/home/buildroot/usr/src/sys/dev/gpio/gpiobus.c
--- ./dev/gpio/gpiobus.c	2020-10-23 03:02:05.000000000 +0300
+++ /usr/home/buildroot/usr/src/sys/dev/gpio/gpiobus.c	2020-11-25 12:17:30.428119000 +0300
@@ -143,7 +143,15 @@
 	/* Cannot mix pull-up/pull-down together. */
 	if (flags & GPIO_PIN_PULLUP && flags & GPIO_PIN_PULLDOWN)
 		return (EINVAL);
-
+	/* Cannot mix output and interrupt flags together */
+	if (flags & GPIO_PIN_OUTPUT && flags & GPIO_INTR_MASK)
+		return (EINVAL);
+	/* Only one interrupt flag can be defined at once */
+	if ((flags & GPIO_INTR_MASK) & ((flags & GPIO_INTR_MASK) - 1))
+		return (EINVAL);
+	/* The interrupt attached flag cannot be set */
+	if (flags & GPIO_INTR_ATTACHED)
+ 		return (EINVAL);
 	return (0);
 }
 
Only in /usr/home/buildroot/usr/src/sys/dev/gpio: gpiobus.c.orig
diff -ru ./dev/gpio/gpioc.c /usr/home/buildroot/usr/src/sys/dev/gpio/gpioc.c
--- ./dev/gpio/gpioc.c	2020-10-23 03:02:05.000000000 +0300
+++ /usr/home/buildroot/usr/src/sys/dev/gpio/gpioc.c	2020-11-27 03:19:04.628872000 +0300
@@ -35,8 +35,15 @@
 #include <sys/conf.h>
 #include <sys/gpio.h>
 #include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/sigio.h>
+#include <sys/signalvar.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
 #include <sys/module.h>
 
 #include <dev/gpio/gpiobusvar.h>
@@ -44,40 +51,433 @@
 #include "gpio_if.h"
 #include "gpiobus_if.h"
 
-#undef GPIOC_DEBUG
+#define GPIOC_DEBUG 1
 #ifdef GPIOC_DEBUG
 #define dprintf printf
 #else
 #define dprintf(x, arg...)
 #endif
 
+struct gpioc_softc {
+	device_t		sc_dev;		/* gpiocX dev */
+	device_t		sc_pdev;	/* gpioX dev */
+	struct cdev		*sc_ctl_dev;	/* controller device */
+	int			sc_unit;
+	int			sc_npins;
+	struct gpioc_pin_intr	*sc_pin_intr;
+};
+
+struct gpioc_pin_intr {
+	struct gpioc_softc				*sc;
+	gpio_pin_t					pin;
+	bool						config_locked;
+	int						intr_rid;
+	struct resource					*intr_res;
+	void						*intr_cookie;
+	struct mtx					mtx;
+	SLIST_HEAD(gpioc_privs_list, gpioc_privs)	privs;
+};
+
+struct gpioc_event {
+	uint32_t pin;
+	struct bintime bt;
+};
+
+struct gpioc_events {
+	struct gpioc_event event;
+	STAILQ_ENTRY(gpioc_events) next;
+};
+
+struct gpioc_cdevpriv {
+	struct gpioc_softc			*sc;
+	uint32_t				last_intr_pin;
+	struct selinfo				selinfo;
+	bool					async;
+	struct sigio				*sigio;
+	struct mtx				mtx;
+	STAILQ_HEAD(gpioc_event_list, gpioc_events) events;
+	SLIST_HEAD(gpioc_pins_list, gpioc_pins)	pins;
+};
+
+struct gpioc_privs {
+	struct gpioc_cdevpriv		*priv;
+	SLIST_ENTRY(gpioc_privs)	next;
+};
+
+struct gpioc_pins {
+	struct gpioc_pin_intr	*pin;
+	SLIST_ENTRY(gpioc_pins)	next;
+};
+
+static MALLOC_DEFINE(M_GPIOC, "gpioc", "gpioc device data");
+
+static int	gpioc_allocate_pin_intr(struct gpioc_pin_intr*, uint32_t);
+static int	gpioc_release_pin_intr(struct gpioc_pin_intr*);
+static int	gpioc_attach_priv_pin(struct gpioc_cdevpriv*,
+		struct gpioc_pin_intr*);
+static int	gpioc_detach_priv_pin(struct gpioc_cdevpriv*,
+		struct gpioc_pin_intr*);
+static bool	gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv*,
+		struct gpioc_pin_intr *intr_conf);
+static uint32_t	gpioc_get_intr_config(struct gpioc_softc*,
+		struct gpioc_cdevpriv*, uint32_t pin);
+static int	gpioc_set_intr_config(struct gpioc_softc*,
+		struct gpioc_cdevpriv*, uint32_t, uint32_t);
+static void	gpioc_interrupt_handler(void*);
+
 static int gpioc_probe(device_t dev);
 static int gpioc_attach(device_t dev);
 static int gpioc_detach(device_t dev);
 
+static void gpioc_cdevpriv_dtor(void*);
+
+static d_open_t		gpioc_open;
+static d_read_t		gpioc_read;
 static d_ioctl_t	gpioc_ioctl;
+static d_poll_t		gpioc_poll;
+static d_kqfilter_t	gpioc_kqfilter;
 
+static int		gpioc_kqread(struct knote*, long);
+static void		gpioc_kqdetach(struct knote*);
+
+
 static struct cdevsw gpioc_cdevsw = {
 	.d_version	= D_VERSION,
+	.d_open		= gpioc_open,
+	.d_read		= gpioc_read,
 	.d_ioctl	= gpioc_ioctl,
+	.d_poll		= gpioc_poll,
+	.d_kqfilter	= gpioc_kqfilter,
 	.d_name		= "gpioc",
 };
 
-struct gpioc_softc {
-	device_t	sc_dev;		/* gpiocX dev */
-	device_t	sc_pdev;	/* gpioX dev */
-	struct cdev	*sc_ctl_dev;	/* controller device */
-	int		sc_unit;
+static struct filterops gpioc_read_filterops = {
+	.f_isfd =	true,
+	.f_attach =	NULL,
+	.f_detach =	gpioc_kqdetach,
+	.f_event =	gpioc_kqread,
+	.f_touch =	NULL
 };
 
-static int
+	static int
+gpioc_allocate_pin_intr(struct gpioc_pin_intr *intr_conf, uint32_t flags)
+{
+	int err;
+
+	intr_conf->config_locked = true;
+	mtx_unlock(&intr_conf->mtx);
+
+	intr_conf->intr_res = gpio_alloc_intr_resource(intr_conf->pin->dev,
+			&intr_conf->intr_rid, RF_ACTIVE, intr_conf->pin, flags);
+	if (intr_conf->intr_res == NULL)
+		return (ENXIO);
+
+	err = bus_setup_intr(intr_conf->pin->dev, intr_conf->intr_res,
+			INTR_TYPE_MISC | INTR_MPSAFE, NULL, gpioc_interrupt_handler,
+			intr_conf, &intr_conf->intr_cookie);
+	if (err != 0)
+		return (err);
+
+	intr_conf->pin->flags = flags;
+	mtx_lock(&intr_conf->mtx);
+	intr_conf->config_locked = false;
+	wakeup(&intr_conf->config_locked);
+
+	return (0);
+}
+
+	static int
+gpioc_release_pin_intr(struct gpioc_pin_intr *intr_conf)
+{
+	int err;
+
+	intr_conf->config_locked = true;
+	mtx_unlock(&intr_conf->mtx);
+
+	if (intr_conf->intr_cookie != NULL) {
+		err = bus_teardown_intr(intr_conf->pin->dev,
+				intr_conf->intr_res, intr_conf->intr_cookie);
+		if (err != 0)
+			return (err);
+		else
+			intr_conf->intr_cookie = NULL;
+	}
+
+	if (intr_conf->intr_res != NULL) {
+		err = bus_release_resource(intr_conf->pin->dev, SYS_RES_IRQ,
+				intr_conf->intr_rid, intr_conf->intr_res);
+		if (err != 0)
+			return (err);
+		else {
+			intr_conf->intr_rid = 0;
+			intr_conf->intr_res = NULL;
+		}
+	}
+
+	intr_conf->pin->flags = 0;
+	mtx_lock(&intr_conf->mtx);
+	intr_conf->config_locked = false;
+	wakeup(&intr_conf->config_locked);
+
+	return (0);
+}
+
+	static int
+gpioc_attach_priv_pin(struct gpioc_cdevpriv *priv,
+		struct gpioc_pin_intr *intr_conf)
+{
+	struct gpioc_privs	*priv_link;
+	struct gpioc_pins	*pin_link;
+	unsigned int		consistency_a, consistency_b;
+
+	consistency_a = 0;
+	consistency_b = 0;
+	mtx_assert(&intr_conf->mtx, MA_OWNED);
+	mtx_lock(&priv->mtx);
+	SLIST_FOREACH(priv_link, &intr_conf->privs, next) {
+		if (priv_link->priv == priv)
+			consistency_a++;
+	}
+	KASSERT(consistency_a <= 1,
+			("inconsistent links between pin config and cdevpriv"));
+	SLIST_FOREACH(pin_link, &priv->pins, next) {
+		if (pin_link->pin == intr_conf)
+			consistency_b++;
+	}
+	KASSERT(consistency_a == consistency_b,
+			("inconsistent links between pin config and cdevpriv"));
+	if (consistency_a == 1 && consistency_b == 1) {
+		mtx_unlock(&priv->mtx);
+		mtx_unlock(&intr_conf->mtx);
+		return (EEXIST);
+	}
+	priv_link = malloc(sizeof(struct gpioc_privs), M_GPIOC,
+			M_NOWAIT | M_ZERO);
+	if (priv_link == NULL)
+	{
+		mtx_unlock(&priv->mtx);
+		mtx_unlock(&intr_conf->mtx);
+		return (ENOMEM);
+	}
+	pin_link = malloc(sizeof(struct gpioc_pins), M_GPIOC,
+			M_NOWAIT | M_ZERO);
+	if (pin_link == NULL) {
+		mtx_unlock(&priv->mtx);
+		mtx_unlock(&intr_conf->mtx);
+		return (ENOMEM);
+	}
+	priv_link->priv = priv;
+	pin_link->pin = intr_conf;
+	SLIST_INSERT_HEAD(&intr_conf->privs, priv_link, next);
+	SLIST_INSERT_HEAD(&priv->pins, pin_link, next);
+	mtx_unlock(&priv->mtx);
+
+	return (0);
+}
+
+	static int
+gpioc_detach_priv_pin(struct gpioc_cdevpriv *priv,
+		struct gpioc_pin_intr *intr_conf)
+{
+	struct gpioc_privs	*priv_link, *priv_link_temp;
+	struct gpioc_pins	*pin_link, *pin_link_temp;
+	struct gpioc_events     *evs_link, *evs_link_temp;
+	unsigned int		consistency_a, consistency_b;
+
+	consistency_a = 0;
+	consistency_b = 0;
+	mtx_assert(&intr_conf->mtx, MA_OWNED);
+	mtx_lock(&priv->mtx);
+	SLIST_FOREACH_SAFE(priv_link, &intr_conf->privs, next, priv_link_temp) {
+		if (priv_link->priv == priv) {
+			SLIST_REMOVE(&intr_conf->privs, priv_link, gpioc_privs,
+					next);
+			free(priv_link, M_GPIOC);
+			consistency_a++;
+		}
+	}
+
+	STAILQ_FOREACH_SAFE(evs_link, &priv->events, next, evs_link_temp) {
+		STAILQ_REMOVE(&priv->events,evs_link, gpioc_events, next);
+		free(evs_link, M_GPIOC);
+		printf("deleting unhandled event\n");
+	}
+	KASSERT(consistency_a <= 1,
+			("inconsistent links between pin config and cdevpriv"));
+	SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) {
+		if (pin_link->pin == intr_conf) {
+			SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next);
+			free(pin_link, M_GPIOC);
+			consistency_b++;
+		}
+	}
+	KASSERT(consistency_a == consistency_b,
+			("inconsistent links between pin config and cdevpriv"));
+	mtx_unlock(&priv->mtx);
+
+	return (0);
+}
+
+	static bool
+gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv *priv,
+		struct gpioc_pin_intr *intr_conf)
+{
+	struct gpioc_privs	*priv_link;
+
+	mtx_assert(&intr_conf->mtx, MA_OWNED);
+
+	if (SLIST_EMPTY(&intr_conf->privs))
+		return (true);
+
+	SLIST_FOREACH(priv_link, &intr_conf->privs, next) {
+		if (priv_link->priv != priv)
+			return (false);
+	}
+
+	return (true);
+}
+
+
+	static uint32_t
+gpioc_get_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv,
+		uint32_t pin)
+{
+	struct gpioc_pin_intr	*intr_conf = &sc->sc_pin_intr[pin];
+	struct gpioc_privs	*priv_link;
+	uint32_t		flags;
+
+	flags = intr_conf->pin->flags;
+
+	if (flags == 0)
+		return (0);
+
+	SLIST_FOREACH(priv_link, &intr_conf->privs, next) {
+		if (priv_link->priv == priv) {
+			flags |= GPIO_INTR_ATTACHED;
+			break;
+		}
+	}
+
+	return (flags);
+}
+
+	static int
+gpioc_set_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv,
+		uint32_t pin, uint32_t flags)
+{
+	struct gpioc_pin_intr *intr_conf = &sc->sc_pin_intr[pin];
+	int res;
+
+	res = 0;
+	if (intr_conf->pin->flags == 0 && flags == 0) {
+		/* No interrupt configured and none requested: Do nothing. */
+		return (0);
+	}
+	mtx_lock(&intr_conf->mtx);
+	while (intr_conf->config_locked == true)
+		mtx_sleep(&intr_conf->config_locked, &intr_conf->mtx, 0,
+				"gpicfg", 0);
+	if (intr_conf->pin->flags == 0 && flags != 0) {
+		/* No interrupt is configured, but one is requested: Allocate
+		   and setup interrupt on the according pin. */
+		res = gpioc_allocate_pin_intr(intr_conf, flags);
+		if (res == 0)
+			res = gpioc_attach_priv_pin(priv, intr_conf);
+		if (res == EEXIST)
+			res = 0;
+	} else if (intr_conf->pin->flags == flags) {
+		/* Same interrupt requested as already configured: Attach the
+		   cdevpriv to the corresponding pin. */
+		res = gpioc_attach_priv_pin(priv, intr_conf);
+		if (res == EEXIST)
+			res = 0;
+	} else if (intr_conf->pin->flags != 0 && flags == 0) {
+		/* Interrupt configured, but none requested: Teardown and
+		   release the pin when no other cdevpriv is attached.
+		   Otherwise just detach pin and cdevpriv from each other. */
+		if (gpioc_intr_reconfig_allowed(priv, intr_conf)) {
+			res = gpioc_release_pin_intr(intr_conf);
+		}
+		if (res == 0)
+			res = gpioc_detach_priv_pin(priv, intr_conf);
+	} else {
+		/* Other flag requested than configured: Reconfigure when no
+		   other cdevpriv is are attached to the pin. */
+		if (!gpioc_intr_reconfig_allowed(priv, intr_conf))
+			res = EBUSY;
+		else {
+			res = gpioc_release_pin_intr(intr_conf);
+			if (res == 0)
+				res = gpioc_allocate_pin_intr(intr_conf, flags);
+			if (res == 0)
+				res = gpioc_attach_priv_pin(priv, intr_conf);
+			if (res == EEXIST)
+				res = 0;
+		}
+	}
+	mtx_unlock(&intr_conf->mtx);
+
+	return (res);
+}
+
+	static void
+gpioc_interrupt_handler(void *arg)
+{
+	struct gpioc_pin_intr *intr_conf = arg;
+	struct gpioc_softc *sc = intr_conf->sc;
+	struct gpioc_privs *privs;
+
+	mtx_lock(&intr_conf->mtx);
+
+	if (intr_conf->config_locked == true) {
+		device_printf(sc->sc_dev, "Interrupt configuration in "
+				"progress. Discarding interrupt on pin %d.\n",
+				intr_conf->pin->pin);
+		mtx_unlock(&intr_conf->mtx);
+		return;
+	}
+
+	if (SLIST_EMPTY(&intr_conf->privs)) {
+		device_printf(sc->sc_dev, "No file descriptor associated with "
+				"occurred interrupt on pin %d.\n", intr_conf->pin->pin);
+		mtx_unlock(&intr_conf->mtx);
+		return;
+	}
+
+	SLIST_FOREACH(privs, &intr_conf->privs, next) {
+		mtx_lock(&privs->priv->mtx);
+		struct gpioc_events *event=malloc(sizeof(struct gpioc_events), M_GPIOC, M_NOWAIT | M_ZERO);
+		if(event){
+			event->event.pin=intr_conf->pin->pin;
+			getbintime(&event->event.bt);
+			STAILQ_INSERT_TAIL(&privs->priv->events,event,next);
+			privs->priv->last_intr_pin = intr_conf->pin->pin;
+		}else{
+			if (privs->priv->last_intr_pin != -1)
+				device_printf(sc->sc_dev, "Unhandled interrupt on pin "
+						"%d.\n", intr_conf->pin->pin);
+			privs->priv->last_intr_pin = intr_conf->pin->pin;
+
+		}
+		wakeup(privs->priv);
+		selwakeup(&privs->priv->selinfo);
+		KNOTE_LOCKED(&privs->priv->selinfo.si_note, 0);
+		if (privs->priv->async == true && privs->priv->sigio != NULL)
+			pgsigio(&privs->priv->sigio, SIGIO, 0);
+		mtx_unlock(&privs->priv->mtx);
+	}
+
+	mtx_unlock(&intr_conf->mtx);
+}
+
+	static int
 gpioc_probe(device_t dev)
 {
 	device_set_desc(dev, "GPIO controller");
 	return (0);
 }
 
-static int
+	static int
 gpioc_attach(device_t dev)
 {
 	int err;
@@ -88,6 +488,22 @@
 	sc->sc_dev = dev;
 	sc->sc_pdev = device_get_parent(dev);
 	sc->sc_unit = device_get_unit(dev);
+
+	err = GPIO_PIN_MAX(sc->sc_pdev, &sc->sc_npins);
+	if (err != 0)
+		return (err);
+	sc->sc_pin_intr = malloc(sizeof(struct gpioc_pin_intr) * sc->sc_npins,
+			M_GPIOC, M_WAITOK | M_ZERO);
+	for (int i = 0; i <= sc->sc_npins; i++) {
+		sc->sc_pin_intr[i].pin = malloc(sizeof(struct gpiobus_pin),
+				M_GPIOC, M_WAITOK | M_ZERO);
+		sc->sc_pin_intr[i].sc = sc;
+		sc->sc_pin_intr[i].pin->pin = i;
+		sc->sc_pin_intr[i].pin->dev = sc->sc_pdev;
+		mtx_init(&sc->sc_pin_intr[i].mtx, "gpioc pin", NULL, MTX_DEF);
+		SLIST_INIT(&sc->sc_pin_intr[i].privs);
+	}
+
 	make_dev_args_init(&devargs);
 	devargs.mda_devsw = &gpioc_cdevsw;
 	devargs.mda_uid = UID_ROOT;
@@ -103,7 +519,7 @@
 	return (0);
 }
 
-static int
+	static int
 gpioc_detach(device_t dev)
 {
 	struct gpioc_softc *sc = device_get_softc(dev);
@@ -112,19 +528,148 @@
 	if (sc->sc_ctl_dev)
 		destroy_dev(sc->sc_ctl_dev);
 
+	for (int i = 0; i <= sc->sc_npins; i++) {
+		mtx_destroy(&sc->sc_pin_intr[i].mtx);
+		free(&sc->sc_pin_intr[i].pin, M_GPIOC);
+	}
+	free(sc->sc_pin_intr, M_GPIOC);
+
 	if ((err = bus_generic_detach(dev)) != 0)
 		return (err);
 
 	return (0);
 }
 
-static int 
+	static void
+gpioc_cdevpriv_dtor(void *data)
+{
+	struct gpioc_cdevpriv	*priv;
+	struct gpioc_privs	*priv_link, *priv_link_temp;
+	struct gpioc_pins	*pin_link, *pin_link_temp;
+	unsigned int		consistency;
+
+	priv = data;
+
+	SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) {
+		consistency = 0;
+		mtx_lock(&pin_link->pin->mtx);
+		while (pin_link->pin->config_locked == true)
+			mtx_sleep(&pin_link->pin->config_locked,
+					&pin_link->pin->mtx, 0, "gpicfg", 0);
+		SLIST_FOREACH_SAFE(priv_link, &pin_link->pin->privs, next,
+				priv_link_temp) {
+			if (priv_link->priv == priv) {
+				SLIST_REMOVE(&pin_link->pin->privs, priv_link,
+						gpioc_privs, next);
+				free(priv_link, M_GPIOC);
+				consistency++;
+			}
+		}
+		KASSERT(consistency == 1,
+				("inconsistent links between pin config and cdevpriv"));
+		if (gpioc_intr_reconfig_allowed(priv, pin_link->pin)) {
+			gpioc_release_pin_intr(pin_link->pin);
+		}
+		mtx_unlock(&pin_link->pin->mtx);
+		SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next);
+		free(pin_link, M_GPIOC);
+	}
+
+	wakeup(&priv);
+	knlist_clear(&priv->selinfo.si_note, 0);
+	seldrain(&priv->selinfo);
+	knlist_destroy(&priv->selinfo.si_note);
+	funsetown(&priv->sigio);
+
+	mtx_destroy(&priv->mtx);
+	free(data, M_GPIOC);
+}
+
+	static int
+gpioc_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+	struct gpioc_cdevpriv *priv;
+	int err;
+
+	priv = malloc(sizeof(*priv), M_GPIOC, M_WAITOK | M_ZERO);
+	err = devfs_set_cdevpriv(priv, gpioc_cdevpriv_dtor);
+	if (err != 0) {
+		gpioc_cdevpriv_dtor(priv);
+		return (err);
+	}
+	priv->sc = dev->si_drv1;
+	priv->last_intr_pin = -1;
+	STAILQ_INIT(&priv->events);
+	mtx_init(&priv->mtx, "gpioc priv", NULL, MTX_DEF);
+	knlist_init_mtx(&priv->selinfo.si_note, &priv->mtx);
+
+	return (0);
+}
+
+	static int
+gpioc_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+	struct gpioc_cdevpriv *priv;
+	uint32_t last_intr_pin;
+	int err;
+
+	if (uio->uio_resid < sizeof(priv->last_intr_pin))
+		return EINVAL;
+
+	err = devfs_get_cdevpriv((void **)&priv);
+	if (err != 0)
+		return err;
+
+	mtx_lock(&priv->mtx);
+	
+	if(!STAILQ_EMPTY(&priv->events)){
+		struct gpioc_events *event=STAILQ_FIRST(&priv->events);
+
+		STAILQ_REMOVE_HEAD(&priv->events, next);
+		if(STAILQ_EMPTY(&priv->events)){
+			priv->last_intr_pin = -1;
+		}
+
+		mtx_unlock(&priv->mtx);
+
+		err = uiomove(&event->event, sizeof(struct gpioc_event), uio);
+		free(event, M_GPIOC);
+	}else{
+		while (priv->last_intr_pin == -1) {
+			if (SLIST_EMPTY(&priv->pins)) {
+				err = ENXIO;
+				break;
+			} else if (ioflag & O_NONBLOCK) {
+				err = EWOULDBLOCK;
+				break;
+			} else {
+				err = mtx_sleep(priv, &priv->mtx, PCATCH, "gpintr", 0);
+				if (err != 0)
+					break;
+			}
+		}
+
+		if (err == 0 && priv->last_intr_pin != -1) {
+			last_intr_pin = priv->last_intr_pin;
+			priv->last_intr_pin = -1;
+			mtx_unlock(&priv->mtx);
+			err = uiomove(&last_intr_pin, sizeof(last_intr_pin), uio);
+		} else {
+			mtx_unlock(&priv->mtx);
+		}
+	}
+
+	return (err);
+}
+
+	static int 
 gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, 
-    struct thread *td)
+		struct thread *td)
 {
 	device_t bus;
 	int max_pin, res;
 	struct gpioc_softc *sc = cdev->si_drv1;
+	struct gpioc_cdevpriv *priv;
 	struct gpio_pin pin;
 	struct gpio_req req;
 	struct gpio_access_32 *a32;
@@ -144,10 +689,15 @@
 			bcopy(arg, &pin, sizeof(pin));
 			dprintf("get config pin %d\n", pin.gp_pin);
 			res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin,
-			    &pin.gp_flags);
+					&pin.gp_flags);
 			/* Fail early */
 			if (res)
 				break;
+			res = devfs_get_cdevpriv((void **)&priv);
+			if (res)
+				break;
+			pin.gp_flags |= gpioc_get_intr_config(sc, priv,
+					pin.gp_pin);
 			GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps);
 			GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name);
 			bcopy(&pin, arg, sizeof(pin));
@@ -155,56 +705,173 @@
 		case GPIOSETCONFIG:
 			bcopy(arg, &pin, sizeof(pin));
 			dprintf("set config pin %d\n", pin.gp_pin);
-			res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps);
+			res = devfs_get_cdevpriv((void **)&priv);
 			if (res == 0)
+				res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin,
+						&caps);
+			if (res == 0)
 				res = gpio_check_flags(caps, pin.gp_flags);
 			if (res == 0)
 				res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin,
-				    pin.gp_flags);
+						(pin.gp_flags & ~GPIO_INTR_MASK));
+			if (res == 0)
+				res = gpioc_set_intr_config(sc, priv,
+						pin.gp_pin,
+						(pin.gp_flags & GPIO_INTR_MASK));
 			break;
 		case GPIOGET:
 			bcopy(arg, &req, sizeof(req));
 			res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin,
-			    &req.gp_value);
+					&req.gp_value);
 			dprintf("read pin %d -> %d\n", 
-			    req.gp_pin, req.gp_value);
+					req.gp_pin, req.gp_value);
 			bcopy(&req, arg, sizeof(req));
 			break;
 		case GPIOSET:
 			bcopy(arg, &req, sizeof(req));
 			res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, 
-			    req.gp_value);
+					req.gp_value);
 			dprintf("write pin %d -> %d\n", 
-			    req.gp_pin, req.gp_value);
+					req.gp_pin, req.gp_value);
 			break;
 		case GPIOTOGGLE:
 			bcopy(arg, &req, sizeof(req));
 			dprintf("toggle pin %d\n", 
-			    req.gp_pin);
+					req.gp_pin);
 			res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin);
 			break;
 		case GPIOSETNAME:
 			bcopy(arg, &pin, sizeof(pin));
 			dprintf("set name on pin %d\n", pin.gp_pin);
 			res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin,
-			    pin.gp_name);
+					pin.gp_name);
 			break;
 		case GPIOACCESS32:
 			a32 = (struct gpio_access_32 *)arg;
 			res = GPIO_PIN_ACCESS_32(sc->sc_pdev, a32->first_pin,
-			    a32->clear_pins, a32->change_pins, &a32->orig_pins);
+					a32->clear_pins, a32->orig_pins, &a32->orig_pins);
 			break;
 		case GPIOCONFIG32:
 			c32 = (struct gpio_config_32 *)arg;
 			res = GPIO_PIN_CONFIG_32(sc->sc_pdev, c32->first_pin,
-			    c32->num_pins, c32->pin_flags);
+					c32->num_pins, c32->pin_flags);
 			break;
+		case FIONBIO:
+			/* This dummy handler is necessary to prevent fcntl()
+			   from failing. The actual handling of non-blocking IO
+			   is done using the O_NONBLOCK ioflag passed to the
+			   read() syscall. */
+			res = 0;
+			break;
+		case FIOASYNC:
+			res = devfs_get_cdevpriv((void **)&priv);
+			if (res == 0) {
+				if (*(int *)arg == FASYNC)
+					priv->async = true;
+				else
+					priv->async = false;
+			}
+			break;
+		case FIOGETOWN:
+			res = devfs_get_cdevpriv((void **)&priv);
+			if (res == 0)
+				*(int *)arg = fgetown(&priv->sigio);
+			break;
+		case FIOSETOWN:
+			res = devfs_get_cdevpriv((void **)&priv);
+			if (res == 0)
+				res = fsetown(*(int *)arg, &priv->sigio);
+			break;
 		default:
 			return (ENOTTY);
 			break;
 	}
 
 	return (res);
+}
+
+	static int
+gpioc_poll(struct cdev *dev, int events, struct thread *td)
+{
+	struct gpioc_cdevpriv *priv;
+	int err;
+	int revents;
+
+	revents = 0;
+
+	err = devfs_get_cdevpriv((void **)&priv);
+	if (err != 0) {
+		revents = POLLERR;
+		return (revents);
+	}
+
+	if (SLIST_EMPTY(&priv->pins)) {
+		revents = POLLHUP;
+		return (revents);
+	}
+
+	if (events & (POLLIN | POLLRDNORM)) {
+		if (priv->last_intr_pin != -1)
+			revents |= events & (POLLIN | POLLRDNORM);
+		else
+			selrecord(td, &priv->selinfo);
+	}
+
+	return (revents);
+}
+
+	static int
+gpioc_kqfilter(struct cdev *dev, struct knote *kn)
+{//attach to kqueue
+	struct gpioc_cdevpriv *priv;
+	struct knlist *knlist;
+	int err;
+
+	err = devfs_get_cdevpriv((void **)&priv);
+	if (err != 0)
+		return err;
+
+	if (SLIST_EMPTY(&priv->pins))
+		return (ENXIO);
+
+	switch(kn->kn_filter) {
+	case EVFILT_READ:
+		kn->kn_fop = &gpioc_read_filterops;
+		kn->kn_hook = (void *)priv;
+		break;
+	default:
+		return (EOPNOTSUPP);
+	}
+
+	knlist = &priv->selinfo.si_note;
+	knlist_add(knlist, kn, 0);
+
+	return (0);
+}
+
+static int
+gpioc_kqread(struct knote *kn, long hint)
+{
+	struct gpioc_cdevpriv *priv = kn->kn_hook;
+	if (SLIST_EMPTY(&priv->pins)) {
+		kn->kn_flags |= EV_EOF;
+		return (1);
+	} else {
+		if (priv->last_intr_pin != -1) {
+			kn->kn_data = sizeof(priv->last_intr_pin);
+			return (1);
+		}
+	}
+	return (0);
+}
+
+static void
+gpioc_kqdetach(struct knote *kn)
+{
+	struct gpioc_cdevpriv *priv = kn->kn_hook;
+	struct knlist *knlist = &priv->selinfo.si_note;
+
+	knlist_remove(knlist, kn, 0);
 }
 
 static device_method_t gpioc_methods[] = {
Only in /usr/home/buildroot/usr/src/sys/dev/gpio: gpioc.c.orig
Only in .: diff.core
Only in .: gpio.diff
diff -ru ./sys/gpio.h /usr/home/buildroot/usr/src/sys/sys/gpio.h
--- ./sys/gpio.h	2020-10-23 03:02:18.000000000 +0300
+++ /usr/home/buildroot/usr/src/sys/sys/gpio.h	2020-11-25 12:22:41.460941000 +0300
@@ -84,6 +84,7 @@
 #define GPIO_INTR_MASK		(GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | \
 				GPIO_INTR_EDGE_RISING |			      \
 				GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH)
+#define GPIO_INTR_ATTACHED	0x00200000      /* interrupt attached to file */
 
 struct gpio_pin {
 	uint32_t gp_pin;			/* pin number */
Only in /usr/home/buildroot/usr/src/sys/sys: gpio.h.orig
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.