nuttx/drivers/rc/lirc_dev.c

1048 lines
26 KiB
C
Raw Normal View History

/****************************************************************************
* drivers/rc/lirc_dev.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <sys/param.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <poll.h>
#include <fcntl.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/mm/circbuf.h>
#include <nuttx/rc/lirc_dev.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define DEVNAME_FMT "/dev/lirc%d"
#define DEVNAME_MAX 32
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure describes the state of the upper half driver */
struct lirc_upperhalf_s
{
struct list_node fh; /* list of struct lirc_fh_s object */
FAR struct lirc_lowerhalf_s *lower; /* the handle of lower half driver */
mutex_t lock; /* Manages exclusive access to lowerhalf */
bool gap; /* true if we're in a gap */
uint64_t gap_start; /* time when gap starts */
uint64_t gap_duration; /* duration of initial gap */
};
/* The structure describes an open lirc file */
struct lirc_fh_s
{
struct list_node node; /* list of open file handles */
FAR struct lirc_lowerhalf_s *lower; /* the pointer to lirc_lowerhalf_s */
struct circbuf_s buffer; /* buffer for incoming IR */
FAR struct pollfd *fds; /* poll structures of threads waiting for driver events */
sem_t waitsem; /* sem of wait buffer for ready */
int carrier_low; /* when setting the carrier range, first the low end must be
* set with an ioctl and then the high end with another ioctl
*/
unsigned char send_mode; /* lirc mode for sending, LIRC_MODE_PULSE LIRC_MODE_SCANCODE */
unsigned char rec_mode; /* lirc mode for receiving, LIRC_MODE_MODE2 LIRC_MODE_SCANCODE */
bool send_timeout_reports; /* report timeouts in lirc raw IR. */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int lirc_open(FAR struct file *filep);
static int lirc_close(FAR struct file *filep);
static int lirc_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
static int lirc_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
static ssize_t lirc_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
static ssize_t lirc_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_lirc_fops =
{
lirc_open, /* open */
lirc_close, /* close */
lirc_read, /* read */
lirc_write, /* write */
NULL, /* seek */
lirc_ioctl, /* ioctl */
NULL, /* mmap */
NULL, /* truncate */
lirc_poll /* poll */
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int lirc_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lirc_upperhalf_s *upper = inode->i_private;
FAR struct lirc_lowerhalf_s *lower = upper->lower;
FAR struct lirc_fh_s *fh;
irqstate_t flags;
int ret;
fh = kmm_zalloc(sizeof(*fh));
if (!fh)
{
return -ENOMEM;
}
switch (lower->ops->driver_type)
{
case LIRC_DRIVER_SCANCODE:
fh->rec_mode = LIRC_MODE_SCANCODE;
break;
default:
fh->rec_mode = LIRC_MODE_MODE2;
break;
}
if (lower->ops->tx_scancode)
{
fh->send_mode = LIRC_MODE_SCANCODE;
}
else if (lower->ops->tx_ir)
{
fh->send_mode = LIRC_MODE_PULSE;
}
ret = circbuf_init(&fh->buffer, NULL, lower->buffer_bytes);
if (ret < 0)
{
goto buffer_err;
}
nxsem_init(&fh->waitsem, 0, 0);
fh->lower = lower;
fh->send_timeout_reports = true;
if (list_is_empty(&upper->fh))
{
ret = lower->ops->open(lower);
if (ret < 0)
{
goto open_err;
}
}
flags = enter_critical_section();
list_add_tail(&upper->fh, &fh->node);
leave_critical_section(flags);
filep->f_priv = fh;
return 0;
open_err:
nxsem_destroy(&fh->waitsem);
circbuf_uninit(&fh->buffer);
buffer_err:
kmm_free(fh);
return ret;
}
static int lirc_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lirc_upperhalf_s *upper = inode->i_private;
FAR struct lirc_fh_s *fh = filep->f_priv;
FAR struct lirc_lowerhalf_s *lower = fh->lower;
irqstate_t flags;
flags = enter_critical_section();
list_delete(&fh->node);
leave_critical_section(flags);
nxsem_destroy(&fh->waitsem);
circbuf_uninit(&fh->buffer);
kmm_free(fh);
if (list_is_empty(&upper->fh))
{
lower->ops->close(lower);
}
return 0;
}
static int lirc_poll(FAR struct file *filep,
FAR struct pollfd *fds, bool setup)
{
FAR struct lirc_fh_s *fh = filep->f_priv;
pollevent_t eventset = 0;
irqstate_t flags;
int ret = 0;
flags = enter_critical_section();
if (setup)
{
if (fh->fds)
{
ret = -EBUSY;
goto errout;
}
fh->fds = fds;
fds->priv = &fh->fds;
if (!circbuf_is_empty(&fh->buffer))
{
eventset |= POLLIN | POLLRDNORM;
}
poll_notify(&fds, 1, eventset);
}
else if (fds->priv != NULL)
{
FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv;
if (!slot)
{
ret = -EIO;
goto errout;
}
*slot = NULL;
fds->priv = NULL;
}
errout:
leave_critical_section(flags);
return ret;
}
static int lirc_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct lirc_fh_s *fh = filep->f_priv;
FAR struct lirc_lowerhalf_s *lower = fh->lower;
FAR struct lirc_upperhalf_s *upper = lower->priv;
FAR unsigned int *val = (FAR unsigned int *)(uintptr_t)arg;
int ret;
ret = nxmutex_lock(&upper->lock);
if (ret < 0)
{
return ret;
}
switch (cmd)
{
case LIRC_GET_FEATURES:
switch (lower->ops->driver_type)
{
case LIRC_DRIVER_SCANCODE:
*val = LIRC_CAN_REC_SCANCODE;
break;
case LIRC_DRIVER_IR_RAW:
*val = LIRC_CAN_REC_MODE2;
break;
default:
*val = 0;
break;
}
if (lower->rx_resolution)
{
*val |= LIRC_CAN_GET_REC_RESOLUTION;
}
if (lower->ops->tx_ir)
{
*val |= LIRC_CAN_SEND_PULSE;
}
if (lower->ops->tx_scancode)
{
*val |= LIRC_CAN_SEND_SCANCODE;
}
if (lower->ops->s_tx_mask)
{
*val |= LIRC_CAN_SET_TRANSMITTER_MASK;
}
if (lower->ops->s_tx_carrier)
{
*val |= LIRC_CAN_SET_SEND_CARRIER;
}
if (lower->ops->s_tx_duty_cycle)
{
*val |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
}
if (lower->ops->s_rx_carrier_range)
{
*val |= LIRC_CAN_SET_REC_CARRIER |
LIRC_CAN_SET_REC_CARRIER_RANGE;
}
if (lower->ops->s_learning_mode)
{
*val |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
}
if (lower->ops->s_carrier_report)
{
*val |= LIRC_CAN_MEASURE_CARRIER;
}
if (lower->max_timeout)
{
*val |= LIRC_CAN_SET_REC_TIMEOUT;
}
break;
/* mode support */
case LIRC_GET_REC_MODE:
if (lower->ops->driver_type == LIRC_DRIVER_IR_RAW_TX)
{
ret = -ENOTTY;
}
else
{
*val = fh->rec_mode;
}
break;
case LIRC_SET_REC_MODE:
switch (lower->ops->driver_type)
{
case LIRC_DRIVER_IR_RAW_TX:
ret = -ENOTTY;
break;
case LIRC_DRIVER_SCANCODE:
if (arg != LIRC_MODE_SCANCODE)
{
ret = -EINVAL;
}
break;
case LIRC_DRIVER_IR_RAW:
if (arg != LIRC_MODE_MODE2)
{
ret = -EINVAL;
}
break;
}
if (ret >= 0)
{
fh->rec_mode = arg;
}
break;
case LIRC_GET_SEND_MODE:
if (!lower->ops->tx_ir && !lower->ops->tx_scancode)
{
ret = -ENOTTY;
}
else
{
*val = fh->send_mode;
}
break;
case LIRC_SET_SEND_MODE:
if ((arg == LIRC_MODE_PULSE && lower->ops->tx_ir) ||
(arg == LIRC_MODE_SCANCODE && lower->ops->tx_scancode))
{
fh->send_mode = arg;
}
else
{
ret = -EINVAL;
}
break;
/* TX settings */
case LIRC_SET_TRANSMITTER_MASK:
if (!lower->ops->s_tx_mask)
{
ret = -ENOTTY;
}
else
{
ret = lower->ops->s_tx_mask(lower, arg);
}
break;
case LIRC_SET_SEND_CARRIER:
if (!lower->ops->s_tx_carrier)
{
ret = -ENOTTY;
}
else
{
ret = lower->ops->s_tx_carrier(lower, arg);
}
break;
case LIRC_SET_SEND_DUTY_CYCLE:
if (!lower->ops->s_tx_duty_cycle)
{
ret = -ENOTTY;
}
else if (arg <= 0 || arg >= 100)
{
ret = -EINVAL;
}
else
{
ret = lower->ops->s_tx_duty_cycle(lower, arg);
}
break;
/* RX settings */
case LIRC_SET_REC_CARRIER:
if (!lower->ops->s_rx_carrier_range)
{
ret = -ENOTTY;
}
else if (arg <= 0)
{
ret = -EINVAL;
}
else
{
ret = lower->ops->s_rx_carrier_range(lower,
fh->carrier_low, arg);
}
break;
case LIRC_SET_REC_CARRIER_RANGE:
if (!lower->ops->s_rx_carrier_range)
{
ret = -ENOTTY;
}
else if (arg <= 0)
{
ret = -EINVAL;
}
else
{
fh->carrier_low = arg;
}
break;
case LIRC_GET_REC_RESOLUTION:
if (!lower->rx_resolution)
{
ret = -ENOTTY;
}
else
{
*val = lower->rx_resolution;
}
break;
case LIRC_SET_WIDEBAND_RECEIVER:
if (!lower->ops->s_learning_mode)
{
ret = -ENOTTY;
}
else
{
ret = lower->ops->s_learning_mode(lower, !!arg);
}
break;
case LIRC_SET_MEASURE_CARRIER_MODE:
if (!lower->ops->s_carrier_report)
{
ret = -ENOTTY;
}
else
{
ret = lower->ops->s_carrier_report(lower, !!arg);
}
break;
/* Generic timeout support */
case LIRC_GET_MIN_TIMEOUT:
if (!lower->min_timeout)
{
ret = -ENOTTY;
}
else
{
*val = lower->min_timeout;
}
break;
case LIRC_GET_MAX_TIMEOUT:
if (!lower->max_timeout)
{
ret = -ENOTTY;
}
else
{
*val = lower->max_timeout;
}
break;
case LIRC_SET_REC_TIMEOUT:
if (!lower->max_timeout)
{
ret = -ENOTTY;
}
else
{
if (arg < lower->min_timeout || arg > lower->max_timeout)
{
ret = -EINVAL;
}
else if (lower->ops->s_timeout)
{
ret = lower->ops->s_timeout(lower, arg);
}
else
{
lower->timeout = arg;
}
}
break;
case LIRC_GET_REC_TIMEOUT:
if (!lower->timeout)
{
ret = -ENOTTY;
}
else
{
*val = lower->timeout;
}
break;
case LIRC_SET_REC_TIMEOUT_REPORTS:
if (lower->ops->driver_type != LIRC_DRIVER_IR_RAW)
{
ret = -ENOTTY;
}
else
{
fh->send_timeout_reports = !!arg;
}
break;
default:
ret = -ENOTTY;
}
nxmutex_unlock(&upper->lock);
return ret;
}
static ssize_t lirc_write_pulse(FAR struct file *filep,
FAR const char *buffer, size_t buflen)
{
FAR struct lirc_fh_s *fh = filep->f_priv;
FAR struct lirc_lowerhalf_s *lower = fh->lower;
size_t count;
if (buflen < sizeof(unsigned int) || buflen % sizeof(unsigned int))
{
return -EINVAL;
}
count = buflen / sizeof(unsigned int);
if (count % 2 == 0)
{
return -EINVAL;
}
/* tx_ir need sleep some time to wait for thr actual IR signal
* to be transmitted before returning
*/
return lower->ops->tx_ir(lower, (FAR unsigned int *)buffer, count);
}
static ssize_t lirc_write_scancode(FAR struct file *filep,
FAR const char *buffer, size_t buflen)
{
FAR struct lirc_fh_s *fh = filep->f_priv;
FAR struct lirc_lowerhalf_s *lower = fh->lower;
if (buflen != sizeof(struct lirc_scancode))
{
return -EINVAL;
}
/* tx_scancode need sleep some time to wait for thr actual IR signal
* to be transmitted before returning
*/
return lower->ops->tx_scancode(lower,
(FAR struct lirc_scancode *)buffer);
}
static ssize_t lirc_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct lirc_upperhalf_s *upper = inode->i_private;
FAR struct lirc_fh_s *fh = filep->f_priv;
ssize_t ret;
ret = nxmutex_lock(&upper->lock);
if (ret < 0)
{
return ret;
}
if (fh->send_mode == LIRC_MODE_SCANCODE)
{
ret = lirc_write_scancode(filep, buffer, buflen);
}
else
{
ret = lirc_write_pulse(filep, buffer, buflen);
}
nxmutex_unlock(&upper->lock);
return ret;
}
static ssize_t lirc_read_scancode(FAR struct file *filep, FAR char *buffer,
size_t length)
{
FAR struct lirc_fh_s *fh = filep->f_priv;
irqstate_t flags;
ssize_t ret;
if (length < sizeof(struct lirc_scancode) ||
length % sizeof(struct lirc_scancode))
{
return -EINVAL;
}
flags = enter_critical_section();
do
{
if (circbuf_is_empty(&fh->buffer))
{
if (filep->f_oflags & O_NONBLOCK)
{
ret = -EAGAIN;
goto err;
}
ret = nxsem_wait_uninterruptible(&fh->waitsem);
if (ret < 0)
{
goto err;
}
}
ret = circbuf_read(&fh->buffer, buffer, length);
}
while (ret <= 0);
err:
leave_critical_section(flags);
return ret;
}
static ssize_t lirc_read_mode2(FAR struct file *filep, FAR char *buffer,
size_t length)
{
FAR struct lirc_fh_s *fh = filep->f_priv;
irqstate_t flags;
ssize_t ret = 0;
if (length < sizeof(unsigned int) || length % sizeof(unsigned int))
{
return -EINVAL;
}
flags = enter_critical_section();
do
{
if (circbuf_is_empty(&fh->buffer))
{
if (filep->f_oflags & O_NONBLOCK)
{
ret = -EAGAIN;
goto err;
}
ret = nxsem_wait_uninterruptible(&fh->waitsem);
if (ret < 0)
{
goto err;
}
}
ret = circbuf_read(&fh->buffer, buffer, length);
}
while (ret <= 0);
err:
leave_critical_section(flags);
return ret;
}
static ssize_t lirc_read(FAR struct file *filep, FAR char *buffer,
size_t len)
{
FAR struct lirc_fh_s *fh = filep->f_priv;
if (fh->rec_mode == LIRC_MODE_MODE2)
{
return lirc_read_mode2(filep, buffer, len);
}
else /* LIRC_MODE_SCANCODE */
{
return lirc_read_scancode(filep, buffer, len);
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: lirc_register
*
* Description:
* This function binds an instance of a "lower half" lirc driver with the
* "upper half" RC device and registers that device so that can be used
* by application code.
*
* We will register the chararter device. ex: /dev/lirc%d(0, 1, ...)
*
* Input Parameters:
* lower - A pointer to an instance of lower half lirc driver.
* devno - The user specifies device number, from 0. If the
* devno alerady exists, -EEXIST will be returned.
*
* Returned Value:
* OK if the driver was successfully register; A negated errno value is
* returned on any failure.
*
****************************************************************************/
int lirc_register(FAR struct lirc_lowerhalf_s *lower, int devno)
{
FAR struct lirc_upperhalf_s *upper;
char path[DEVNAME_MAX];
int ret;
DEBUGASSERT(lower != NULL);
/* Allocate and init the upper-half data structure */
upper = kmm_zalloc(sizeof(struct lirc_upperhalf_s));
if (!upper)
{
snerr("ERROR: Allocation failed\n");
return -ENOMEM;
}
upper->lower = lower;
list_initialize(&upper->fh);
nxmutex_init(&upper->lock);
lower->priv = upper;
/* Register remote control character device */
snprintf(path, DEVNAME_MAX, DEVNAME_FMT, devno);
ret = register_driver(path, &g_lirc_fops, 0666, upper);
if (ret < 0)
{
goto drv_err;
}
return ret;
drv_err:
nxmutex_destroy(&upper->lock);
kmm_free(upper);
return ret;
}
/****************************************************************************
* Name: lirc_unregister
*
* Description:
* This function unregister character node and release all resource about
* upper half driver.
*
* Input Parameters:
* lower - A pointer to an instance of lower half lirc driver.
* devno - The user specifies device number, from 0.
****************************************************************************/
void lirc_unregister(FAR struct lirc_lowerhalf_s *lower, int devno)
{
FAR struct lirc_upperhalf_s *upper = lower->priv;
char path[DEVNAME_MAX];
nxmutex_destroy(&upper->lock);
snprintf(path, DEVNAME_MAX, DEVNAME_FMT, devno);
rcinfo("UnRegistering %s\n", path);
unregister_driver(path);
kmm_free(upper);
}
/****************************************************************************
* Name: lirc_raw_event
*
* Description:
* Lirc lowerhalf driver sends IR data to lirc upperhalf buffer, to
* notify userspace to read IR data.
*
* The type of data is struct lirc_raw_event_s.
*
* Input Parameters:
* lower - A pointer to an instance of lower half lirc driver.
* ev - The data of receiving from IR device
****************************************************************************/
void lirc_raw_event(FAR struct lirc_lowerhalf_s *lower,
struct lirc_raw_event_s ev)
{
FAR struct lirc_upperhalf_s *upper = lower->priv;
FAR struct list_node *node;
FAR struct list_node *tmp;
FAR struct lirc_fh_s *fh;
unsigned int sample;
irqstate_t flags;
int semcount;
int gap;
/* Packet start */
if (ev.reset)
{
/* Userspace expects a long space event before the start of
* the signal to use as a sync. This may be done with repeat
* packets and normal samples. But if a reset has been sent
* then we assume that a long time has passed, so we send a
* space with the maximum time value.
*/
sample = LIRC_SPACE(LIRC_VALUE_MASK);
rcinfo("delivering reset sync space to lirc_dev\n");
}
else if (ev.carrier_report)
{
/* Carrier reports */
sample = LIRC_FREQUENCY(ev.carrier);
rcinfo("carrier report (freq: %d)\n", sample);
}
else if (ev.timeout)
{
/* Packet end */
if (upper->gap)
{
return;
}
upper->gap = true;
upper->gap_start = lirc_get_timestamp() / 1000;
upper->gap_duration = ev.duration;
sample = LIRC_TIMEOUT(ev.duration);
rcinfo("timeout report (duration: %d)\n", sample);
}
else
{
/* Normal sample */
if (upper->gap)
{
upper->gap_duration += (lirc_get_timestamp() / 1000) -
upper->gap_start;
/* Cap by LIRC_VALUE_MASK */
upper->gap_duration = MIN(upper->gap_duration, LIRC_VALUE_MASK);
gap = LIRC_SPACE(upper->gap_duration);
flags = enter_critical_section();
list_for_every_safe(&upper->fh, node, tmp)
{
fh = (FAR struct lirc_fh_s *)node;
if (circbuf_write(&fh->buffer, &gap, sizeof(int)) > 0)
{
poll_notify(&fh->fds, 1, POLLIN | POLLRDNORM);
nxsem_get_value(&fh->waitsem, &semcount);
if (semcount < 1)
{
nxsem_post(&fh->waitsem);
}
}
upper->gap = false;
}
leave_critical_section(flags);
}
sample = ev.pulse ? LIRC_PULSE(ev.duration) : LIRC_SPACE(ev.duration);
rcinfo("delivering %" PRIu32 "us %u to lirc\n",
ev.duration, ev.pulse ? 1 : 0);
}
flags = enter_critical_section();
list_for_every_safe(&upper->fh, node, tmp)
{
fh = (FAR struct lirc_fh_s *)node;
if (LIRC_IS_TIMEOUT(sample) && !fh->send_timeout_reports)
{
continue;
}
if (circbuf_write(&fh->buffer, &sample, sizeof(unsigned int)) > 0)
{
poll_notify(&fh->fds, 1, POLLIN | POLLRDNORM);
nxsem_get_value(&fh->waitsem, &semcount);
if (semcount < 1)
{
nxsem_post(&fh->waitsem);
}
}
}
leave_critical_section(flags);
}
/****************************************************************************
* Name: lirc_scancode_event
*
* Description:
* Lirc lowerhalf driver sends IR data to lirc upperhalf buffer, to
* notify userspace to read IR data.
*
* The type of data is struct lirc_scancode.
*
* Input Parameters:
* lower - A pointer to an instance of lower half lirc driver.
* lsc - The data of receiving from IR device
****************************************************************************/
void lirc_scancode_event(FAR struct lirc_lowerhalf_s *lower,
FAR struct lirc_scancode *lsc)
{
FAR struct lirc_upperhalf_s *upper = lower->priv;
FAR struct list_node *node;
FAR struct list_node *tmp;
FAR struct lirc_fh_s *fh;
irqstate_t flags;
int semcount;
lsc->timestamp = lirc_get_timestamp();
flags = enter_critical_section();
list_for_every_safe(&upper->fh, node, tmp)
{
fh = (FAR struct lirc_fh_s *)node;
if (circbuf_write(&fh->buffer, lsc, sizeof(*lsc)) > 0)
{
poll_notify(&fh->fds, 1, POLLIN | POLLRDNORM);
nxsem_get_value(&fh->waitsem, &semcount);
if (semcount < 1)
{
nxsem_post(&fh->waitsem);
}
}
}
leave_critical_section(flags);
}
/****************************************************************************
* Name: lirc_sample_event
*
* Description:
* Lirc lowerhalf driver sends raw IR data to lirc upperhalf buffer, to
* notify userspace to read IR data.
*
* The type of data is a sequence of pulse and space codes, as a seriers
* of unsigned int values.
*
* The upper 8 bits determine the packet type, and the lower 24 bits the
* payload.
*
* Input Parameters:
* lower - A pointer to an instance of lower half lirc driver.
* sample - The data of receiving from IR device
****************************************************************************/
void lirc_sample_event(FAR struct lirc_lowerhalf_s *lower,
unsigned int sample)
{
FAR struct lirc_upperhalf_s *upper = lower->priv;
FAR struct list_node *node;
FAR struct list_node *tmp;
FAR struct lirc_fh_s *fh;
irqstate_t flags;
int semcount;
flags = enter_critical_section();
list_for_every_safe(&upper->fh, node, tmp)
{
fh = (FAR struct lirc_fh_s *)node;
if (circbuf_write(&fh->buffer, &sample, sizeof(unsigned int)) > 0)
{
poll_notify(&fh->fds, 1, POLLIN | POLLRDNORM);
nxsem_get_value(&fh->waitsem, &semcount);
if (semcount < 1)
{
nxsem_post(&fh->waitsem);
}
}
}
leave_critical_section(flags);
}