Module Name: src
Committed By: brad
Date: Mon Nov 6 00:35:06 UTC 2023
Modified Files:
src/doc: CHANGES
src/etc: MAKEDEV.tmpl
src/share/man/man4: gpioirq.4
src/sys/conf: majors
src/sys/dev/gpio: gpio.c gpioirq.c gpiovar.h
Log Message:
gpioirq(4) version 2
This update makes this driver more than just an example and allows for:
o More than one pin to be attached to a gpioirq instance. That is,
the mask parameter can be greater than 0x01 now.
o A /dev/gpioirqN device that allows GPIO pin interrupts to be
transported into userland. This is a device that can be opened for
reading with a simple fixed output indicating the device unit, pin
number and current pin state.
This update was used as part of a physical intrusion detection system
where multiple switches (i.e. window magnetic reed switches and etc.)
are tied to a bunch of GPIO inputs with userland software that reacts
to the pins changing state.
To generate a diff of this commit:
cvs rdiff -u -r1.3015 -r1.3016 src/doc/CHANGES
cvs rdiff -u -r1.233 -r1.234 src/etc/MAKEDEV.tmpl
cvs rdiff -u -r1.3 -r1.4 src/share/man/man4/gpioirq.4
cvs rdiff -u -r1.102 -r1.103 src/sys/conf/majors
cvs rdiff -u -r1.72 -r1.73 src/sys/dev/gpio/gpio.c
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/gpio/gpioirq.c
cvs rdiff -u -r1.18 -r1.19 src/sys/dev/gpio/gpiovar.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/doc/CHANGES
diff -u src/doc/CHANGES:1.3015 src/doc/CHANGES:1.3016
--- src/doc/CHANGES:1.3015 Sun Nov 5 18:32:01 2023
+++ src/doc/CHANGES Mon Nov 6 00:35:06 2023
@@ -1,4 +1,4 @@
-# LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.3015 $>
+# LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.3016 $>
#
#
# [Note: This file does not mention every change made to the NetBSD source tree.
@@ -258,3 +258,6 @@ Changes from NetBSD 10.0 to NetBSD 11.0:
machines. [tsutsui 20231104]
ena(4): MP-enable always, add RSS support, and reliability fixes.
[jdolecek 20231105]
+ gpioirq(4): allow multiple pins per gpioirq instance, add the ability
+ to use a /dev/gpioirqN device to get pin interrupts into userland.
+ [brad 20231105]
Index: src/etc/MAKEDEV.tmpl
diff -u src/etc/MAKEDEV.tmpl:1.233 src/etc/MAKEDEV.tmpl:1.234
--- src/etc/MAKEDEV.tmpl:1.233 Wed Dec 28 19:23:02 2022
+++ src/etc/MAKEDEV.tmpl Mon Nov 6 00:35:05 2023
@@ -1,5 +1,5 @@
#!/bin/sh -
-# $NetBSD: MAKEDEV.tmpl,v 1.233 2022/12/28 19:23:02 jakllsch Exp $
+# $NetBSD: MAKEDEV.tmpl,v 1.234 2023/11/06 00:35:05 brad Exp $
#
# Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc.
# All rights reserved.
@@ -232,6 +232,7 @@
# dtv* Digital TV interface
# fb* PMAX generic framebuffer pseudo-device
# fd file descriptors
+# gpioirq* Interrupts on GPIO pins
# gpiopps* 1PPS signals on GPIO pins
# grf* graphics frame buffer device
# hdaudio* High Definition audio control device
@@ -830,7 +831,7 @@ all)
makedev srt0 srt1 srt2 srt3
makedev tap tap0 tap1 tap2 tap3
makedev gpio gpio0 gpio1 gpio2 gpio3 gpio4 gpio5 gpio6 gpio7
- makedev gpiopps0
+ makedev gpioirq0 gpiopps0
makedev pad pad0 pad1 pad2 pad3
makedev bthub
makedev putter
@@ -873,6 +874,10 @@ gpio)
lndev gpio0 gpio
;;
+gpioirq)
+ makedev gpioirq0
+ ;;
+
gpiopps)
makedev gpiopps0
lndev gpiopps0 gpiopps
@@ -1547,6 +1552,11 @@ gpio[0-9]*)
mkdev gpio$unit c %gpio_chr% $unit 664 $g_gpio
;;
+gpioirq[0-9]*)
+ unit=${i#gpioirq}
+ mkdev gpioirq$unit c %gpioirq_chr% $unit 444 $g_gpio
+ ;;
+
gpiopps[0-9]*)
unit=${i#gpiopps}
mkdev gpiopps$unit c %gpiopps_chr% $unit 664 $g_gpio
Index: src/share/man/man4/gpioirq.4
diff -u src/share/man/man4/gpioirq.4:1.3 src/share/man/man4/gpioirq.4:1.4
--- src/share/man/man4/gpioirq.4:1.3 Tue Aug 1 20:39:15 2023
+++ src/share/man/man4/gpioirq.4 Mon Nov 6 00:35:05 2023
@@ -1,6 +1,6 @@
-.\" $NetBSD: gpioirq.4,v 1.3 2023/08/01 20:39:15 andvar Exp $
+.\" $NetBSD: gpioirq.4,v 1.4 2023/11/06 00:35:05 brad Exp $
.\"
-.\" Copyright (c) 2016 Brad Spencer <[email protected]>
+.\" Copyright (c) 2016, 2023 Brad Spencer <[email protected]>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -14,25 +14,26 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd May 11, 2018
+.Dd November 5, 2023
.Dt GPIOIRQ 4
.Os
.Sh NAME
.Nm gpioirq
-.Nd Install an interrupt handler on a GPIO pin
+.Nd Install an interrupt handler on GPIO pins
.Sh SYNOPSIS
.Cd "gpioirq* at gpio? offset 0 mask 0x1 flag 0x00"
.Sh DESCRIPTION
The
.Nm
-driver attaches an interrupt handler to a single GPIO pin.
+driver attaches an interrupt handler to a one or more GPIO pins.
.Pp
-The pin number is specified in the kernel configuration file with the
+The base pin number is specified in the kernel configuration file with the
.Ar offset
locator.
The
.Ar mask
-locator should always be 0x1.
+locator can be 0x01 or greater to indicate that more pins should have an
+interrupt handler attached to them.
.Pp
The
.Ar flag
@@ -49,7 +50,7 @@ edge of the pin.
.It Dv 0x04
Interrupt on both edges of the pin.
.It Dv 0x08
-Assert the interrupt as long as the pin is high.
+Assert the intrerrupt as long as the pin is high.
.It Dv 0x10
Assert the interrupt as long as the pin is low.
.El
@@ -80,6 +81,36 @@ is attached at runtime using the
on the
.Xr gpio 4
device.
+.Sh FILES
+.Bl -tag -width "/dev/gpioirqu" -compact
+.It /dev/gpioirq Ns Ar u
+GPIOIRQ device unit
+.Ar u
+file.
+The output from this device are three uint8_t bytes every time an interrupt fires.
+The bytes contain the device unit, pin number and the current state of the pin.
+.Sh EXAMPLES
+The following example will output the device unit, pin and
+the pins current state for pins 4, 5, 6, 7, 8, 9, 10, 11, 12 on gpio0:
+.Bd -literal -offset indent
+/etc/gpio.conf contains:
+gpio0 attach gpioirq 4 0x1ff 0x04
+
+or a kernel was compiled to have the same parameters.
+
+#!/usr/pkg/bin/perl
+
+$dev = "/dev/gpioirq0";
+
+sysopen(DEV,$dev,O_RDONLY) || die "sysopen: $!";
+
+while (sysread(DEV,$b,3)) {
+ @v = unpack("CCC",$b);
+
+ print join(',',@v);
+ print "\\n";
+}
+
.Sh SEE ALSO
.Xr gpio 4 ,
.Xr drvctl 8 ,
@@ -95,3 +126,22 @@ The
.Nm
driver was written by
.An Brad Spencer Aq Mt [email protected] .
+.Sh BUGS
+When an interrupt fires in most devices there is not any information carried
+along in the interrupt as to whether or not the pin is high or low. Hence the
+driver reads the current state of the pin after the interrupt has fired and it is
+possible that the state of the pin could have changed between the time the interrupt
+fired and the reading of the state. As a practical matter the only time the pin state
+will be reported wrong is if there is a very large number of interrupts happening. The
+driver could have made some assumptions if the interrupt was only for a rising edge or falling
+edge as in those cases it would be possible to know what the pin state would have been, but
+in the case of the double edge, there really will not be any way to be sure with most hardware
+and, in any case, the
+.Xr gpio 4
+infrastructure does not support getting at that information even if it did exist.
+.Pp
+It is important that if the
+.Xr gpioirq 4
+device is opened that it be read, as it may be possible
+to run the kernel out of memory if the device is opened but not read and interrupts
+occur on a pin tied to the driver.
Index: src/sys/conf/majors
diff -u src/sys/conf/majors:1.102 src/sys/conf/majors:1.103
--- src/sys/conf/majors:1.102 Fri Aug 12 11:15:42 2022
+++ src/sys/conf/majors Mon Nov 6 00:35:05 2023
@@ -1,4 +1,4 @@
-# $NetBSD: majors,v 1.102 2022/08/12 11:15:42 riastradh Exp $
+# $NetBSD: majors,v 1.103 2023/11/06 00:35:05 brad Exp $
#
# Device majors for Machine-Independent drivers.
#
@@ -96,3 +96,4 @@ device-major efi char 361
device-major sht3xtemp char 362 sht3xtemp
device-major scmd char 363 scmd
device-major viocon char 364 viocon
+device-major gpioirq char 365 gpioirq
Index: src/sys/dev/gpio/gpio.c
diff -u src/sys/dev/gpio/gpio.c:1.72 src/sys/dev/gpio/gpio.c:1.73
--- src/sys/dev/gpio/gpio.c:1.72 Tue Dec 13 21:50:43 2022
+++ src/sys/dev/gpio/gpio.c Mon Nov 6 00:35:05 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: gpio.c,v 1.72 2022/12/13 21:50:43 jakllsch Exp $ */
+/* $NetBSD: gpio.c,v 1.73 2023/11/06 00:35:05 brad Exp $ */
/* $OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $ */
/*
@@ -23,7 +23,7 @@
#endif
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.72 2022/12/13 21:50:43 jakllsch Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.73 2023/11/06 00:35:05 brad Exp $");
/*
* General Purpose Input/Output framework.
@@ -619,6 +619,14 @@ gpio_intr_str(void *gpio, struct gpio_pi
}
int
+gpio_pin_to_pin_num(void *gpio, struct gpio_pinmap *map, int pin)
+{
+ struct gpio_softc *sc = gpio;
+
+ return sc->sc_pins[map->pm_map[pin]].pin_num;
+}
+
+int
gpio_npins(uint32_t mask)
{
int npins, i;
Index: src/sys/dev/gpio/gpioirq.c
diff -u src/sys/dev/gpio/gpioirq.c:1.1 src/sys/dev/gpio/gpioirq.c:1.2
--- src/sys/dev/gpio/gpioirq.c:1.1 Sat May 19 14:15:39 2018
+++ src/sys/dev/gpio/gpioirq.c Mon Nov 6 00:35:05 2023
@@ -1,7 +1,7 @@
-/* $NetBSD: gpioirq.c,v 1.1 2018/05/19 14:15:39 thorpej Exp $ */
+/* $NetBSD: gpioirq.c,v 1.2 2023/11/06 00:35:05 brad Exp $ */
/*
- * Copyright (c) 2016 Brad Spencer <[email protected]>
+ * Copyright (c) 2016, 2023 Brad Spencer <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -17,32 +17,64 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gpioirq.c,v 1.1 2018/05/19 14:15:39 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gpioirq.c,v 1.2 2023/11/06 00:35:05 brad Exp $");
/*
- * Example GPIO driver that uses interrupts.
+ * GPIO driver that uses interrupts and can send that fact to userland.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
+#include <sys/device_impl.h>
#include <sys/gpio.h>
#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/pool.h>
+#include <sys/kmem.h>
+#include <sys/condvar.h>
#include <dev/gpio/gpiovar.h>
-#define GPIOIRQ_NPINS 1
+#define GPIOIRQ_NPINS 64
+
+struct gpioirq_iv {
+ char sc_intrstr[128];
+ void * sc_ih;
+ int i_thispin_index;
+ uint8_t i_thispin_num;
+ uint8_t i_parentunit;
+ struct gpioirq_softc *sc;
+};
struct gpioirq_softc {
device_t sc_dev;
+ device_t sc_parentdev;
void * sc_gpio;
struct gpio_pinmap sc_map;
int _map[GPIOIRQ_NPINS];
- char sc_intrstr[128];
- void * sc_ih;
+ struct gpioirq_iv sc_intrs[GPIOIRQ_NPINS];
+ int sc_npins;
kmutex_t sc_lock;
+ kmutex_t sc_read_mutex;
+ kmutex_t sc_dying_mutex;
bool sc_verbose;
bool sc_functional;
+ bool sc_opened;
+ bool sc_dying;
+ kcondvar_t sc_condreadready;
+ kcondvar_t sc_cond_dying;
+ pool_cache_t sc_readpool;
+ char *sc_readpoolname;
+ SIMPLEQ_HEAD(,gpioirq_read_q) sc_read_queue;
+};
+
+struct gpioirq_read_q {
+ int parentunit;
+ int thepin;
+ int theval;
+ SIMPLEQ_ENTRY(gpioirq_read_q) read_q;
};
#define GPIOIRQ_FLAGS_IRQMODE GPIO_INTR_MODE_MASK
@@ -52,8 +84,9 @@ static int gpioirq_match(device_t, cfdat
static void gpioirq_attach(device_t, device_t, void *);
static int gpioirq_detach(device_t, int);
static int gpioirq_activate(device_t, enum devact);
-
static int gpioirq_intr(void *);
+static uint8_t gpioirq_index_to_pin_num(struct gpioirq_softc *, int);
+static uint8_t gpioirq_parent_unit(struct gpioirq_softc *);
CFATTACH_DECL_NEW(gpioirq, sizeof(struct gpioirq_softc),
gpioirq_match, gpioirq_attach,
@@ -61,20 +94,47 @@ CFATTACH_DECL_NEW(gpioirq, sizeof(struct
extern struct cfdriver gpioirq_cd;
+static dev_type_open(gpioirq_open);
+static dev_type_read(gpioirq_read);
+static dev_type_close(gpioirq_close);
+const struct cdevsw gpioirq_cdevsw = {
+ .d_open = gpioirq_open,
+ .d_close = gpioirq_close,
+ .d_read = gpioirq_read,
+ .d_write = nowrite,
+ .d_ioctl = noioctl,
+ .d_stop = nostop,
+ .d_tty = notty,
+ .d_poll = nopoll,
+ .d_mmap = nommap,
+ .d_kqfilter = nokqfilter,
+ .d_discard = nodiscard,
+ .d_flag = D_OTHER
+};
+
+static uint8_t
+gpioirq_index_to_pin_num(struct gpioirq_softc *sc, int index)
+{
+ return (uint8_t)gpio_pin_to_pin_num(sc->sc_gpio, &sc->sc_map, index);
+}
+
+static uint8_t
+gpioirq_parent_unit(struct gpioirq_softc *sc)
+{
+ device_t parent = sc->sc_parentdev;
+
+ return (uint8_t)parent->dv_unit;
+}
+
static int
gpioirq_match(device_t parent, cfdata_t cf, void *aux)
{
struct gpio_attach_args *ga = aux;
- int npins;
if (strcmp(ga->ga_dvname, cf->cf_name))
return (0);
-
- if (ga->ga_offset == -1)
- return (0);
- npins = gpio_npins(ga->ga_mask);
- if (npins > 1)
+ if (ga->ga_offset == -1)
return (0);
return (1);
@@ -85,18 +145,30 @@ gpioirq_attach(device_t parent, device_t
{
struct gpioirq_softc *sc = device_private(self);
struct gpio_attach_args *ga = aux;
- int npins = gpio_npins(ga->ga_mask);
+ int mask = ga->ga_mask;
int irqmode, flags;
sc->sc_dev = self;
+ sc->sc_parentdev = parent;
+ sc->sc_opened = false;
+ sc->sc_dying = false;
+ sc->sc_readpoolname = NULL;
/* Map pins */
sc->sc_gpio = ga->ga_gpio;
sc->sc_map.pm_map = sc->_map;
- /* We always map just 1 pin. */
+ /* Determine our pin configuation. */
+ sc->sc_npins = gpio_npins(mask);
+ if (sc->sc_npins == 0) {
+ sc->sc_npins = 1;
+ mask = 0x1;
+ }
+
+ /* XXX - exit if more than allowed number of pins */
+
if (gpio_pin_map(sc->sc_gpio, ga->ga_offset,
- npins ? ga->ga_mask : 0x1, &sc->sc_map)) {
+ mask, &sc->sc_map)) {
aprint_error(": can't map pins\n");
return;
}
@@ -109,74 +181,239 @@ gpioirq_attach(device_t parent, device_t
irqmode = ga->ga_flags & GPIOIRQ_FLAGS_IRQMODE;
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
-
- if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 0, irqmode,
- sc->sc_intrstr, sizeof(sc->sc_intrstr))) {
- aprint_error_dev(self, "failed to decode interrupt\n");
- return;
- }
-
- if (!gpio_pin_irqmode_issupported(sc->sc_gpio, &sc->sc_map, 0,
- irqmode)) {
- aprint_error_dev(self,
- "irqmode not supported: %s\n", sc->sc_intrstr);
- gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
- return;
- }
-
- flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 0);
- flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
- GPIO_PIN_INPUT;
- if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 0, flags)) {
- aprint_error_dev(sc->sc_dev, "pin not capable of input\n");
- gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
- return;
+ mutex_init(&sc->sc_dying_mutex, MUTEX_DEFAULT, IPL_VM);
+ mutex_init(&sc->sc_read_mutex, MUTEX_DEFAULT, IPL_VM);
+ cv_init(&sc->sc_cond_dying, "girqdie");
+ cv_init(&sc->sc_condreadready,"girqrr");
+ sc->sc_readpoolname = kmem_asprintf("girqread%d",device_unit(self));
+ sc->sc_readpool = pool_cache_init(sizeof(struct gpioirq_read_q),0,0,0,sc->sc_readpoolname,NULL,IPL_VM,NULL,NULL,NULL);
+ pool_cache_sethiwat(sc->sc_readpool,100);
+ SIMPLEQ_INIT(&sc->sc_read_queue);
+
+ for(int apin = 0; apin < sc->sc_npins; apin++) {
+ if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, apin, irqmode,
+ sc->sc_intrs[apin].sc_intrstr, sizeof(sc->sc_intrs[apin].sc_intrstr))) {
+ aprint_error_dev(self, "failed to decode interrupt\n");
+ return;
+ }
+
+ if (!gpio_pin_irqmode_issupported(sc->sc_gpio, &sc->sc_map, apin,
+ irqmode)) {
+ aprint_error_dev(self,
+ "irqmode not supported: %s\n", sc->sc_intrs[apin].sc_intrstr);
+ gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+ return;
+ }
+
+ flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, apin);
+ flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
+ GPIO_PIN_INPUT;
+ if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, apin, flags)) {
+ aprint_error_dev(sc->sc_dev, "pin not capable of input\n");
+ gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+ return;
+ }
+
+ /* These are static for each pin, so just stuff them in here,
+ * so they don't need to be looked up again.
+ */
+ sc->sc_intrs[apin].i_thispin_index = apin;
+ sc->sc_intrs[apin].i_thispin_num = gpioirq_index_to_pin_num(sc,apin);
+ sc->sc_intrs[apin].i_parentunit = gpioirq_parent_unit(sc);
+ sc->sc_intrs[apin].sc = sc;
+
+ sc->sc_intrs[apin].sc_ih = gpio_intr_establish(sc->sc_gpio, &sc->sc_map, apin, IPL_VM,
+ irqmode | GPIO_INTR_MPSAFE,
+ gpioirq_intr, &sc->sc_intrs[apin]);
+ if (sc->sc_intrs[apin].sc_ih == NULL) {
+ aprint_error_dev(self,
+ "unable to establish interrupt on %s\n", sc->sc_intrs[apin].sc_intrstr);
+ gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+ return;
+ }
+ aprint_normal_dev(self, "interrupting on %s\n", sc->sc_intrs[apin].sc_intrstr);
}
- sc->sc_ih = gpio_intr_establish(sc->sc_gpio, &sc->sc_map, 0, IPL_VM,
- irqmode | GPIO_INTR_MPSAFE,
- gpioirq_intr, sc);
- if (sc->sc_ih == NULL) {
- aprint_error_dev(self,
- "unable to establish interrupt on %s\n", sc->sc_intrstr);
- gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
- return;
- }
- aprint_normal_dev(self, "interrupting on %s\n", sc->sc_intrstr);
-
sc->sc_functional = true;
}
int
gpioirq_intr(void *arg)
{
- struct gpioirq_softc *sc = arg;
+ struct gpioirq_iv *is = arg;
+ struct gpioirq_softc *sc = is->sc;
+ struct gpioirq_read_q *q;
int val;
mutex_enter(&sc->sc_lock);
- val = gpio_pin_read(sc->sc_gpio, &sc->sc_map, 0);
+ val = gpio_pin_read(sc->sc_gpio, &sc->sc_map, is->i_thispin_index);
if (sc->sc_verbose)
printf("%s: interrupt on %s --> %d\n",
- device_xname(sc->sc_dev), sc->sc_intrstr, val);
+ device_xname(sc->sc_dev), sc->sc_intrs[is->i_thispin_index].sc_intrstr, val);
mutex_exit(&sc->sc_lock);
+ if (sc->sc_opened) {
+ mutex_enter(&sc->sc_read_mutex);
+ q = pool_cache_get(sc->sc_readpool,PR_NOWAIT);
+ if (q != NULL) {
+ q->thepin = is->i_thispin_num;
+ q->parentunit = is->i_parentunit;
+ q->theval = val;
+ SIMPLEQ_INSERT_TAIL(&sc->sc_read_queue,q,read_q);
+ cv_signal(&sc->sc_condreadready);
+ } else {
+ aprint_error("Could not allocate memory for read pool\n");
+ }
+ mutex_exit(&sc->sc_read_mutex);
+ }
+
return (1);
}
+static int
+gpioirq_open(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+ struct gpioirq_softc *sc;
+
+ sc = device_lookup_private(&gpioirq_cd, minor(dev));
+ if (!sc)
+ return (ENXIO);
+
+ if (sc->sc_opened)
+ return (EBUSY);
+
+ mutex_enter(&sc->sc_lock);
+ sc->sc_opened = true;
+ mutex_exit(&sc->sc_lock);
+
+ return (0);
+}
+
+static int
+gpioirq_read(dev_t dev, struct uio *uio, int flags)
+{
+ struct gpioirq_softc *sc;
+ struct gpioirq_read_q *chp;
+ int error = 0,any;
+ uint8_t obuf[3];
+
+ sc = device_lookup_private(&gpioirq_cd, minor(dev));
+ if (!sc)
+ return (ENXIO);
+
+ while (uio->uio_resid > 0) {
+ any = 0;
+ error = 0;
+ mutex_enter(&sc->sc_read_mutex);
+
+ while (any == 0) {
+ chp = SIMPLEQ_FIRST(&sc->sc_read_queue);
+ if (chp != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_read_queue, read_q);
+ any = 1;
+ break;
+ } else {
+ error = cv_wait_sig(&sc->sc_condreadready,&sc->sc_read_mutex);
+ if (sc->sc_dying)
+ error = EIO;
+ if (error == 0)
+ continue;
+ break;
+ }
+ }
+
+ if (any == 1 && error == 0) {
+ obuf[0] = (uint8_t)chp->parentunit;
+ obuf[1] = (uint8_t)chp->thepin;
+ obuf[2] = (uint8_t)chp->theval;
+ pool_cache_put(sc->sc_readpool,chp);
+ mutex_exit(&sc->sc_read_mutex);
+ if ((error = uiomove(&obuf[0], 3, uio)) != 0) {
+ break;
+ }
+ } else {
+ mutex_exit(&sc->sc_read_mutex);
+ if (error) {
+ break;
+ }
+ }
+ }
+
+ if (sc->sc_dying) {
+ mutex_enter(&sc->sc_dying_mutex);
+ cv_signal(&sc->sc_cond_dying);
+ mutex_exit(&sc->sc_dying_mutex);
+ }
+ return error;
+}
+
+static int
+gpioirq_close(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+ struct gpioirq_softc *sc;
+ struct gpioirq_read_q *q;
+
+ sc = device_lookup_private(&gpioirq_cd, minor(dev));
+
+ mutex_enter(&sc->sc_lock);
+ while ((q = SIMPLEQ_FIRST(&sc->sc_read_queue)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_read_queue, read_q);
+ pool_cache_put(sc->sc_readpool,q);
+ }
+ sc->sc_opened = false;
+ mutex_exit(&sc->sc_lock);
+
+ return(0);
+}
+
int
gpioirq_detach(device_t self, int flags)
{
struct gpioirq_softc *sc = device_private(self);
+ struct gpioirq_read_q *q;
/* Clear the handler and disable the interrupt. */
- gpio_intr_disestablish(sc->sc_gpio, sc->sc_ih);
+ for(int apin = 0;apin < sc->sc_npins;apin++) {
+ gpio_intr_disestablish(sc->sc_gpio, sc->sc_intrs[apin].sc_ih);
+ }
/* Release the pin. */
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+ sc->sc_dying = true;
+
+ if (sc->sc_opened) {
+ mutex_enter(&sc->sc_dying_mutex);
+ mutex_enter(&sc->sc_read_mutex);
+ cv_signal(&sc->sc_condreadready);
+ mutex_exit(&sc->sc_read_mutex);
+ /* In the worst case this will time out after 5 seconds.
+ * It really should not take that long for the drain / whatever
+ * to happen
+ */
+ cv_timedwait_sig(&sc->sc_cond_dying,
+ &sc->sc_dying_mutex, mstohz(5000));
+ mutex_exit(&sc->sc_dying_mutex);
+ cv_destroy(&sc->sc_condreadready);
+ cv_destroy(&sc->sc_cond_dying);
+ }
+
+ /* Drain any read pools */
+ while ((q = SIMPLEQ_FIRST(&sc->sc_read_queue)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_read_queue, read_q);
+ pool_cache_put(sc->sc_readpool,q);
+ }
+
+ if (sc->sc_readpoolname != NULL) {
+ kmem_free(sc->sc_readpoolname,strlen(sc->sc_readpoolname) + 1);
+ }
+
+ mutex_destroy(&sc->sc_read_mutex);
+ mutex_destroy(&sc->sc_lock);
+
return (0);
}
@@ -184,9 +421,11 @@ int
gpioirq_activate(device_t self, enum devact act)
{
+ struct gpioirq_softc *sc = device_private(self);
+
switch (act) {
case DVACT_DEACTIVATE:
- /* We don't really need to do anything. */
+ sc->sc_dying = true;
return (0);
default:
return (EOPNOTSUPP);
@@ -203,26 +442,39 @@ static int
gpioirq_modcmd(modcmd_t cmd, void *opaque)
{
int error = 0;
+#ifdef _MODULE
+ int bmaj = -1, cmaj = -1;
+#endif
switch (cmd) {
case MODULE_CMD_INIT:
#ifdef _MODULE
error = config_init_component(cfdriver_ioconf_gpioirq,
cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq);
- if (error)
+ if (error) {
aprint_error("%s: unable to init component\n",
gpioirq_cd.cd_name);
+ return (error);
+ }
+
+ error = devsw_attach("gpioirq", NULL, &bmaj,
+ &gpioirq_cdevsw, &cmaj);
+ if (error) {
+ aprint_error("%s: unable to attach devsw\n",
+ gpioirq_cd.cd_name);
+ config_fini_component(cfdriver_ioconf_gpioirq,
+ cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq);
+ }
#endif
- break;
+ return (error);
case MODULE_CMD_FINI:
#ifdef _MODULE
+ devsw_detach(NULL, &gpioirq_cdevsw);
config_fini_component(cfdriver_ioconf_gpioirq,
cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq);
#endif
- break;
+ return (0);
default:
- error = ENOTTY;
+ return (ENOTTY);
}
-
- return (error);
}
Index: src/sys/dev/gpio/gpiovar.h
diff -u src/sys/dev/gpio/gpiovar.h:1.18 src/sys/dev/gpio/gpiovar.h:1.19
--- src/sys/dev/gpio/gpiovar.h:1.18 Sat May 19 13:59:06 2018
+++ src/sys/dev/gpio/gpiovar.h Mon Nov 6 00:35:05 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: gpiovar.h,v 1.18 2018/05/19 13:59:06 thorpej Exp $ */
+/* $NetBSD: gpiovar.h,v 1.19 2023/11/06 00:35:05 brad Exp $ */
/* $OpenBSD: gpiovar.h,v 1.3 2006/01/14 12:33:49 grange Exp $ */
/*
@@ -118,6 +118,7 @@ void * gpio_intr_establish(void *, struc
void gpio_intr_disestablish(void *, void *);
bool gpio_intr_str(void *, struct gpio_pinmap *, int, int,
char *, size_t);
+int gpio_pin_to_pin_num(void *, struct gpio_pinmap *, int);
int gpio_lock(void *);
void gpio_unlock(void *);