diff --git a/drivers/analog/Kconfig b/drivers/analog/Kconfig index 64fa435f4d6..7823a7851e9 100644 --- a/drivers/analog/Kconfig +++ b/drivers/analog/Kconfig @@ -33,6 +33,13 @@ config ADC_FIFOSIZE this is a ring buffer, the actual number of bytes that can be retained in buffer is (ADC_FIFOSIZE - 1). +config ADC_NPOLLWAITERS + int "Number of poll waiters" + default 2 + depends on !DISABLE_POLL + ---help--- + Maximum number of threads that can be waiting on poll. + config ADC_ADS1242 bool "TI ADS1242 support" default n diff --git a/drivers/analog/adc.c b/drivers/analog/adc.c index 0f8dc474ef8..1fbc5805588 100644 --- a/drivers/analog/adc.c +++ b/drivers/analog/adc.c @@ -75,6 +75,10 @@ static ssize_t adc_read(FAR struct file *fielp, FAR char *buffer, static int adc_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch, int32_t data); +static void adc_notify(FAR struct adc_dev_s *dev); +#ifndef CONFIG_DISABLE_POLL +static int adc_poll(FAR struct file *filep, struct pollfd *fds, bool setup); +#endif /**************************************************************************** * Private Data @@ -89,7 +93,10 @@ static const struct file_operations g_adc_fops = 0, /* seek */ adc_ioctl /* ioctl */ #ifndef CONFIG_DISABLE_POLL - , 0 /* poll */ + , adc_poll /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ #endif }; @@ -449,10 +456,7 @@ static int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch, int32_t data) fifo->af_tail = nexttail; - if (dev->ad_nrxwaiters > 0) - { - sem_post(&fifo->af_sem); - } + adc_notify(dev); errcode = OK; } @@ -460,6 +464,132 @@ static int adc_receive(FAR struct adc_dev_s *dev, uint8_t ch, int32_t data) return errcode; } +/**************************************************************************** + * Name: adc_pollnotify + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static void adc_pollnotify(FAR struct adc_dev_s *dev, uint32_t type) +{ + int i; + + for (i = 0; i < CONFIG_ADC_NPOLLWAITERS; i++) + { + struct pollfd *fds = dev->fds[i]; + if (fds) + { + fds->revents |= type; + sem_post(fds->sem); + } + } +} +#endif + +/**************************************************************************** + * Name: adc_notify + ****************************************************************************/ + +static void adc_notify(FAR struct adc_dev_s *dev) +{ + FAR struct adc_fifo_s *fifo = &dev->ad_recv; + + /* If there are threads waiting for read data, then signal one of them + * that the read data is available. + */ + + if (dev->ad_nrxwaiters > 0) + { + sem_post(&fifo->af_sem); + } + + /* If there are threads waiting on poll() for data to become available, + * then wake them up now. + */ + +#ifndef CONFIG_DISABLE_POLL + adc_pollnotify(dev, POLLIN); +#endif +} + +/************************************************************************************ + * Name: adc_poll + ************************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int adc_poll(FAR struct file *filep, struct pollfd *fds, bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct adc_dev_s *dev = inode->i_private; + irqstate_t flags; + int ret = 0; + int i; + + /* Interrupts must be disabled while accessing the list of poll structures + * and ad_recv FIFO. + */ + + flags = enter_critical_section(); + + if (setup) + { + /* Ignore waits that do not include POLLIN */ + + if ((fds->events & POLLIN) == 0) + { + ret = -EDEADLK; + goto return_with_irqdisabled; + } + + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < CONFIG_ADC_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!dev->fds[i]) + { + /* Bind the poll structure and this slot */ + + dev->fds[i] = fds; + fds->priv = &dev->fds[i]; + break; + } + } + + if (i >= CONFIG_ADC_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + goto return_with_irqdisabled; + } + + /* Should we immediately notify on any of the requested events? */ + + if (dev->ad_recv.af_head != dev->ad_recv.af_tail) + { + adc_pollnotify(dev, POLLIN); + } + } + else if (fds->priv) + { + /* This is a request to tear down the poll. */ + + struct pollfd **slot = (struct pollfd **)fds->priv; + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +return_with_irqdisabled: + leave_critical_section(flags); + return ret; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/include/nuttx/analog/adc.h b/include/nuttx/analog/adc.h index 73d839b0038..524773e7290 100644 --- a/include/nuttx/analog/adc.h +++ b/include/nuttx/analog/adc.h @@ -50,6 +50,7 @@ #include #include +#include #include #include #include @@ -72,6 +73,10 @@ # define CONFIG_ADC_FIFOSIZE 255 #endif +#if !defined(CONFIG_ADC_NPOLLWAITERS) +# define CONFIG_ADC_NPOLLWAITERS 2 +#endif + #define ADC_RESET(dev) ((dev)->ad_ops->ao_reset((dev))) #define ADC_SETUP(dev) ((dev)->ad_ops->ao_setup((dev))) #define ADC_SHUTDOWN(dev) ((dev)->ad_ops->ao_shutdown((dev))) @@ -183,8 +188,18 @@ struct adc_dev_s sem_t ad_closesem; /* Locks out new opens while close is in progress */ sem_t ad_recvsem; /* Used to wakeup user waiting for space in ad_recv.buffer */ struct adc_fifo_s ad_recv; /* Describes receive FIFO */ + + /* The following is a list of poll structures of threads waiting for + * driver events. The 'struct pollfd' reference for each open is also + * retained in the f_priv field of the 'struct file'. + */ + +#if !defined(CONFIG_DISABLE_POLL) + struct pollfd *fds[CONFIG_ADC_NPOLLWAITERS]; #endif +#endif /* CONFIG_ADC */ + /* Fields provided by lower half ADC logic */ FAR const struct adc_ops_s *ad_ops; /* Arch-specific operations */