GPIO driver: Add support for receiving signals from interrupt pins.

This commit is contained in:
Gregory Nutt 2016-07-23 13:53:06 -06:00
parent 9b9b721406
commit a932578e76
2 changed files with 162 additions and 111 deletions

View File

@ -41,6 +41,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h>
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
@ -53,6 +54,7 @@
* Private Function Prototypes * Private Function Prototypes
****************************************************************************/ ****************************************************************************/
static int gpio_handler(FAR struct gpio_dev_s *dev);
static int gpio_open(FAR struct file *filep); static int gpio_open(FAR struct file *filep);
static int gpio_close(FAR struct file *filep); static int gpio_close(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,
@ -66,23 +68,7 @@ static int gpio_ioctl(FAR struct file *filep, int cmd,
* Private Data * Private Data
****************************************************************************/ ****************************************************************************/
static const struct file_operations g_gpio_input_ops = static const struct file_operations g_gpio_drvrops =
{
gpio_open, /* open */
gpio_close, /* close */
gpio_read, /* read */
NULL, /* write */
NULL, /* seek */
gpio_ioctl /* ioctl */
#ifndef CONFIG_DISABLE_POLL
, NULL /* poll */
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL /* unlink */
#endif
};
static const struct file_operations g_gpio_output_ops =
{ {
gpio_open, /* open */ gpio_open, /* open */
gpio_close, /* close */ gpio_close, /* close */
@ -102,6 +88,21 @@ static const struct file_operations g_gpio_output_ops =
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: gpio_handler
*
* Description:
* Standard character driver open method.
*
****************************************************************************/
static int gpio_handler(FAR struct gpio_dev_s *dev)
{
DEBUGASSERT(dev != NULL);
kill(dev->gp_pid, dev->gp_signo);
return OK;
}
/**************************************************************************** /****************************************************************************
* Name: gpio_open * Name: gpio_open
* *
@ -167,7 +168,7 @@ static ssize_t gpio_write(FAR struct file *filep, FAR const char *buffer,
static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg) static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{ {
FAR struct inode *inode; FAR struct inode *inode;
FAR struct gpio_common_dev_s *dev; FAR struct gpio_dev_s *dev;
int ret; int ret;
DEBUGASSERT(filep != NULL && filep->f_inode != NULL); DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
@ -177,20 +178,16 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
switch (cmd) switch (cmd)
{ {
/* Command: GPIO_WRITE /* Command: GPIOC_WRITE
* Description: Set the value of an output GPIO * Description: Set the value of an output GPIO
* Argument: 0=output a low value; 1=outut a high value * Argument: 0=output a low value; 1=outut a high value
*/ */
case GPIO_WRITE: case GPIOC_WRITE:
if (dev->gp_output) if (dev->gp_pintype == GPIO_OUTPUT_PIN)
{ {
FAR struct gpio_output_dev_s *outdev = DEBUGASSERT(arg == 0ul || arg == 1ul);
(FAR struct gpio_output_dev_s *)dev; ret = dev->gp_ops->go_write(dev, (int)arg);
DEBUGASSERT(outdev->gpout_write != NULL &&
((arg == 0UL) || (arg == 1UL)));
ret = outdev->gpout_write(outdev, (int)arg);
} }
else else
{ {
@ -198,38 +195,64 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
} }
break; break;
/* Command: GPIO_READ /* Command: GPIOC_READ
* Description: Read the value of an input or output GPIO * Description: Read the value of an input or output GPIO
* Argument: A pointer to an integer value to receive the result: * Argument: A pointer to an integer value to receive the result:
* 0=low value; 1=high value. * 0=low value; 1=high value.
*/ */
case GPIO_READ: case GPIOC_READ:
{ {
FAR int *ptr = (FAR int *)((uintptr_t)arg); FAR int *ptr = (FAR int *)((uintptr_t)arg);
DEBUGASSERT(ptr != NULL); DEBUGASSERT(ptr != NULL);
if (dev->gp_output) ret = dev->gp_ops->go_read(dev, ptr);
{
FAR struct gpio_output_dev_s *outdev =
(FAR struct gpio_output_dev_s *)dev;
DEBUGASSERT(outdev->gpout_read != NULL);
ret = outdev->gpout_read(outdev, ptr);
}
else
{
FAR struct gpio_input_dev_s *indev =
(FAR struct gpio_input_dev_s *)dev;
DEBUGASSERT(indev->gpin_read != NULL);
ret = indev->gpin_read(indev, ptr);
}
DEBUGASSERT(ret < 0 || *ptr == 0 || *ptr == 1); DEBUGASSERT(ret < 0 || *ptr == 0 || *ptr == 1);
} }
break; break;
/* Command: GPIOC_REGISTER
* Description: Register to receive a signal whenever there an
* interrupt is received on an input gpio pin. This
* feature, of course, depends upon interrupt GPIO
* support from the platform.
* Argument: The number of signal to be generated when the
* interrupt occurs.
*/
case GPIOC_REGISTER:
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)
{
/* Save signal information */
DEBUGASSERT(GOOD_SIGNO(arg));
dev->gp_pid = getpid();
dev->gp_signo = (uint8_t)arg;
/* Register our handler */
ret = dev->gp_ops->go_attach(dev,
(pin_interrupt_t)gpio_handler);
if (ret >= 0)
{
/* Enable pin interrupts */
ret = dev->gp_ops->go_enable(dev, true);
}
}
}
else
{
ret = -EACCES;
}
break;
/* Unrecognized command */ /* Unrecognized command */
default: default:
@ -245,41 +268,63 @@ static int gpio_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
****************************************************************************/ ****************************************************************************/
/**************************************************************************** /****************************************************************************
* Name: gpio_input_register * Name: gpio_pin_register
* *
* Description: * Description:
* Register GPIO input pin device driver. * Register GPIO pin device driver.
* *
****************************************************************************/ ****************************************************************************/
int gpio_input_register(FAR struct gpio_input_dev_s *dev, int minor) int gpio_pin_register(FAR struct gpio_dev_s *dev, int minor)
{ {
FAR const char *fmt;
char devname[16]; char devname[16];
int ret;
DEBUGASSERT(dev != NULL && !dev->gpin_output && dev->gpin_read != NULL && DEBUGASSERT(dev != NULL && dev->gp_ops != NULL && (unsigned int)minor < 100);
(unsigned int)minor < 100);
snprintf(devname, 16, "/dev/gpin%u", (unsigned int)minor); switch (dev->gp_pintype)
return register_driver(devname, &g_gpio_input_ops, 0444, dev); {
} case GPIO_INPUT_PIN:
{
DEBUGASSERT(dev->gp_ops->go_read != NULL);
fmt = "/dev/gpin%u";
}
break;
/**************************************************************************** case GPIO_OUTPUT_PIN:
* Name: gpio_output_register {
* DEBUGASSERT(dev->gp_ops->go_read != NULL &&
* Description: dev->gp_ops->go_write != NULL);
* Register GPIO output pin device driver. fmt = "/dev/gpout%u";
*
****************************************************************************/ }
break;
int gpio_output_register(FAR struct gpio_output_dev_s *dev, int minor) case GPIO_INTERRUPT_PIN:
{ {
char devname[16]; DEBUGASSERT(dev->gp_ops->go_read != NULL &&
dev->gp_ops->go_attach != NULL &&
dev->gp_ops->go_enable != NULL);
DEBUGASSERT(dev != NULL && dev->gpout_output && dev->gpout_read != NULL && /* Make sure that the pin interrupt is disabled */
dev->gpout_write != NULL &&(unsigned int)minor < 100);
snprintf(devname, 16, "/dev/gpout%u", (unsigned int)minor); ret = dev->gp_ops->go_enable(dev, false);
return register_driver(devname, &g_gpio_output_ops, 0222, dev); if (ret < 0)
{
return ret;
}
fmt = "/dev/gpint%u";
}
break;
default:
return -EINVAL;
}
snprintf(devname, 16, fmt, (unsigned int)minor);
return register_driver(devname, &g_gpio_drvrops, 0666, dev);
} }
#endif /* CONFIG_DEV_GPIO */ #endif /* CONFIG_DEV_GPIO */

View File

@ -47,64 +47,80 @@
* Pre-processor Definitions * Pre-processor Definitions
****************************************************************************/ ****************************************************************************/
/* Command: GPIO_WRITE /* Command: GPIOC_WRITE
* Description: Set the value of an output GPIO * Description: Set the value of an output GPIO
* Argument: 0=output a low value; 1=outut a high value * Argument: 0=output a low value; 1=outut a high value
* *
* Command: GPIO_READ * Command: GPIOC_READ
* Description: Read the value of an input or output GPIO * Description: Read the value of an input or output GPIO
* Argument: A pointer to an integer value to receive the result: * Argument: A pointer to an integer value to receive the result:
* 0=low value; 1=high value. * 0=low value; 1=high value.
*
* Command: GPIOC_REGISTER
* Description: Register to receive a signal whenever there an interrupt
* is received on an input gpio pin. This feature, of course,
* depends upon interrupt GPIO support from the platform.
* Argument: The number of signal to be generated when the interrupt
* occurs.
*/ */
#define GPIO_WRITE _GPIOC(1) #define GPIOC_WRITE _GPIOC(1)
#define GPIO_READ _GPIOC(2) #define GPIOC_READ _GPIOC(2)
#define GPIOC_REGISTER _GPIOC(3)
/**************************************************************************** /****************************************************************************
* Public Types * Public Types
****************************************************************************/ ****************************************************************************/
/* Common interface definition. Must be cast-compatible with struct /* Identifies the type of the GPIO pin */
* gpio_input_dev_s and struct gpio_output_dev_s
*/
struct gpio_common_dev_s enum gpio_pintype_e
{ {
bool gp_output; GPIO_INPUT_PIN = 0,
uint8_t gp_unused[3]; GPIO_OUTPUT_PIN,
GPIO_INTERRUPT_PIN
}; };
/* The interface to a GPIO input pin */ /* Interrupt callback */
struct gpio_input_dev_s typedef CODE int (*pin_interrupt_t)(FAR struct gpio_dev_s *dev);
/* Pin interface definition. */
struct gpio_dev_s;
struct gpio_operations_s
{ {
/* Common fields */ /* Interface methods */
bool gpin_output; CODE int (*go_read)(FAR struct gpio_dev_s *dev, FAR int *value);
uint8_t gpin_unused[3]; CODE int (*go_write)(FAR struct gpio_dev_s *dev, int value);
CODE int (*go_attach)(FAR struct gpio_dev_s *dev,
/* Fields unique to input pins */ pin_interrupt_t callback);
CODE int (*go_enable)(FAR struct gpio_dev_s *dev, bool enable);
CODE int (*gpin_read)(FAR struct gpio_input_dev_s *dev, FAR int *value);
/* Lower-half private definitions may follow */
}; };
/* The interface to a GPIO input pin */ /* Pin interface definition. */
struct gpio_output_dev_s struct gpio_dev_s
{ {
/* Common fields */ /* Information provided from the lower half driver to the upper half
* driver when gpio_pin_register() is called.
*/
bool gpout_output; uint8_t gp_pintype; /* See enum gpio_pintype_e */;
uint8_t gpout_unused[3];
/* Fields unique to output pins */ /* Writable storage used by the upper half driver */
CODE int (*gpout_read)(FAR struct gpio_output_dev_s *dev, FAR int *value); uint8_t gp_signo; /* signo to use when signaling a GPIO interrupt */
CODE int (*gpout_write)(FAR struct gpio_output_dev_s *dev, int value); pid_t gp_pid; /* The task to be signalled */
/* Lower-half private definitions may follow */ /* Read-only pointer to GPIO device operations (also provided by the
* lower half driver).
*/
FAR const struct gpio_operations_s *gp_ops;
/* Device specific information may follow */
}; };
/**************************************************************************** /****************************************************************************
@ -120,24 +136,14 @@ extern "C"
#endif #endif
/**************************************************************************** /****************************************************************************
* Name: gpio_input_register * Name: gpio_pin_register
* *
* Description: * Description:
* Register GPIO input pin device driver. * Register GPIO pin device driver.
* *
****************************************************************************/ ****************************************************************************/
int gpio_input_register(FAR struct gpio_input_dev_s *dev, int minor); int gpio_pin_register(FAR struct gpio_dev_s *dev, int minor);
/****************************************************************************
* Name: gpio_output_register
*
* Description:
* Register GPIO output pin device driver.
*
****************************************************************************/
int gpio_output_register(FAR struct gpio_output_dev_s *dev, int minor);
#ifdef __cplusplus #ifdef __cplusplus
} }