nuttx/drivers/rc/lirc_dev.c
Catalin Visinescu 081e4c478a drivers/: Multiple Drivers Are Registered With World Writable - Part 2
Permissions (Part 2)

Description:

In kernel builds, any unprivileged process running on the NuttX
device can open /dev/efuse and attempt to read/write fuse content.
Reading the fuses may provide valuable information to an attacker
controlling the user process. The write operation, in extreme cases
where the fuse blocks are not locked, may brick the device.

DISCLAIMER: I tried to be strict with the settings, better to relax them
later if it's needed.

This is part of https://github.com/apache/nuttx/issues/19410

See https://github.com/apache/nuttx/issues/19410

Compiles ok.

Signed-off-by: Catalin Visinescu <catalin_visinescu@yahoo.com>
2026-07-15 15:27:28 +08:00

1050 lines
27 KiB
C

/****************************************************************************
* drivers/rc/lirc_dev.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <nuttx/debug.h>
#include <errno.h>
#include <poll.h>
#include <fcntl.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/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 already exists, -EEXIST will be returned.
*
* Returned Value:
* OK if the driver was successfully registered; 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, 0600, 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);
}