From 71a6244198db7c025c41790fa767985c1cdf0914 Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Thu, 8 Nov 2018 08:29:22 -0600 Subject: [PATCH] drivers/ioexpander: Support multiple registrations of GPIO signal events. --- drivers/ioexpander/Kconfig | 8 +++ drivers/ioexpander/gpio.c | 99 +++++++++++++++++++++++++++------ include/nuttx/ioexpander/gpio.h | 12 +++- 3 files changed, 99 insertions(+), 20 deletions(-) diff --git a/drivers/ioexpander/Kconfig b/drivers/ioexpander/Kconfig index 619e9105b7..7108d1c021 100644 --- a/drivers/ioexpander/Kconfig +++ b/drivers/ioexpander/Kconfig @@ -181,6 +181,14 @@ config DEV_GPIO Enables a simple GPIO input/output driver to support application- space testing of hardware. +config DEV_GPIO_NSIGNALS + int "Max number of signals" + default 1 + depends on DEV_GPIO + ---help--- + The maximum number of signals that can be registered with the GPIO + driver + config GPIO_LOWER_HALF bool "GPIO Lower Half" default n diff --git a/drivers/ioexpander/gpio.c b/drivers/ioexpander/gpio.c index f534a7160e..76193c81a4 100644 --- a/drivers/ioexpander/gpio.c +++ b/drivers/ioexpander/gpio.c @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -99,8 +100,22 @@ static const struct file_operations g_gpio_drvrops = static int gpio_handler(FAR struct gpio_dev_s *dev, uint8_t pin) { + int i; + DEBUGASSERT(dev != NULL); - (void)nxsig_kill(dev->gp_pid, dev->gp_signo); + + for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++) + { + FAR struct gpio_signal_s *signal = &dev->gp_signals[i]; + + if (signal->gp_pid == 0) + { + break; + } + + nxsig_notification(signal->gp_pid, &signal->gp_event, SI_QUEUE); + } + return OK; } @@ -170,7 +185,11 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode; FAR struct gpio_dev_s *dev; + irqstate_t flags; + pid_t pid; int ret; + int i; + int j; DEBUGASSERT(filep != NULL && filep->f_inode != NULL); inode = filep->f_inode; @@ -237,20 +256,28 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) */ case GPIOC_REGISTER: - if (dev->gp_pintype == GPIO_INTERRUPT_PIN) + if (arg && dev->gp_pintype == GPIO_INTERRUPT_PIN) { - /* Make sure that the pin interrupt is disabled */ - - ret = dev->gp_ops->go_enable(dev, false); - if (ret >= 0) + pid = getpid(); + flags = spin_lock_irqsave(); + for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++) { - /* Save signal information */ + FAR struct gpio_signal_s *signal = &dev->gp_signals[i]; - DEBUGASSERT(GOOD_SIGNO(arg)); + if (signal->gp_pid == 0 || signal->gp_pid == pid) + { + memcpy(&signal->gp_event, (FAR void *)arg, + sizeof(signal->gp_event)); + signal->gp_pid = pid; + ret = OK; + break; + } + } - dev->gp_pid = getpid(); - dev->gp_signo = (uint8_t)arg; + spin_unlock_irqrestore(flags); + if (i == 0) + { /* Register our handler */ ret = dev->gp_ops->go_attach(dev, @@ -262,6 +289,10 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) ret = dev->gp_ops->go_enable(dev, true); } } + else if (i == CONFIG_DEV_GPIO_NSIGNALS) + { + ret = -EBUSY; + } } else { @@ -277,17 +308,49 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) case GPIOC_UNREGISTER: if (dev->gp_pintype == GPIO_INTERRUPT_PIN) { - /* Make sure that the pin interrupt is disabled */ - - ret = dev->gp_ops->go_enable(dev, false); - if (ret >= 0) + pid = getpid(); + flags = spin_lock_irqsave(); + for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++) { - /* Detach the handler */ + if (pid == dev->gp_signals[i].gp_pid) + { + for (j = i + 1; j < CONFIG_DEV_GPIO_NSIGNALS; j++) + { + if (dev->gp_signals[j].gp_pid == 0) + { + break; + } + } - ret = dev->gp_ops->go_attach(dev, NULL); + if (i != --j) + { + memcpy(&dev->gp_signals[i], &dev->gp_signals[j], + sizeof(dev->gp_signals[i])); + } - dev->gp_pid = 0; - dev->gp_signo = 0; + dev->gp_signals[j].gp_pid = 0; + ret = OK; + break; + } + } + + spin_unlock_irqrestore(flags); + + if (i == 0 && j == 0) + { + /* Make sure that the pin interrupt is disabled */ + + ret = dev->gp_ops->go_enable(dev, false); + if (ret >= 0) + { + /* Detach the handler */ + + ret = dev->gp_ops->go_attach(dev, NULL); + } + } + else if (i == CONFIG_DEV_GPIO_NSIGNALS) + { + ret = -EINVAL; } } else diff --git a/include/nuttx/ioexpander/gpio.h b/include/nuttx/ioexpander/gpio.h index f33ef99fad..026e13f119 100644 --- a/include/nuttx/ioexpander/gpio.h +++ b/include/nuttx/ioexpander/gpio.h @@ -45,6 +45,7 @@ #include #include #include +#include #include @@ -138,6 +139,14 @@ struct gpio_operations_s enum gpio_pintype_e pintype); }; + /* Signal information */ + +struct gpio_signal_s +{ + struct sigevent gp_event; + pid_t gp_pid; /* The task to be signaled */ +}; + /* Pin interface definition. Must lie in writable memory. */ struct gpio_dev_s @@ -150,8 +159,7 @@ struct gpio_dev_s /* Writable storage used by the upper half driver */ - uint8_t gp_signo; /* signo to use when signaling a GPIO interrupt */ - pid_t gp_pid; /* The task to be signalled */ + struct gpio_signal_s gp_signals[CONFIG_DEV_GPIO_NSIGNALS]; /* Read-only pointer to GPIO device operations (also provided by the * lower half driver).