nuttx/drivers/rc/lirc_dev.c
dongjiuzhu 2ed6e13241 driver/rc: support IR remote control
N/A

guide link: https://www.kernel.org/doc/html/v4.14/media/uapi/rc/lirc-dev.html

Change-Id: I84abfc12578d444d72c8c0df31d06d1a85ce0725
Signed-off-by: dongjiuzhu <dongjiuzhu1@xiaomi.com>
2020-11-05 11:38:28 -03:00

1169 lines
29 KiB
C

/****************************************************************************
* 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 <errno.h>
#include <poll.h>
#include <fcntl.h>
#include <nuttx/kmalloc.h>
#include <nuttx/rc/lirc_dev.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#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 */
sem_t exclsem; /* 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 */
};
/* This structure describes lirc circular buffer */
struct lirc_buffer_s
{
uint32_t head;
uint32_t tail;
uint32_t size;
FAR void *data;
};
/* 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 */
FAR struct lirc_buffer_s *buffer; /* buffer for incoming IR */
FAR struct pollfd *fd; /* 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 void lirc_pollnotify(FAR struct lirc_fh_s *fh, pollevent_t eventset);
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 */
lirc_poll, /* poll */
};
/****************************************************************************
* Private Functions
****************************************************************************/
static void lirc_buffer_free(FAR struct lirc_buffer_s *buffer)
{
kmm_free(buffer);
}
static uint32_t lirc_buffer_len(FAR struct lirc_buffer_s *buffer)
{
return buffer->head - buffer->tail;
}
static uint32_t lirc_buffer_empty(FAR struct lirc_buffer_s *buffer)
{
return !lirc_buffer_len(buffer);
}
static uint32_t lirc_buffer_unused(FAR struct lirc_buffer_s *buffer)
{
return buffer->size - lirc_buffer_len(buffer);
}
static uint32_t lirc_buffer_read(FAR struct lirc_buffer_s *buffer,
FAR void *dest, uint32_t bytes)
{
uint32_t len = lirc_buffer_len(buffer);
uint32_t off = buffer->tail % buffer->size;
if (bytes > len)
{
bytes = len;
}
len = buffer->size - off;
if (bytes < len)
{
len = bytes;
}
memcpy(dest, buffer->data + off, len);
memcpy(dest + len, buffer->data, bytes - len);
buffer->tail += bytes;
return bytes;
}
static uint32_t lirc_buffer_write(FAR struct lirc_buffer_s *buffer,
FAR const void *src, uint32_t bytes)
{
uint32_t space = lirc_buffer_unused(buffer);
uint32_t off = buffer->head % buffer->size;
if (bytes > space)
{
bytes = space;
}
space = buffer->size - off;
if (bytes < space)
{
space = bytes;
}
memcpy(buffer->data + off, src, space);
memcpy(buffer->data, src + space, bytes - space);
buffer->head += bytes;
return bytes;
}
static int lirc_buffer_create(FAR struct lirc_buffer_s **buffer,
uint32_t bytes)
{
FAR struct lirc_buffer_s *tmp;
tmp = kmm_malloc(sizeof((*tmp)) + bytes);
if (!tmp)
{
rcerr("Faild to malloc memory for circular buffer\n");
return -ENOMEM;
}
tmp->size = bytes;
tmp->data = tmp + 1;
tmp->head = 0;
tmp->tail = 0;
*buffer = tmp;
return 0;
}
static void lirc_pollnotify(FAR struct lirc_fh_s *fh,
pollevent_t eventset)
{
int semcount;
if (fh->fd)
{
fh->fd->revents |= (fh->fd->events & eventset);
if (fh->fd->revents != 0)
{
rcinfo("Report events: %02x\n", fh->fd->revents);
nxsem_get_value(fh->fd->sem, &semcount);
if (semcount < 1)
{
nxsem_post(fh->fd->sem);
}
}
}
}
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;
}
if (lirc_buffer_create(&fh->buffer, lower->buffer_bytes))
{
ret = -ENOMEM;
goto buffer_err;
}
nxsem_init(&fh->waitsem, 0, 0);
nxsem_set_protocol(&fh->waitsem, SEM_PRIO_NONE);
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);
lirc_buffer_free(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);
lirc_buffer_free(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->fd)
{
ret = -EBUSY;
goto errout;
}
fh->fd = fds;
fds->priv = &fh->fd;
if (!lirc_buffer_empty(fh->buffer))
{
eventset = (fds->events & (POLLIN | POLLRDNORM));
}
if (eventset)
{
lirc_pollnotify(fh, 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 = (unsigned int *)(uintptr_t)arg;
int ret;
ret = nxsem_wait(&upper->exclsem);
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;
}
nxsem_post(&upper->exclsem);
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 = nxsem_wait(&upper->exclsem);
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);
}
nxsem_post(&upper->exclsem);
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 len;
int ret;
if (length < sizeof(struct lirc_scancode) ||
length % sizeof(struct lirc_scancode))
{
return -EINVAL;
}
flags = enter_critical_section();
do
{
if (lirc_buffer_empty(fh->buffer))
{
if (filep->f_oflags & O_NONBLOCK)
{
ret = -EAGAIN;
goto err;
}
ret = nxsem_wait_uninterruptible(&fh->waitsem);
if (ret < 0)
{
goto err;
}
}
len = lirc_buffer_read(fh->buffer, buffer, length);
}
while (len == 0);
err:
leave_critical_section(flags);
return ret < 0 ? ret : len;
}
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 len = 0;
int ret;
if (length < sizeof(unsigned int) || length % sizeof(unsigned int))
{
return -EINVAL;
}
flags = enter_critical_section();
do
{
if (lirc_buffer_empty(fh->buffer))
{
if (filep->f_oflags & O_NONBLOCK)
{
ret = -EAGAIN;
goto err;
}
ret = nxsem_wait_uninterruptible(&fh->waitsem);
if (ret < 0)
{
goto err;
}
}
len = lirc_buffer_read(fh->buffer, buffer, length);
}
while (len == 0);
err:
leave_critical_section(flags);
return ret < 0 ? ret : len;
}
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);
nxsem_init(&upper->exclsem, 0, 1);
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:
nxsem_destroy(&upper->exclsem);
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];
nxsem_destroy(&upper->exclsem);
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 (lirc_buffer_write(fh->buffer, &gap, sizeof(int)))
{
lirc_pollnotify(fh, POLLIN | POLLRDNORM);
nxsem_get_value(&fh->waitsem, &semcount);
if (semcount < 1)
{
nxsem_post(&fh->waitsem);
}
}
leave_critical_section(flags);
upper->gap = false;
}
}
sample = ev.pulse ? LIRC_PULSE(ev.duration) : LIRC_SPACE(ev.duration);
rcinfo("delivering %uus %d 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 (lirc_buffer_write(fh->buffer, &sample, sizeof(unsigned int)))
{
lirc_pollnotify(fh, 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 (lirc_buffer_write(fh->buffer, lsc, sizeof(*lsc)))
{
lirc_pollnotify(fh, 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 (lirc_buffer_write(fh->buffer, &sample, sizeof(unsigned int)))
{
lirc_pollnotify(fh, POLLIN | POLLRDNORM);
nxsem_get_value(&fh->waitsem, &semcount);
if (semcount < 1)
{
nxsem_post(&fh->waitsem);
}
}
}
leave_critical_section(flags);
}