drivers/ioexpander: Support multiple registrations of GPIO signal events.

This commit is contained in:
Xiang Xiao 2018-11-08 08:29:22 -06:00 committed by Gregory Nutt
parent a9ff43d93c
commit 71a6244198
3 changed files with 99 additions and 20 deletions

View File

@ -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

View File

@ -41,6 +41,7 @@
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <errno.h>
@ -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

View File

@ -45,6 +45,7 @@
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <nuttx/fs/ioctl.h>
@ -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).