Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F111569674
D27398.id80066.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
20 KB
Referenced Files
None
Subscribers
None
D27398.id80066.diff
View Options
Index: sys/dev/gpio/gpiobus.c
===================================================================
--- sys/dev/gpio/gpiobus.c
+++ sys/dev/gpio/gpiobus.c
@@ -143,6 +143,15 @@
/* Cannot mix pull-up/pull-down together. */
if (flags & GPIO_PIN_PULLUP && flags & GPIO_PIN_PULLDOWN)
return (EINVAL);
+ /* Cannot mix output and interrupt flags together */
+ if (flags & GPIO_PIN_OUTPUT && flags & GPIO_INTR_MASK)
+ return (EINVAL);
+ /* Only one interrupt flag can be defined at once */
+ if ((flags & GPIO_INTR_MASK) & ((flags & GPIO_INTR_MASK) - 1))
+ return (EINVAL);
+ /* The interrupt attached flag cannot be set */
+ if (flags & GPIO_INTR_ATTACHED)
+ return (EINVAL);
return (0);
}
Index: sys/dev/gpio/gpioc.c
===================================================================
--- sys/dev/gpio/gpioc.c
+++ sys/dev/gpio/gpioc.c
@@ -35,8 +35,15 @@
#include <sys/conf.h>
#include <sys/gpio.h>
#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/sigio.h>
+#include <sys/signalvar.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/selinfo.h>
#include <sys/module.h>
#include <dev/gpio/gpiobusvar.h>
@@ -47,30 +54,414 @@
#undef GPIOC_DEBUG
#ifdef GPIOC_DEBUG
#define dprintf printf
+#define ddevice_printf device_printf
#else
#define dprintf(x, arg...)
+#define ddevice_printf(dev, x, arg...)
#endif
-static int gpioc_probe(device_t dev);
-static int gpioc_attach(device_t dev);
-static int gpioc_detach(device_t dev);
+struct gpioc_softc {
+ device_t sc_dev; /* gpiocX dev */
+ device_t sc_pdev; /* gpioX dev */
+ struct cdev *sc_ctl_dev; /* controller device */
+ int sc_unit;
+ int sc_npins;
+ struct gpioc_pin_intr *sc_pin_intr;
+};
+struct gpioc_pin_intr {
+ struct gpioc_softc *sc;
+ gpio_pin_t pin;
+ bool config_locked;
+ int intr_rid;
+ struct resource *intr_res;
+ void *intr_cookie;
+ struct mtx mtx;
+ SLIST_HEAD(gpioc_privs_list, gpioc_privs) privs;
+};
+
+struct gpioc_cdevpriv {
+ struct gpioc_softc *sc;
+ uint32_t last_intr_pin;
+ struct selinfo selinfo;
+ bool async;
+ struct sigio *sigio;
+ struct mtx mtx;
+ SLIST_HEAD(gpioc_pins_list, gpioc_pins) pins;
+};
+
+struct gpioc_privs {
+ struct gpioc_cdevpriv *priv;
+ SLIST_ENTRY(gpioc_privs) next;
+};
+
+struct gpioc_pins {
+ struct gpioc_pin_intr *pin;
+ SLIST_ENTRY(gpioc_pins) next;
+};
+
+static MALLOC_DEFINE(M_GPIOC, "gpioc", "gpioc device data");
+
+static int gpioc_allocate_pin_intr(struct gpioc_pin_intr*, uint32_t);
+static int gpioc_release_pin_intr(struct gpioc_pin_intr*);
+static int gpioc_attach_priv_pin(struct gpioc_cdevpriv*,
+ struct gpioc_pin_intr*);
+static int gpioc_detach_priv_pin(struct gpioc_cdevpriv*,
+ struct gpioc_pin_intr*);
+static bool gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv*,
+ struct gpioc_pin_intr *intr_conf);
+static uint32_t gpioc_get_intr_config(struct gpioc_softc*,
+ struct gpioc_cdevpriv*, uint32_t pin);
+static int gpioc_set_intr_config(struct gpioc_softc*,
+ struct gpioc_cdevpriv*, uint32_t, uint32_t);
+static void gpioc_interrupt_handler(void*);
+
+static int gpioc_kqread(struct knote*, long);
+static void gpioc_kqdetach(struct knote*);
+
+static int gpioc_probe(device_t dev);
+static int gpioc_attach(device_t dev);
+static int gpioc_detach(device_t dev);
+
+static void gpioc_cdevpriv_dtor(void*);
+
+static d_open_t gpioc_open;
+static d_read_t gpioc_read;
static d_ioctl_t gpioc_ioctl;
+static d_poll_t gpioc_poll;
+static d_kqfilter_t gpioc_kqfilter;
static struct cdevsw gpioc_cdevsw = {
.d_version = D_VERSION,
+ .d_open = gpioc_open,
+ .d_read = gpioc_read,
.d_ioctl = gpioc_ioctl,
+ .d_poll = gpioc_poll,
+ .d_kqfilter = gpioc_kqfilter,
.d_name = "gpioc",
};
-struct gpioc_softc {
- device_t sc_dev; /* gpiocX dev */
- device_t sc_pdev; /* gpioX dev */
- struct cdev *sc_ctl_dev; /* controller device */
- int sc_unit;
+static struct filterops gpioc_read_filterops = {
+ .f_isfd = true,
+ .f_attach = NULL,
+ .f_detach = gpioc_kqdetach,
+ .f_event = gpioc_kqread,
+ .f_touch = NULL
};
static int
+gpioc_allocate_pin_intr(struct gpioc_pin_intr *intr_conf, uint32_t flags)
+{
+ int err;
+
+ intr_conf->config_locked = true;
+ mtx_unlock(&intr_conf->mtx);
+
+ intr_conf->intr_res = gpio_alloc_intr_resource(intr_conf->pin->dev,
+ &intr_conf->intr_rid, RF_ACTIVE, intr_conf->pin, flags);
+ if (intr_conf->intr_res == NULL) {
+ err = ENXIO;
+ goto error_exit;
+ }
+
+ err = bus_setup_intr(intr_conf->pin->dev, intr_conf->intr_res,
+ INTR_TYPE_MISC | INTR_MPSAFE, NULL, gpioc_interrupt_handler,
+ intr_conf, &intr_conf->intr_cookie);
+ if (err != 0)
+ goto error_exit;
+
+ intr_conf->pin->flags = flags;
+
+error_exit:
+ mtx_lock(&intr_conf->mtx);
+ intr_conf->config_locked = false;
+ wakeup(&intr_conf->config_locked);
+
+ return (err);
+}
+
+static int
+gpioc_release_pin_intr(struct gpioc_pin_intr *intr_conf)
+{
+ int err;
+
+ intr_conf->config_locked = true;
+ mtx_unlock(&intr_conf->mtx);
+
+ if (intr_conf->intr_cookie != NULL) {
+ err = bus_teardown_intr(intr_conf->pin->dev,
+ intr_conf->intr_res, intr_conf->intr_cookie);
+ if (err != 0)
+ goto error_exit;
+ else
+ intr_conf->intr_cookie = NULL;
+ }
+
+ if (intr_conf->intr_res != NULL) {
+ err = bus_release_resource(intr_conf->pin->dev, SYS_RES_IRQ,
+ intr_conf->intr_rid, intr_conf->intr_res);
+ if (err != 0)
+ goto error_exit;
+ else {
+ intr_conf->intr_rid = 0;
+ intr_conf->intr_res = NULL;
+ }
+ }
+
+ intr_conf->pin->flags = 0;
+ err = 0;
+
+error_exit:
+ mtx_lock(&intr_conf->mtx);
+ intr_conf->config_locked = false;
+ wakeup(&intr_conf->config_locked);
+
+ return (err);
+}
+
+static int
+gpioc_attach_priv_pin(struct gpioc_cdevpriv *priv,
+ struct gpioc_pin_intr *intr_conf)
+{
+ struct gpioc_privs *priv_link;
+ struct gpioc_pins *pin_link;
+ unsigned int consistency_a, consistency_b;
+
+ consistency_a = 0;
+ consistency_b = 0;
+ mtx_assert(&intr_conf->mtx, MA_OWNED);
+ mtx_lock(&priv->mtx);
+ SLIST_FOREACH(priv_link, &intr_conf->privs, next) {
+ if (priv_link->priv == priv)
+ consistency_a++;
+ }
+ KASSERT(consistency_a <= 1,
+ ("inconsistent links between pin config and cdevpriv"));
+ SLIST_FOREACH(pin_link, &priv->pins, next) {
+ if (pin_link->pin == intr_conf)
+ consistency_b++;
+ }
+ KASSERT(consistency_a == consistency_b,
+ ("inconsistent links between pin config and cdevpriv"));
+ if (consistency_a == 1 && consistency_b == 1) {
+ mtx_unlock(&priv->mtx);
+ return (EEXIST);
+ }
+ priv_link = malloc(sizeof(struct gpioc_privs), M_GPIOC,
+ M_NOWAIT | M_ZERO);
+ if (priv_link == NULL)
+ {
+ mtx_unlock(&priv->mtx);
+ return (ENOMEM);
+ }
+ pin_link = malloc(sizeof(struct gpioc_pins), M_GPIOC,
+ M_NOWAIT | M_ZERO);
+ if (pin_link == NULL) {
+ mtx_unlock(&priv->mtx);
+ return (ENOMEM);
+ }
+ priv_link->priv = priv;
+ pin_link->pin = intr_conf;
+ SLIST_INSERT_HEAD(&intr_conf->privs, priv_link, next);
+ SLIST_INSERT_HEAD(&priv->pins, pin_link, next);
+ mtx_unlock(&priv->mtx);
+
+ return (0);
+}
+
+static int
+gpioc_detach_priv_pin(struct gpioc_cdevpriv *priv,
+ struct gpioc_pin_intr *intr_conf)
+{
+ struct gpioc_privs *priv_link, *priv_link_temp;
+ struct gpioc_pins *pin_link, *pin_link_temp;
+ unsigned int consistency_a, consistency_b;
+
+ consistency_a = 0;
+ consistency_b = 0;
+ mtx_assert(&intr_conf->mtx, MA_OWNED);
+ mtx_lock(&priv->mtx);
+ SLIST_FOREACH_SAFE(priv_link, &intr_conf->privs, next, priv_link_temp) {
+ if (priv_link->priv == priv) {
+ SLIST_REMOVE(&intr_conf->privs, priv_link, gpioc_privs,
+ next);
+ free(priv_link, M_GPIOC);
+ consistency_a++;
+ }
+ }
+ KASSERT(consistency_a <= 1,
+ ("inconsistent links between pin config and cdevpriv"));
+ SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) {
+ if (pin_link->pin == intr_conf) {
+ SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next);
+ free(pin_link, M_GPIOC);
+ consistency_b++;
+ }
+ }
+ KASSERT(consistency_a == consistency_b,
+ ("inconsistent links between pin config and cdevpriv"));
+ mtx_unlock(&priv->mtx);
+
+ return (0);
+}
+
+static bool
+gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv *priv,
+ struct gpioc_pin_intr *intr_conf)
+{
+ struct gpioc_privs *priv_link;
+
+ mtx_assert(&intr_conf->mtx, MA_OWNED);
+
+ if (SLIST_EMPTY(&intr_conf->privs))
+ return (true);
+
+ SLIST_FOREACH(priv_link, &intr_conf->privs, next) {
+ if (priv_link->priv != priv)
+ return (false);
+ }
+
+ return (true);
+}
+
+
+static uint32_t
+gpioc_get_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv,
+ uint32_t pin)
+{
+ struct gpioc_pin_intr *intr_conf = &sc->sc_pin_intr[pin];
+ struct gpioc_privs *priv_link;
+ uint32_t flags;
+
+ flags = intr_conf->pin->flags;
+
+ if (flags == 0)
+ return (0);
+
+ mtx_lock(&intr_conf->mtx);
+ SLIST_FOREACH(priv_link, &intr_conf->privs, next) {
+ if (priv_link->priv == priv) {
+ flags |= GPIO_INTR_ATTACHED;
+ break;
+ }
+ }
+ mtx_unlock(&intr_conf->mtx);
+
+ return (flags);
+}
+
+static int
+gpioc_set_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv,
+ uint32_t pin, uint32_t flags)
+{
+ struct gpioc_pin_intr *intr_conf = &sc->sc_pin_intr[pin];
+ int res;
+
+ res = 0;
+ if (intr_conf->pin->flags == 0 && flags == 0) {
+ /* No interrupt configured and none requested: Do nothing. */
+ return (0);
+ }
+ mtx_lock(&intr_conf->mtx);
+ while (intr_conf->config_locked == true)
+ mtx_sleep(&intr_conf->config_locked, &intr_conf->mtx, 0,
+ "gpicfg", 0);
+ if (intr_conf->pin->flags == 0 && flags != 0) {
+ /*
+ * No interrupt is configured, but one is requested: Allocate
+ * and setup interrupt on the according pin.
+ */
+ res = gpioc_allocate_pin_intr(intr_conf, flags);
+ if (res == 0)
+ res = gpioc_attach_priv_pin(priv, intr_conf);
+ if (res == EEXIST)
+ res = 0;
+ } else if (intr_conf->pin->flags == flags) {
+ /*
+ * Same interrupt requested as already configured: Attach the
+ * cdevpriv to the corresponding pin.
+ */
+ res = gpioc_attach_priv_pin(priv, intr_conf);
+ if (res == EEXIST)
+ res = 0;
+ } else if (intr_conf->pin->flags != 0 && flags == 0) {
+ /*
+ * Interrupt configured, but none requested: Teardown and
+ * release the pin when no other cdevpriv is attached. Otherwise
+ * just detach pin and cdevpriv from each other.
+ */
+ if (gpioc_intr_reconfig_allowed(priv, intr_conf)) {
+ res = gpioc_release_pin_intr(intr_conf);
+ }
+ if (res == 0)
+ res = gpioc_detach_priv_pin(priv, intr_conf);
+ } else {
+ /*
+ * Other flag requested than configured: Reconfigure when no
+ * other cdevpriv is are attached to the pin.
+ */
+ if (!gpioc_intr_reconfig_allowed(priv, intr_conf))
+ res = EBUSY;
+ else {
+ res = gpioc_release_pin_intr(intr_conf);
+ if (res == 0)
+ res = gpioc_allocate_pin_intr(intr_conf, flags);
+ if (res == 0)
+ res = gpioc_attach_priv_pin(priv, intr_conf);
+ if (res == EEXIST)
+ res = 0;
+ }
+ }
+ mtx_unlock(&intr_conf->mtx);
+
+ return (res);
+}
+
+static void
+gpioc_interrupt_handler(void *arg)
+{
+ struct gpioc_pin_intr *intr_conf;
+ struct gpioc_privs *privs;
+ struct gpioc_softc *sc;
+
+ intr_conf = arg;
+ sc = intr_conf->sc;
+
+ mtx_lock(&intr_conf->mtx);
+
+ if (intr_conf->config_locked == true) {
+ ddevice_printf(sc->sc_dev, "Interrupt configuration in "
+ "progress. Discarding interrupt on pin %d.\n",
+ intr_conf->pin->pin);
+ mtx_unlock(&intr_conf->mtx);
+ return;
+ }
+
+ if (SLIST_EMPTY(&intr_conf->privs)) {
+ ddevice_printf(sc->sc_dev, "No file descriptor associated with "
+ "occurred interrupt on pin %d.\n", intr_conf->pin->pin);
+ mtx_unlock(&intr_conf->mtx);
+ return;
+ }
+
+ SLIST_FOREACH(privs, &intr_conf->privs, next) {
+ mtx_lock(&privs->priv->mtx);
+ if (privs->priv->last_intr_pin != -1)
+ ddevice_printf(sc->sc_dev, "Unhandled interrupt on pin "
+ "%d.\n", intr_conf->pin->pin);
+ privs->priv->last_intr_pin = intr_conf->pin->pin;
+ wakeup(privs->priv);
+ selwakeup(&privs->priv->selinfo);
+ KNOTE_LOCKED(&privs->priv->selinfo.si_note, 0);
+ if (privs->priv->async == true && privs->priv->sigio != NULL)
+ pgsigio(&privs->priv->sigio, SIGIO, 0);
+ mtx_unlock(&privs->priv->mtx);
+ }
+
+ mtx_unlock(&intr_conf->mtx);
+}
+
+static int
gpioc_probe(device_t dev)
{
device_set_desc(dev, "GPIO controller");
@@ -88,6 +479,22 @@
sc->sc_dev = dev;
sc->sc_pdev = device_get_parent(dev);
sc->sc_unit = device_get_unit(dev);
+
+ err = GPIO_PIN_MAX(sc->sc_pdev, &sc->sc_npins);
+ if (err != 0)
+ return (err);
+ sc->sc_pin_intr = malloc(sizeof(struct gpioc_pin_intr) * sc->sc_npins,
+ M_GPIOC, M_WAITOK | M_ZERO);
+ for (int i = 0; i <= sc->sc_npins; i++) {
+ sc->sc_pin_intr[i].pin = malloc(sizeof(struct gpiobus_pin),
+ M_GPIOC, M_WAITOK | M_ZERO);
+ sc->sc_pin_intr[i].sc = sc;
+ sc->sc_pin_intr[i].pin->pin = i;
+ sc->sc_pin_intr[i].pin->dev = sc->sc_pdev;
+ mtx_init(&sc->sc_pin_intr[i].mtx, "gpioc pin", NULL, MTX_DEF);
+ SLIST_INIT(&sc->sc_pin_intr[i].privs);
+ }
+
make_dev_args_init(&devargs);
devargs.mda_devsw = &gpioc_cdevsw;
devargs.mda_uid = UID_ROOT;
@@ -112,12 +519,126 @@
if (sc->sc_ctl_dev)
destroy_dev(sc->sc_ctl_dev);
+ for (int i = 0; i <= sc->sc_npins; i++) {
+ mtx_destroy(&sc->sc_pin_intr[i].mtx);
+ free(&sc->sc_pin_intr[i].pin, M_GPIOC);
+ }
+ free(sc->sc_pin_intr, M_GPIOC);
+
if ((err = bus_generic_detach(dev)) != 0)
return (err);
return (0);
}
+static void
+gpioc_cdevpriv_dtor(void *data)
+{
+ struct gpioc_cdevpriv *priv;
+ struct gpioc_privs *priv_link, *priv_link_temp;
+ struct gpioc_pins *pin_link, *pin_link_temp;
+ unsigned int consistency;
+
+ priv = data;
+
+ SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) {
+ consistency = 0;
+ mtx_lock(&pin_link->pin->mtx);
+ while (pin_link->pin->config_locked == true)
+ mtx_sleep(&pin_link->pin->config_locked,
+ &pin_link->pin->mtx, 0, "gpicfg", 0);
+ SLIST_FOREACH_SAFE(priv_link, &pin_link->pin->privs, next,
+ priv_link_temp) {
+ if (priv_link->priv == priv) {
+ SLIST_REMOVE(&pin_link->pin->privs, priv_link,
+ gpioc_privs, next);
+ free(priv_link, M_GPIOC);
+ consistency++;
+ }
+ }
+ KASSERT(consistency == 1,
+ ("inconsistent links between pin config and cdevpriv"));
+ if (gpioc_intr_reconfig_allowed(priv, pin_link->pin)) {
+ gpioc_release_pin_intr(pin_link->pin);
+ }
+ mtx_unlock(&pin_link->pin->mtx);
+ SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next);
+ free(pin_link, M_GPIOC);
+ }
+
+ wakeup(&priv);
+ knlist_clear(&priv->selinfo.si_note, 0);
+ seldrain(&priv->selinfo);
+ knlist_destroy(&priv->selinfo.si_note);
+ funsetown(&priv->sigio);
+
+ mtx_destroy(&priv->mtx);
+ free(data, M_GPIOC);
+}
+
+static int
+gpioc_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+ struct gpioc_cdevpriv *priv;
+ int err;
+
+ priv = malloc(sizeof(*priv), M_GPIOC, M_WAITOK | M_ZERO);
+ err = devfs_set_cdevpriv(priv, gpioc_cdevpriv_dtor);
+ if (err != 0) {
+ gpioc_cdevpriv_dtor(priv);
+ return (err);
+ }
+ priv->sc = dev->si_drv1;
+ priv->last_intr_pin = -1;
+ mtx_init(&priv->mtx, "gpioc priv", NULL, MTX_DEF);
+ knlist_init_mtx(&priv->selinfo.si_note, &priv->mtx);
+
+ return (0);
+}
+
+static int
+gpioc_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+ struct gpioc_cdevpriv *priv;
+ uint32_t last_intr_pin;
+ int err;
+
+ if (uio->uio_resid < sizeof(priv->last_intr_pin))
+ return EINVAL;
+
+ err = devfs_get_cdevpriv((void **)&priv);
+ if (err != 0)
+ return err;
+
+ mtx_lock(&priv->mtx);
+ while (priv->last_intr_pin == -1) {
+ if (SLIST_EMPTY(&priv->pins)) {
+ err = ENXIO;
+ break;
+ } else if (ioflag & O_NONBLOCK) {
+ err = EWOULDBLOCK;
+ break;
+ } else {
+ err = mtx_sleep(priv, &priv->mtx, PCATCH, "gpintr", 0);
+ if (err != 0)
+ break;
+ }
+ }
+
+ if (err == 0 && priv->last_intr_pin != -1)
+ {
+ last_intr_pin = priv->last_intr_pin;
+ priv->last_intr_pin = -1;
+ mtx_unlock(&priv->mtx);
+ err = uiomove(&last_intr_pin, sizeof(last_intr_pin), uio);
+ }
+ else {
+ mtx_unlock(&priv->mtx);
+ }
+
+ return (err);
+}
+
static int
gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag,
struct thread *td)
@@ -125,6 +646,7 @@
device_t bus;
int max_pin, res;
struct gpioc_softc *sc = cdev->si_drv1;
+ struct gpioc_cdevpriv *priv;
struct gpio_pin pin;
struct gpio_req req;
struct gpio_access_32 *a32;
@@ -148,6 +670,11 @@
/* Fail early */
if (res)
break;
+ res = devfs_get_cdevpriv((void **)&priv);
+ if (res)
+ break;
+ pin.gp_flags |= gpioc_get_intr_config(sc, priv,
+ pin.gp_pin);
GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps);
GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name);
bcopy(&pin, arg, sizeof(pin));
@@ -155,12 +682,19 @@
case GPIOSETCONFIG:
bcopy(arg, &pin, sizeof(pin));
dprintf("set config pin %d\n", pin.gp_pin);
- res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps);
+ res = devfs_get_cdevpriv((void **)&priv);
if (res == 0)
+ res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin,
+ &caps);
+ if (res == 0)
res = gpio_check_flags(caps, pin.gp_flags);
if (res == 0)
res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin,
- pin.gp_flags);
+ (pin.gp_flags & ~GPIO_INTR_MASK));
+ if (res == 0)
+ res = gpioc_set_intr_config(sc, priv,
+ pin.gp_pin,
+ (pin.gp_flags & GPIO_INTR_MASK));
break;
case GPIOGET:
bcopy(arg, &req, sizeof(req));
@@ -199,12 +733,125 @@
res = GPIO_PIN_CONFIG_32(sc->sc_pdev, c32->first_pin,
c32->num_pins, c32->pin_flags);
break;
+ case FIONBIO:
+ /*
+ * This dummy handler is necessary to prevent fcntl()
+ * from failing. The actual handling of non-blocking IO
+ * is done using the O_NONBLOCK ioflag passed to the
+ * read() syscall.
+ */
+ res = 0;
+ break;
+ case FIOASYNC:
+ res = devfs_get_cdevpriv((void **)&priv);
+ if (res == 0) {
+ if (*(int *)arg == FASYNC)
+ priv->async = true;
+ else
+ priv->async = false;
+ }
+ break;
+ case FIOGETOWN:
+ res = devfs_get_cdevpriv((void **)&priv);
+ if (res == 0)
+ *(int *)arg = fgetown(&priv->sigio);
+ break;
+ case FIOSETOWN:
+ res = devfs_get_cdevpriv((void **)&priv);
+ if (res == 0)
+ res = fsetown(*(int *)arg, &priv->sigio);
+ break;
default:
return (ENOTTY);
break;
}
return (res);
+}
+
+static int
+gpioc_poll(struct cdev *dev, int events, struct thread *td)
+{
+ struct gpioc_cdevpriv *priv;
+ int err;
+ int revents;
+
+ revents = 0;
+
+ err = devfs_get_cdevpriv((void **)&priv);
+ if (err != 0) {
+ revents = POLLERR;
+ return (revents);
+ }
+
+ if (SLIST_EMPTY(&priv->pins)) {
+ revents = POLLHUP;
+ return (revents);
+ }
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (priv->last_intr_pin != -1)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(td, &priv->selinfo);
+ }
+
+ return (revents);
+}
+
+static int
+gpioc_kqfilter(struct cdev *dev, struct knote *kn)
+{
+ struct gpioc_cdevpriv *priv;
+ struct knlist *knlist;
+ int err;
+
+ err = devfs_get_cdevpriv((void **)&priv);
+ if (err != 0)
+ return err;
+
+ if (SLIST_EMPTY(&priv->pins))
+ return (ENXIO);
+
+ switch(kn->kn_filter) {
+ case EVFILT_READ:
+ kn->kn_fop = &gpioc_read_filterops;
+ kn->kn_hook = (void *)priv;
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ knlist = &priv->selinfo.si_note;
+ knlist_add(knlist, kn, 0);
+
+ return (0);
+}
+
+static int
+gpioc_kqread(struct knote *kn, long hint)
+{
+ struct gpioc_cdevpriv *priv = kn->kn_hook;
+
+ if (SLIST_EMPTY(&priv->pins)) {
+ kn->kn_flags |= EV_EOF;
+ return (1);
+ } else {
+ if (priv->last_intr_pin != -1) {
+ kn->kn_data = sizeof(priv->last_intr_pin);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static void
+gpioc_kqdetach(struct knote *kn)
+{
+ struct gpioc_cdevpriv *priv = kn->kn_hook;
+ struct knlist *knlist = &priv->selinfo.si_note;
+
+ knlist_remove(knlist, kn, 0);
}
static device_method_t gpioc_methods[] = {
Index: sys/sys/gpio.h
===================================================================
--- sys/sys/gpio.h
+++ sys/sys/gpio.h
@@ -81,9 +81,11 @@
#define GPIO_INTR_EDGE_RISING 0x00040000 /* edge trigger, rising */
#define GPIO_INTR_EDGE_FALLING 0x00080000 /* edge trigger, falling */
#define GPIO_INTR_EDGE_BOTH 0x00100000 /* edge trigger, both */
-#define GPIO_INTR_MASK (GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | \
- GPIO_INTR_EDGE_RISING | \
- GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH)
+#define GPIO_INTR_ATTACHED 0x00200000 /* interrupt attached to file */
+#define GPIO_INTR_MASK (GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | \
+ GPIO_INTR_EDGE_RISING | \
+ GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH | \
+ GPIO_INTR_ATTACHED)
struct gpio_pin {
uint32_t gp_pin; /* pin number */
Index: usr.sbin/gpioctl/gpioctl.c
===================================================================
--- usr.sbin/gpioctl/gpioctl.c
+++ usr.sbin/gpioctl/gpioctl.c
@@ -332,6 +332,8 @@
flag = str2cap(argv[i]);
if (flag < 0)
fail("Invalid flag: %s\n", argv[i]);
+ else if ((flag & GPIO_INTR_MASK) != 0)
+ fail("Interrupt capability %s cannot be set as configuration flag\n", argv[i]);
flags |= flag;
}
pin.g_pin = pinn;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Mar 6, 9:49 AM (2 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17012224
Default Alt Text
D27398.id80066.diff (20 KB)
Attached To
Mode
D27398: Provide userland notification of gpio pin changes (aka "userland gpio interrupts").
Attached
Detach File
Event Timeline
Log In to Comment