drivers/gpio: add poll function for gpio device

Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
dongjiuzhu1 2023-11-23 14:16:55 +08:00 committed by Xiang Xiao
parent 997be40003
commit 28815fb7c5
2 changed files with 175 additions and 55 deletions

View File

@ -32,6 +32,7 @@
#include <debug.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h>
#include <nuttx/fs/fs.h>
#include <nuttx/spinlock.h>
@ -44,6 +45,7 @@
****************************************************************************/
static int gpio_handler(FAR struct gpio_dev_s *dev, uint8_t pin);
static int gpio_open(FAR struct file *filep);
static ssize_t gpio_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t gpio_write(FAR struct file *filep, FAR const char *buffer,
@ -51,6 +53,8 @@ static ssize_t gpio_write(FAR struct file *filep, FAR const char *buffer,
static off_t gpio_seek(FAR struct file *filep, off_t offset, int whence);
static int gpio_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static int gpio_poll(FAR struct file *filep,
FAR struct pollfd *fds, bool setup);
/****************************************************************************
* Private Data
@ -58,12 +62,15 @@ static int gpio_ioctl(FAR struct file *filep, int cmd,
static const struct file_operations g_gpio_drvrops =
{
NULL, /* open */
gpio_open, /* open */
NULL, /* close */
gpio_read, /* read */
gpio_write, /* write */
gpio_seek, /* seek */
gpio_ioctl, /* ioctl */
NULL, /* mmap */
NULL, /* truncate */
gpio_poll, /* poll */
};
/****************************************************************************
@ -84,6 +91,10 @@ static int gpio_handler(FAR struct gpio_dev_s *dev, uint8_t pin)
DEBUGASSERT(dev != NULL);
dev->int_count++;
poll_notify(dev->fds, CONFIG_DEV_GPIO_NSIGNALS, POLLIN);
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
{
FAR struct gpio_signal_s *signal = &dev->gp_signals[i];
@ -100,6 +111,27 @@ static int gpio_handler(FAR struct gpio_dev_s *dev, uint8_t pin)
return OK;
}
/****************************************************************************
* Name: gpio_open
*
* Description:
* Standard character driver open method.
*
****************************************************************************/
static int gpio_open(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct gpio_dev_s *dev;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private != NULL);
dev = inode->i_private;
filep->f_priv = (FAR void *)dev->int_count;
return OK;
}
/****************************************************************************
* Name: gpio_read
*
@ -138,7 +170,9 @@ static ssize_t gpio_read(FAR struct file *filep, FAR char *buffer,
return 0;
}
/* Read the GPIO value */
/* Update interrupt count and read the GPIO value */
filep->f_priv = (FAR void *)dev->int_count;
ret = dev->gp_ops->go_read(dev, (FAR bool *)&buffer[0]);
if (ret < 0)
@ -272,9 +306,9 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
FAR struct gpio_dev_s *dev;
irqstate_t flags;
pid_t pid;
int ret;
int ret = OK;
int i;
int j = 0;
int j;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private != NULL);
@ -313,6 +347,8 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
FAR bool *ptr = (FAR bool *)((uintptr_t)arg);
DEBUGASSERT(ptr != NULL);
filep->f_priv = (FAR void *)dev->int_count;
DEBUGASSERT(dev->gp_ops->go_read != NULL);
ret = dev->gp_ops->go_read(dev, ptr);
DEBUGASSERT(ret < 0 || *ptr == 0 || *ptr == 1);
@ -331,7 +367,6 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
DEBUGASSERT(ptr != NULL);
*ptr = (enum gpio_pintype_e)dev->gp_pintype;
ret = OK;
}
break;
@ -345,44 +380,52 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
*/
case GPIOC_REGISTER:
if (arg && dev->gp_pintype >= GPIO_INTERRUPT_PIN)
if (dev->gp_pintype >= GPIO_INTERRUPT_PIN)
{
pid = nxsched_getpid();
flags = spin_lock_irqsave(NULL);
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
flags = enter_critical_section();
if (arg)
{
FAR struct gpio_signal_s *signal = &dev->gp_signals[i];
if (signal->gp_pid == 0 || signal->gp_pid == pid)
pid = nxsched_getpid();
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
{
memcpy(&signal->gp_event, (FAR void *)arg,
sizeof(signal->gp_event));
signal->gp_pid = pid;
ret = OK;
FAR struct gpio_signal_s *signal = &dev->gp_signals[i];
if (signal->gp_pid == 0 || signal->gp_pid == pid)
{
memcpy(&signal->gp_event, (FAR void *)arg,
sizeof(signal->gp_event));
signal->gp_pid = pid;
break;
}
}
if (i == CONFIG_DEV_GPIO_NSIGNALS)
{
leave_critical_section(flags);
ret = -EBUSY;
break;
}
}
spin_unlock_irqrestore(NULL, flags);
if (i == 0)
if (dev->register_count++ > 0)
{
/* Register our handler */
DEBUGASSERT(dev->gp_ops->go_attach != NULL);
ret = dev->gp_ops->go_attach(dev,
(pin_interrupt_t)gpio_handler);
if (ret >= 0)
{
/* Enable pin interrupts */
DEBUGASSERT(dev->gp_ops->go_enable != NULL);
ret = dev->gp_ops->go_enable(dev, true);
}
leave_critical_section(flags);
break;
}
else if (i == CONFIG_DEV_GPIO_NSIGNALS)
leave_critical_section(flags);
/* Register our handler */
DEBUGASSERT(dev->gp_ops->go_attach != NULL);
ret = dev->gp_ops->go_attach(dev,
(pin_interrupt_t)gpio_handler);
if (ret >= 0)
{
ret = -EBUSY;
/* Enable pin interrupts */
DEBUGASSERT(dev->gp_ops->go_enable != NULL);
ret = dev->gp_ops->go_enable(dev, true);
}
}
else
@ -400,7 +443,7 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
if (dev->gp_pintype >= GPIO_INTERRUPT_PIN)
{
pid = nxsched_getpid();
flags = spin_lock_irqsave(NULL);
flags = enter_critical_section();
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
{
if (pid == dev->gp_signals[i].gp_pid)
@ -421,30 +464,28 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
dev->gp_signals[j].gp_pid = 0;
nxsig_cancel_notification(&dev->gp_signals[j].gp_work);
ret = OK;
break;
}
}
spin_unlock_irqrestore(NULL, flags);
if (i == 0 && j == 0)
{
/* Make sure that the pin interrupt is disabled */
DEBUGASSERT(dev->gp_ops->go_enable != NULL);
ret = dev->gp_ops->go_enable(dev, false);
if (ret >= 0)
{
/* Detach the handler */
DEBUGASSERT(dev->gp_ops->go_attach != NULL);
ret = dev->gp_ops->go_attach(dev, NULL);
}
}
else if (i == CONFIG_DEV_GPIO_NSIGNALS)
if (--dev->register_count > 0)
{
ret = -EINVAL;
leave_critical_section(flags);
break;
}
leave_critical_section(flags);
/* Make sure that the pin interrupt is disabled */
DEBUGASSERT(dev->gp_ops->go_enable != NULL);
ret = dev->gp_ops->go_enable(dev, false);
if (ret >= 0)
{
/* Detach the handler */
DEBUGASSERT(dev->gp_ops->go_attach != NULL);
ret = dev->gp_ops->go_attach(dev, NULL);
}
}
else
@ -476,7 +517,6 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
/* Pintype remains the same, no need to change anything */
ret = OK;
break;
}
@ -532,6 +572,76 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
return ret;
}
/****************************************************************************
* Name: gpio_poll
*
* Description:
* Poll method for gpio device.
*
****************************************************************************/
static int gpio_poll(FAR struct file *filep,
FAR struct pollfd *fds, bool setup)
{
FAR struct inode *inode = filep->f_inode;
FAR struct gpio_dev_s *dev = inode->i_private;
irqstate_t flags;
int ret = OK;
int i;
/* Are we setting up the poll? Or tearing it down? */
flags = enter_critical_section();
if (setup)
{
/* This is a request to set up the poll. Find an available
* slot for the poll structure reference
*/
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
{
/* Find an available slot */
if (dev->fds[i] == NULL)
{
/* Bind the poll structure and this slot */
dev->fds[i] = fds;
fds->priv = &dev->fds[i];
/* Report if a event is pending */
if (dev->int_count != (uintptr_t)(filep->f_priv))
{
poll_notify(&fds, 1, POLLIN);
}
break;
}
}
if (i >= CONFIG_DEV_GPIO_NSIGNALS)
{
fds->priv = NULL;
ret = -EBUSY;
}
}
else if (fds->priv != NULL)
{
/* This is a request to tear down the poll. */
FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv;
/* Remove all memory of the poll setup */
*slot = NULL;
fds->priv = NULL;
}
leave_critical_section(flags);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/

View File

@ -152,6 +152,14 @@ struct gpio_dev_s
uint8_t gp_pintype; /* See enum gpio_pintype_e */
/* Number of times the device has been registered by ioctl */
uint8_t register_count;
/* Number of times interrupt occured */
uintptr_t int_count;
/* Writable storage used by the upper half driver */
struct gpio_signal_s gp_signals[CONFIG_DEV_GPIO_NSIGNALS];
@ -162,6 +170,8 @@ struct gpio_dev_s
FAR const struct gpio_operations_s *gp_ops;
FAR struct pollfd *fds[CONFIG_DEV_GPIO_NSIGNALS];
/* Device specific, lower-half information may follow. */
};