For /dev/ reads against gpioirq(4) implement the following:
o O_NONBLOCK on reads o Add a d_poll function and associated sel[init|notify|record|destroy] calls to the driver so that select(2) and poll(2) work as expected. With these in place async use cases work against /dev/gpioirqN
This commit is contained in:
parent
79de40626c
commit
5749545531
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: gpioirq.c,v 1.2 2023/11/06 00:35:05 brad Exp $ */
|
||||
/* $NetBSD: gpioirq.c,v 1.3 2023/11/10 13:17:17 brad Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016, 2023 Brad Spencer <brad@anduin.eldar.org>
|
||||
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: gpioirq.c,v 1.2 2023/11/06 00:35:05 brad Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: gpioirq.c,v 1.3 2023/11/10 13:17:17 brad Exp $");
|
||||
|
||||
/*
|
||||
* GPIO driver that uses interrupts and can send that fact to userland.
|
||||
|
@ -34,6 +34,9 @@ __KERNEL_RCSID(0, "$NetBSD: gpioirq.c,v 1.2 2023/11/06 00:35:05 brad Exp $");
|
|||
#include <sys/pool.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/poll.h>
|
||||
|
||||
#include <dev/gpio/gpiovar.h>
|
||||
|
||||
|
@ -67,6 +70,7 @@ struct gpioirq_softc {
|
|||
kcondvar_t sc_cond_dying;
|
||||
pool_cache_t sc_readpool;
|
||||
char *sc_readpoolname;
|
||||
struct selinfo sc_rsel;
|
||||
SIMPLEQ_HEAD(,gpioirq_read_q) sc_read_queue;
|
||||
};
|
||||
|
||||
|
@ -97,6 +101,7 @@ extern struct cfdriver gpioirq_cd;
|
|||
static dev_type_open(gpioirq_open);
|
||||
static dev_type_read(gpioirq_read);
|
||||
static dev_type_close(gpioirq_close);
|
||||
static dev_type_poll(gpioirq_poll);
|
||||
const struct cdevsw gpioirq_cdevsw = {
|
||||
.d_open = gpioirq_open,
|
||||
.d_close = gpioirq_close,
|
||||
|
@ -105,7 +110,7 @@ const struct cdevsw gpioirq_cdevsw = {
|
|||
.d_ioctl = noioctl,
|
||||
.d_stop = nostop,
|
||||
.d_tty = notty,
|
||||
.d_poll = nopoll,
|
||||
.d_poll = gpioirq_poll,
|
||||
.d_mmap = nommap,
|
||||
.d_kqfilter = nokqfilter,
|
||||
.d_discard = nodiscard,
|
||||
|
@ -189,6 +194,7 @@ gpioirq_attach(device_t parent, device_t self, void *aux)
|
|||
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);
|
||||
selinit(&sc->sc_rsel);
|
||||
|
||||
for(int apin = 0; apin < sc->sc_npins; apin++) {
|
||||
if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, apin, irqmode,
|
||||
|
@ -263,6 +269,7 @@ gpioirq_intr(void *arg)
|
|||
q->parentunit = is->i_parentunit;
|
||||
q->theval = val;
|
||||
SIMPLEQ_INSERT_TAIL(&sc->sc_read_queue,q,read_q);
|
||||
selnotify(&sc->sc_rsel, POLLIN|POLLRDNORM, NOTE_SUBMIT);
|
||||
cv_signal(&sc->sc_condreadready);
|
||||
} else {
|
||||
aprint_error("Could not allocate memory for read pool\n");
|
||||
|
@ -304,6 +311,10 @@ gpioirq_read(dev_t dev, struct uio *uio, int flags)
|
|||
if (!sc)
|
||||
return (ENXIO);
|
||||
|
||||
if (sc->sc_dying) {
|
||||
return EIO;
|
||||
}
|
||||
|
||||
while (uio->uio_resid > 0) {
|
||||
any = 0;
|
||||
error = 0;
|
||||
|
@ -316,7 +327,11 @@ gpioirq_read(dev_t dev, struct uio *uio, int flags)
|
|||
any = 1;
|
||||
break;
|
||||
} else {
|
||||
error = cv_wait_sig(&sc->sc_condreadready,&sc->sc_read_mutex);
|
||||
if (flags & IO_NDELAY) {
|
||||
error = EWOULDBLOCK;
|
||||
} else {
|
||||
error = cv_wait_sig(&sc->sc_condreadready,&sc->sc_read_mutex);
|
||||
}
|
||||
if (sc->sc_dying)
|
||||
error = EIO;
|
||||
if (error == 0)
|
||||
|
@ -358,6 +373,10 @@ gpioirq_close(dev_t dev, int flags, int fmt, struct lwp *l)
|
|||
|
||||
sc = device_lookup_private(&gpioirq_cd, minor(dev));
|
||||
|
||||
if (sc->sc_dying) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
mutex_enter(&sc->sc_lock);
|
||||
while ((q = SIMPLEQ_FIRST(&sc->sc_read_queue)) != NULL) {
|
||||
SIMPLEQ_REMOVE_HEAD(&sc->sc_read_queue, read_q);
|
||||
|
@ -369,6 +388,31 @@ gpioirq_close(dev_t dev, int flags, int fmt, struct lwp *l)
|
|||
return(0);
|
||||
}
|
||||
|
||||
static int
|
||||
gpioirq_poll(dev_t dev, int events, struct lwp *l)
|
||||
{
|
||||
struct gpioirq_softc *sc;
|
||||
int revents = 0;
|
||||
|
||||
sc = device_lookup_private(&gpioirq_cd, minor(dev));
|
||||
|
||||
mutex_enter(&sc->sc_read_mutex);
|
||||
if (sc->sc_dying) {
|
||||
mutex_exit(&sc->sc_read_mutex);
|
||||
return POLLHUP;
|
||||
}
|
||||
|
||||
if ((events & (POLLIN | POLLRDNORM)) != 0) {
|
||||
if (!SIMPLEQ_EMPTY(&sc->sc_read_queue))
|
||||
revents |= events & (POLLIN | POLLRDNORM);
|
||||
else
|
||||
selrecord(l, &sc->sc_rsel);
|
||||
}
|
||||
|
||||
mutex_exit(&sc->sc_read_mutex);
|
||||
return revents;
|
||||
}
|
||||
|
||||
int
|
||||
gpioirq_detach(device_t self, int flags)
|
||||
{
|
||||
|
@ -413,6 +457,7 @@ gpioirq_detach(device_t self, int flags)
|
|||
|
||||
mutex_destroy(&sc->sc_read_mutex);
|
||||
mutex_destroy(&sc->sc_lock);
|
||||
seldestroy(&sc->sc_rsel);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue