Module Name: src Committed By: bouyer Date: Sun Nov 26 11:45:16 UTC 2023
Modified Files: src/etc [netbsd-10]: MAKEDEV.tmpl src/share/man/man4 [netbsd-10]: gpioirq.4 src/sys/conf [netbsd-10]: majors src/sys/dev/gpio [netbsd-10]: gpio.c gpioirq.c gpiovar.h Log Message: Pull up following revision(s) (requested by brad in ticket #461): sys/dev/gpio/gpiovar.h: revision 1.19 share/man/man4/gpioirq.4: revision 1.4 via patch sys/dev/gpio/gpio.c: revision 1.73 etc/MAKEDEV.tmpl: revision 1.234 sys/conf/majors: revision 1.103 sys/dev/gpio/gpioirq.c: revision 1.2 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.232.2.1 -r1.232.2.2 src/etc/MAKEDEV.tmpl cvs rdiff -u -r1.2 -r1.2.14.1 src/share/man/man4/gpioirq.4 cvs rdiff -u -r1.102 -r1.102.4.1 src/sys/conf/majors cvs rdiff -u -r1.72 -r1.72.2.1 src/sys/dev/gpio/gpio.c cvs rdiff -u -r1.1 -r1.1.36.1 src/sys/dev/gpio/gpioirq.c cvs rdiff -u -r1.18 -r1.18.34.1 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/etc/MAKEDEV.tmpl diff -u src/etc/MAKEDEV.tmpl:1.232.2.1 src/etc/MAKEDEV.tmpl:1.232.2.2 --- src/etc/MAKEDEV.tmpl:1.232.2.1 Fri Jan 13 19:08:30 2023 +++ src/etc/MAKEDEV.tmpl Sun Nov 26 11:45:16 2023 @@ -1,5 +1,5 @@ #!/bin/sh - -# $NetBSD: MAKEDEV.tmpl,v 1.232.2.1 2023/01/13 19:08:30 martin Exp $ +# $NetBSD: MAKEDEV.tmpl,v 1.232.2.2 2023/11/26 11:45:16 bouyer 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.2 src/share/man/man4/gpioirq.4:1.2.14.1 --- src/share/man/man4/gpioirq.4:1.2 Sun May 20 12:08:46 2018 +++ src/share/man/man4/gpioirq.4 Sun Nov 26 11:45:16 2023 @@ -1,6 +1,6 @@ -.\" $NetBSD: gpioirq.4,v 1.2 2018/05/20 12:08:46 wiz Exp $ +.\" $NetBSD: gpioirq.4,v 1.2.14.1 2023/11/26 11:45:16 bouyer Exp $ .\" -.\" Copyright (c) 2016 Brad Spencer <b...@anduin.eldar.org> +.\" Copyright (c) 2016, 2023 Brad Spencer <b...@anduin.eldar.org> .\" .\" 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 intrerrupt as long as the pin is high. +Assert the interrupt 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 b...@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. Index: src/sys/conf/majors diff -u src/sys/conf/majors:1.102 src/sys/conf/majors:1.102.4.1 --- src/sys/conf/majors:1.102 Fri Aug 12 11:15:42 2022 +++ src/sys/conf/majors Sun Nov 26 11:45:16 2023 @@ -1,4 +1,4 @@ -# $NetBSD: majors,v 1.102 2022/08/12 11:15:42 riastradh Exp $ +# $NetBSD: majors,v 1.102.4.1 2023/11/26 11:45:16 bouyer 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.72.2.1 --- src/sys/dev/gpio/gpio.c:1.72 Tue Dec 13 21:50:43 2022 +++ src/sys/dev/gpio/gpio.c Sun Nov 26 11:45:16 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: gpio.c,v 1.72 2022/12/13 21:50:43 jakllsch Exp $ */ +/* $NetBSD: gpio.c,v 1.72.2.1 2023/11/26 11:45:16 bouyer 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.72.2.1 2023/11/26 11:45:16 bouyer 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.1.36.1 --- src/sys/dev/gpio/gpioirq.c:1.1 Sat May 19 14:15:39 2018 +++ src/sys/dev/gpio/gpioirq.c Sun Nov 26 11:45:16 2023 @@ -1,7 +1,7 @@ -/* $NetBSD: gpioirq.c,v 1.1 2018/05/19 14:15:39 thorpej Exp $ */ +/* $NetBSD: gpioirq.c,v 1.1.36.1 2023/11/26 11:45:16 bouyer Exp $ */ /* - * Copyright (c) 2016 Brad Spencer <b...@anduin.eldar.org> + * Copyright (c) 2016, 2023 Brad Spencer <b...@anduin.eldar.org> * * 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.1.36.1 2023/11/26 11:45:16 bouyer 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.18.34.1 --- src/sys/dev/gpio/gpiovar.h:1.18 Sat May 19 13:59:06 2018 +++ src/sys/dev/gpio/gpiovar.h Sun Nov 26 11:45:16 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: gpiovar.h,v 1.18 2018/05/19 13:59:06 thorpej Exp $ */ +/* $NetBSD: gpiovar.h,v 1.18.34.1 2023/11/26 11:45:16 bouyer 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 *);