From 77871fbede6c69d077501cc79168c77acb5181bb Mon Sep 17 00:00:00 2001 From: brad Date: Mon, 6 Nov 2023 00:35:05 +0000 Subject: [PATCH] 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. --- doc/CHANGES | 5 +- etc/MAKEDEV.tmpl | 14 +- share/man/man4/gpioirq.4 | 66 ++++++- sys/conf/majors | 3 +- sys/dev/gpio/gpio.c | 12 +- sys/dev/gpio/gpioirq.c | 366 +++++++++++++++++++++++++++++++++------ sys/dev/gpio/gpiovar.h | 3 +- 7 files changed, 397 insertions(+), 72 deletions(-) diff --git a/doc/CHANGES b/doc/CHANGES index 93037b2ae991..bbee8149b62f 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -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] diff --git a/etc/MAKEDEV.tmpl b/etc/MAKEDEV.tmpl index 3d0d32304dba..2e61ae58eb70 100644 --- a/etc/MAKEDEV.tmpl +++ b/etc/MAKEDEV.tmpl @@ -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 diff --git a/share/man/man4/gpioirq.4 b/share/man/man4/gpioirq.4 index f0fc522385fe..cd59d2527451 100644 --- a/share/man/man4/gpioirq.4 +++ b/share/man/man4/gpioirq.4 @@ -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 +.\" Copyright (c) 2016, 2023 Brad Spencer .\" .\" 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 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. diff --git a/sys/conf/majors b/sys/conf/majors index 7c73fb6f75f7..729455465376 100644 --- a/sys/conf/majors +++ b/sys/conf/majors @@ -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 efi device-major sht3xtemp char 362 sht3xtemp device-major scmd char 363 scmd device-major viocon char 364 viocon +device-major gpioirq char 365 gpioirq diff --git a/sys/dev/gpio/gpio.c b/sys/dev/gpio/gpio.c index e6cf976dfcab..98a597a43f00 100644 --- a/sys/dev/gpio/gpio.c +++ b/sys/dev/gpio/gpio.c @@ -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 -__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. @@ -618,6 +618,14 @@ gpio_intr_str(void *gpio, struct gpio_pinmap *map, int pin, int irqmode, 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 gpio_npins(uint32_t mask) { diff --git a/sys/dev/gpio/gpioirq.c b/sys/dev/gpio/gpioirq.c index 7f4834022be2..a2e257c12e6f 100644 --- a/sys/dev/gpio/gpioirq.c +++ b/sys/dev/gpio/gpioirq.c @@ -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 + * Copyright (c) 2016, 2023 Brad Spencer * * 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 -__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 #include #include +#include #include #include +#include +#include +#include +#include +#include #include -#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, cfdata_t, void *); 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 gpioirq_softc), 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 self, void *aux) { 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,40 +181,58 @@ gpioirq_attach(device_t parent, device_t self, void *aux) irqmode = ga->ga_flags & GPIOIRQ_FLAGS_IRQMODE; 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, - sc->sc_intrstr, sizeof(sc->sc_intrstr))) { - aprint_error_dev(self, "failed to decode interrupt\n"); - return; - } + 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, 0, - irqmode)) { - aprint_error_dev(self, - "irqmode not supported: %s\n", sc->sc_intrstr); - gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); - 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, 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; - } + 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; + } - 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; + /* 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); } - aprint_normal_dev(self, "interrupting on %s\n", sc->sc_intrstr); sc->sc_functional = true; } @@ -150,33 +240,180 @@ gpioirq_attach(device_t parent, device_t self, void *aux) 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); } diff --git a/sys/dev/gpio/gpiovar.h b/sys/dev/gpio/gpiovar.h index 05e69afa979d..9fc22c78e4ae 100644 --- a/sys/dev/gpio/gpiovar.h +++ b/sys/dev/gpio/gpiovar.h @@ -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 *, struct gpio_pinmap *, int, int, int, 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 *);