mirror of
https://github.com/apache/nuttx.git
synced 2026-05-30 05:16:47 +08:00
drivers/gpio: add poll function for gpio device
Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
This commit is contained in:
+165
-55
@@ -32,6 +32,7 @@
|
|||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
#include <nuttx/fs/fs.h>
|
#include <nuttx/fs/fs.h>
|
||||||
#include <nuttx/spinlock.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_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,
|
static ssize_t gpio_read(FAR struct file *filep, FAR char *buffer,
|
||||||
size_t buflen);
|
size_t buflen);
|
||||||
static ssize_t gpio_write(FAR struct file *filep, FAR const char *buffer,
|
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 off_t gpio_seek(FAR struct file *filep, off_t offset, int whence);
|
||||||
static int gpio_ioctl(FAR struct file *filep, int cmd,
|
static int gpio_ioctl(FAR struct file *filep, int cmd,
|
||||||
unsigned long arg);
|
unsigned long arg);
|
||||||
|
static int gpio_poll(FAR struct file *filep,
|
||||||
|
FAR struct pollfd *fds, bool setup);
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Data
|
* Private Data
|
||||||
@@ -58,12 +62,15 @@ static int gpio_ioctl(FAR struct file *filep, int cmd,
|
|||||||
|
|
||||||
static const struct file_operations g_gpio_drvrops =
|
static const struct file_operations g_gpio_drvrops =
|
||||||
{
|
{
|
||||||
NULL, /* open */
|
gpio_open, /* open */
|
||||||
NULL, /* close */
|
NULL, /* close */
|
||||||
gpio_read, /* read */
|
gpio_read, /* read */
|
||||||
gpio_write, /* write */
|
gpio_write, /* write */
|
||||||
gpio_seek, /* seek */
|
gpio_seek, /* seek */
|
||||||
gpio_ioctl, /* ioctl */
|
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);
|
DEBUGASSERT(dev != NULL);
|
||||||
|
|
||||||
|
dev->int_count++;
|
||||||
|
|
||||||
|
poll_notify(dev->fds, CONFIG_DEV_GPIO_NSIGNALS, POLLIN);
|
||||||
|
|
||||||
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
|
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
|
||||||
{
|
{
|
||||||
FAR struct gpio_signal_s *signal = &dev->gp_signals[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;
|
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
|
* Name: gpio_read
|
||||||
*
|
*
|
||||||
@@ -138,7 +170,9 @@ static ssize_t gpio_read(FAR struct file *filep, FAR char *buffer,
|
|||||||
return 0;
|
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]);
|
ret = dev->gp_ops->go_read(dev, (FAR bool *)&buffer[0]);
|
||||||
if (ret < 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;
|
FAR struct gpio_dev_s *dev;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int ret;
|
int ret = OK;
|
||||||
int i;
|
int i;
|
||||||
int j = 0;
|
int j;
|
||||||
|
|
||||||
inode = filep->f_inode;
|
inode = filep->f_inode;
|
||||||
DEBUGASSERT(inode->i_private != NULL);
|
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);
|
FAR bool *ptr = (FAR bool *)((uintptr_t)arg);
|
||||||
DEBUGASSERT(ptr != NULL);
|
DEBUGASSERT(ptr != NULL);
|
||||||
|
|
||||||
|
filep->f_priv = (FAR void *)dev->int_count;
|
||||||
|
|
||||||
DEBUGASSERT(dev->gp_ops->go_read != NULL);
|
DEBUGASSERT(dev->gp_ops->go_read != NULL);
|
||||||
ret = dev->gp_ops->go_read(dev, ptr);
|
ret = dev->gp_ops->go_read(dev, ptr);
|
||||||
DEBUGASSERT(ret < 0 || *ptr == 0 || *ptr == 1);
|
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);
|
DEBUGASSERT(ptr != NULL);
|
||||||
|
|
||||||
*ptr = (enum gpio_pintype_e)dev->gp_pintype;
|
*ptr = (enum gpio_pintype_e)dev->gp_pintype;
|
||||||
ret = OK;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -345,44 +380,52 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
case GPIOC_REGISTER:
|
case GPIOC_REGISTER:
|
||||||
if (arg && dev->gp_pintype >= GPIO_INTERRUPT_PIN)
|
if (dev->gp_pintype >= GPIO_INTERRUPT_PIN)
|
||||||
{
|
{
|
||||||
pid = nxsched_getpid();
|
flags = enter_critical_section();
|
||||||
flags = spin_lock_irqsave(NULL);
|
if (arg)
|
||||||
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
|
|
||||||
{
|
{
|
||||||
FAR struct gpio_signal_s *signal = &dev->gp_signals[i];
|
pid = nxsched_getpid();
|
||||||
|
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
|
||||||
if (signal->gp_pid == 0 || signal->gp_pid == pid)
|
|
||||||
{
|
{
|
||||||
memcpy(&signal->gp_event, (FAR void *)arg,
|
FAR struct gpio_signal_s *signal = &dev->gp_signals[i];
|
||||||
sizeof(signal->gp_event));
|
|
||||||
signal->gp_pid = pid;
|
if (signal->gp_pid == 0 || signal->gp_pid == pid)
|
||||||
ret = OK;
|
{
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(NULL, flags);
|
if (dev->register_count++ > 0)
|
||||||
|
|
||||||
if (i == 0)
|
|
||||||
{
|
{
|
||||||
/* Register our handler */
|
leave_critical_section(flags);
|
||||||
|
break;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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
|
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)
|
if (dev->gp_pintype >= GPIO_INTERRUPT_PIN)
|
||||||
{
|
{
|
||||||
pid = nxsched_getpid();
|
pid = nxsched_getpid();
|
||||||
flags = spin_lock_irqsave(NULL);
|
flags = enter_critical_section();
|
||||||
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
|
for (i = 0; i < CONFIG_DEV_GPIO_NSIGNALS; i++)
|
||||||
{
|
{
|
||||||
if (pid == dev->gp_signals[i].gp_pid)
|
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;
|
dev->gp_signals[j].gp_pid = 0;
|
||||||
nxsig_cancel_notification(&dev->gp_signals[j].gp_work);
|
nxsig_cancel_notification(&dev->gp_signals[j].gp_work);
|
||||||
ret = OK;
|
|
||||||
break;
|
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
|
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 */
|
/* Pintype remains the same, no need to change anything */
|
||||||
|
|
||||||
ret = OK;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,6 +572,76 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|||||||
return ret;
|
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
|
* Public Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|||||||
@@ -152,6 +152,14 @@ struct gpio_dev_s
|
|||||||
|
|
||||||
uint8_t gp_pintype; /* See enum gpio_pintype_e */
|
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 */
|
/* Writable storage used by the upper half driver */
|
||||||
|
|
||||||
struct gpio_signal_s gp_signals[CONFIG_DEV_GPIO_NSIGNALS];
|
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 const struct gpio_operations_s *gp_ops;
|
||||||
|
|
||||||
|
FAR struct pollfd *fds[CONFIG_DEV_GPIO_NSIGNALS];
|
||||||
|
|
||||||
/* Device specific, lower-half information may follow. */
|
/* Device specific, lower-half information may follow. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user