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.
This commit is contained in:
brad 2023-11-06 00:35:05 +00:00
parent 96f512bb33
commit 77871fbede
7 changed files with 397 additions and 72 deletions

View File

@ -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. # [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] machines. [tsutsui 20231104]
ena(4): MP-enable always, add RSS support, and reliability fixes. ena(4): MP-enable always, add RSS support, and reliability fixes.
[jdolecek 20231105] [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]

View File

@ -1,5 +1,5 @@
#!/bin/sh - #!/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. # Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc.
# All rights reserved. # All rights reserved.
@ -232,6 +232,7 @@
# dtv* Digital TV interface # dtv* Digital TV interface
# fb* PMAX generic framebuffer pseudo-device # fb* PMAX generic framebuffer pseudo-device
# fd file descriptors # fd file descriptors
# gpioirq* Interrupts on GPIO pins
# gpiopps* 1PPS signals on GPIO pins # gpiopps* 1PPS signals on GPIO pins
# grf* graphics frame buffer device # grf* graphics frame buffer device
# hdaudio* High Definition audio control device # hdaudio* High Definition audio control device
@ -830,7 +831,7 @@ all)
makedev srt0 srt1 srt2 srt3 makedev srt0 srt1 srt2 srt3
makedev tap tap0 tap1 tap2 tap3 makedev tap tap0 tap1 tap2 tap3
makedev gpio gpio0 gpio1 gpio2 gpio3 gpio4 gpio5 gpio6 gpio7 makedev gpio gpio0 gpio1 gpio2 gpio3 gpio4 gpio5 gpio6 gpio7
makedev gpiopps0 makedev gpioirq0 gpiopps0
makedev pad pad0 pad1 pad2 pad3 makedev pad pad0 pad1 pad2 pad3
makedev bthub makedev bthub
makedev putter makedev putter
@ -873,6 +874,10 @@ gpio)
lndev gpio0 gpio lndev gpio0 gpio
;; ;;
gpioirq)
makedev gpioirq0
;;
gpiopps) gpiopps)
makedev gpiopps0 makedev gpiopps0
lndev gpiopps0 gpiopps lndev gpiopps0 gpiopps
@ -1547,6 +1552,11 @@ gpio[0-9]*)
mkdev gpio$unit c %gpio_chr% $unit 664 $g_gpio 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]*) gpiopps[0-9]*)
unit=${i#gpiopps} unit=${i#gpiopps}
mkdev gpiopps$unit c %gpiopps_chr% $unit 664 $g_gpio mkdev gpiopps$unit c %gpiopps_chr% $unit 664 $g_gpio

View File

@ -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 <brad@anduin.eldar.org> .\" Copyright (c) 2016, 2023 Brad Spencer <brad@anduin.eldar.org>
.\" .\"
.\" Permission to use, copy, modify, and distribute this software for any .\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above .\" 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 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd May 11, 2018 .Dd November 5, 2023
.Dt GPIOIRQ 4 .Dt GPIOIRQ 4
.Os .Os
.Sh NAME .Sh NAME
.Nm gpioirq .Nm gpioirq
.Nd Install an interrupt handler on a GPIO pin .Nd Install an interrupt handler on GPIO pins
.Sh SYNOPSIS .Sh SYNOPSIS
.Cd "gpioirq* at gpio? offset 0 mask 0x1 flag 0x00" .Cd "gpioirq* at gpio? offset 0 mask 0x1 flag 0x00"
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Nm .Nm
driver attaches an interrupt handler to a single GPIO pin. driver attaches an interrupt handler to a one or more GPIO pins.
.Pp .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 .Ar offset
locator. locator.
The The
.Ar mask .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 .Pp
The The
.Ar flag .Ar flag
@ -49,7 +50,7 @@ edge of the pin.
.It Dv 0x04 .It Dv 0x04
Interrupt on both edges of the pin. Interrupt on both edges of the pin.
.It Dv 0x08 .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 .It Dv 0x10
Assert the interrupt as long as the pin is low. Assert the interrupt as long as the pin is low.
.El .El
@ -80,6 +81,36 @@ is attached at runtime using the
on the on the
.Xr gpio 4 .Xr gpio 4
device. 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 .Sh SEE ALSO
.Xr gpio 4 , .Xr gpio 4 ,
.Xr drvctl 8 , .Xr drvctl 8 ,
@ -95,3 +126,22 @@ The
.Nm .Nm
driver was written by driver was written by
.An Brad Spencer Aq Mt brad@anduin.eldar.org . .An Brad Spencer Aq Mt brad@anduin.eldar.org .
.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.

View File

@ -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. # Device majors for Machine-Independent drivers.
# #
@ -96,3 +96,4 @@ device-major efi char 361 efi
device-major sht3xtemp char 362 sht3xtemp device-major sht3xtemp char 362 sht3xtemp
device-major scmd char 363 scmd device-major scmd char 363 scmd
device-major viocon char 364 viocon device-major viocon char 364 viocon
device-major gpioirq char 365 gpioirq

View File

@ -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 $ */ /* $OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $ */
/* /*
@ -23,7 +23,7 @@
#endif #endif
#include <sys/cdefs.h> #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. * General Purpose Input/Output framework.
@ -618,6 +618,14 @@ gpio_intr_str(void *gpio, struct gpio_pinmap *map, int pin, int irqmode,
return (true); return (true);
} }
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 int
gpio_npins(uint32_t mask) gpio_npins(uint32_t mask)
{ {

View File

@ -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 <brad@anduin.eldar.org> * Copyright (c) 2016, 2023 Brad Spencer <brad@anduin.eldar.org>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -17,32 +17,64 @@
*/ */
#include <sys/cdefs.h> #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/param.h>
#include <sys/systm.h> #include <sys/systm.h>
#include <sys/device.h> #include <sys/device.h>
#include <sys/device_impl.h>
#include <sys/gpio.h> #include <sys/gpio.h>
#include <sys/module.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> #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 { struct gpioirq_softc {
device_t sc_dev; device_t sc_dev;
device_t sc_parentdev;
void * sc_gpio; void * sc_gpio;
struct gpio_pinmap sc_map; struct gpio_pinmap sc_map;
int _map[GPIOIRQ_NPINS]; int _map[GPIOIRQ_NPINS];
char sc_intrstr[128]; struct gpioirq_iv sc_intrs[GPIOIRQ_NPINS];
void * sc_ih; int sc_npins;
kmutex_t sc_lock; kmutex_t sc_lock;
kmutex_t sc_read_mutex;
kmutex_t sc_dying_mutex;
bool sc_verbose; bool sc_verbose;
bool sc_functional; 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 #define GPIOIRQ_FLAGS_IRQMODE GPIO_INTR_MODE_MASK
@ -52,8 +84,9 @@ static int gpioirq_match(device_t, cfdata_t, void *);
static void gpioirq_attach(device_t, device_t, void *); static void gpioirq_attach(device_t, device_t, void *);
static int gpioirq_detach(device_t, int); static int gpioirq_detach(device_t, int);
static int gpioirq_activate(device_t, enum devact); static int gpioirq_activate(device_t, enum devact);
static int gpioirq_intr(void *); 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), CFATTACH_DECL_NEW(gpioirq, sizeof(struct gpioirq_softc),
gpioirq_match, gpioirq_attach, gpioirq_match, gpioirq_attach,
@ -61,20 +94,47 @@ CFATTACH_DECL_NEW(gpioirq, sizeof(struct gpioirq_softc),
extern struct cfdriver gpioirq_cd; 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 static int
gpioirq_match(device_t parent, cfdata_t cf, void *aux) gpioirq_match(device_t parent, cfdata_t cf, void *aux)
{ {
struct gpio_attach_args *ga = aux; struct gpio_attach_args *ga = aux;
int npins;
if (strcmp(ga->ga_dvname, cf->cf_name)) if (strcmp(ga->ga_dvname, cf->cf_name))
return (0); return (0);
if (ga->ga_offset == -1)
return (0);
npins = gpio_npins(ga->ga_mask); if (ga->ga_offset == -1)
if (npins > 1)
return (0); return (0);
return (1); return (1);
@ -85,18 +145,30 @@ gpioirq_attach(device_t parent, device_t self, void *aux)
{ {
struct gpioirq_softc *sc = device_private(self); struct gpioirq_softc *sc = device_private(self);
struct gpio_attach_args *ga = aux; struct gpio_attach_args *ga = aux;
int npins = gpio_npins(ga->ga_mask); int mask = ga->ga_mask;
int irqmode, flags; int irqmode, flags;
sc->sc_dev = self; sc->sc_dev = self;
sc->sc_parentdev = parent;
sc->sc_opened = false;
sc->sc_dying = false;
sc->sc_readpoolname = NULL;
/* Map pins */ /* Map pins */
sc->sc_gpio = ga->ga_gpio; sc->sc_gpio = ga->ga_gpio;
sc->sc_map.pm_map = sc->_map; 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, 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"); aprint_error(": can't map pins\n");
return; return;
} }
@ -109,40 +181,58 @@ gpioirq_attach(device_t parent, device_t self, void *aux)
irqmode = ga->ga_flags & GPIOIRQ_FLAGS_IRQMODE; irqmode = ga->ga_flags & GPIOIRQ_FLAGS_IRQMODE;
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
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);
if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 0, irqmode, for(int apin = 0; apin < sc->sc_npins; apin++) {
sc->sc_intrstr, sizeof(sc->sc_intrstr))) { if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, apin, irqmode,
aprint_error_dev(self, "failed to decode interrupt\n"); sc->sc_intrs[apin].sc_intrstr, sizeof(sc->sc_intrs[apin].sc_intrstr))) {
return; aprint_error_dev(self, "failed to decode interrupt\n");
} return;
}
if (!gpio_pin_irqmode_issupported(sc->sc_gpio, &sc->sc_map, 0, if (!gpio_pin_irqmode_issupported(sc->sc_gpio, &sc->sc_map, apin,
irqmode)) { irqmode)) {
aprint_error_dev(self, aprint_error_dev(self,
"irqmode not supported: %s\n", sc->sc_intrstr); "irqmode not supported: %s\n", sc->sc_intrs[apin].sc_intrstr);
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
return; return;
} }
flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 0); flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, apin);
flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) | flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
GPIO_PIN_INPUT; GPIO_PIN_INPUT;
if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 0, flags)) { 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"); aprint_error_dev(sc->sc_dev, "pin not capable of input\n");
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
return; return;
} }
sc->sc_ih = gpio_intr_establish(sc->sc_gpio, &sc->sc_map, 0, IPL_VM, /* These are static for each pin, so just stuff them in here,
irqmode | GPIO_INTR_MPSAFE, * so they don't need to be looked up again.
gpioirq_intr, sc); */
if (sc->sc_ih == NULL) { sc->sc_intrs[apin].i_thispin_index = apin;
aprint_error_dev(self, sc->sc_intrs[apin].i_thispin_num = gpioirq_index_to_pin_num(sc,apin);
"unable to establish interrupt on %s\n", sc->sc_intrstr); sc->sc_intrs[apin].i_parentunit = gpioirq_parent_unit(sc);
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); sc->sc_intrs[apin].sc = sc;
return;
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);
} }
aprint_normal_dev(self, "interrupting on %s\n", sc->sc_intrstr);
sc->sc_functional = true; sc->sc_functional = true;
} }
@ -150,33 +240,180 @@ gpioirq_attach(device_t parent, device_t self, void *aux)
int int
gpioirq_intr(void *arg) 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; int val;
mutex_enter(&sc->sc_lock); 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) if (sc->sc_verbose)
printf("%s: interrupt on %s --> %d\n", 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); 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); 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 int
gpioirq_detach(device_t self, int flags) gpioirq_detach(device_t self, int flags)
{ {
struct gpioirq_softc *sc = device_private(self); struct gpioirq_softc *sc = device_private(self);
struct gpioirq_read_q *q;
/* Clear the handler and disable the interrupt. */ /* 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. */ /* Release the pin. */
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); 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); return (0);
} }
@ -184,9 +421,11 @@ int
gpioirq_activate(device_t self, enum devact act) gpioirq_activate(device_t self, enum devact act)
{ {
struct gpioirq_softc *sc = device_private(self);
switch (act) { switch (act) {
case DVACT_DEACTIVATE: case DVACT_DEACTIVATE:
/* We don't really need to do anything. */ sc->sc_dying = true;
return (0); return (0);
default: default:
return (EOPNOTSUPP); return (EOPNOTSUPP);
@ -203,26 +442,39 @@ static int
gpioirq_modcmd(modcmd_t cmd, void *opaque) gpioirq_modcmd(modcmd_t cmd, void *opaque)
{ {
int error = 0; int error = 0;
#ifdef _MODULE
int bmaj = -1, cmaj = -1;
#endif
switch (cmd) { switch (cmd) {
case MODULE_CMD_INIT: case MODULE_CMD_INIT:
#ifdef _MODULE #ifdef _MODULE
error = config_init_component(cfdriver_ioconf_gpioirq, error = config_init_component(cfdriver_ioconf_gpioirq,
cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq); cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq);
if (error) if (error) {
aprint_error("%s: unable to init component\n", aprint_error("%s: unable to init component\n",
gpioirq_cd.cd_name); 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 #endif
break; return (error);
case MODULE_CMD_FINI: case MODULE_CMD_FINI:
#ifdef _MODULE #ifdef _MODULE
devsw_detach(NULL, &gpioirq_cdevsw);
config_fini_component(cfdriver_ioconf_gpioirq, config_fini_component(cfdriver_ioconf_gpioirq,
cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq); cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq);
#endif #endif
break; return (0);
default: default:
error = ENOTTY; return (ENOTTY);
} }
return (error);
} }

View File

@ -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 $ */ /* $OpenBSD: gpiovar.h,v 1.3 2006/01/14 12:33:49 grange Exp $ */
/* /*
@ -118,6 +118,7 @@ void * gpio_intr_establish(void *, struct gpio_pinmap *, int, int, int,
void gpio_intr_disestablish(void *, void *); void gpio_intr_disestablish(void *, void *);
bool gpio_intr_str(void *, struct gpio_pinmap *, int, int, bool gpio_intr_str(void *, struct gpio_pinmap *, int, int,
char *, size_t); char *, size_t);
int gpio_pin_to_pin_num(void *, struct gpio_pinmap *, int);
int gpio_lock(void *); int gpio_lock(void *);
void gpio_unlock(void *); void gpio_unlock(void *);