drivers/gpio: add poll function for gpio device
Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
parent
997be40003
commit
28815fb7c5
@ -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
|
||||
****************************************************************************/
|
||||
|
@ -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. */
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user