You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2020/11/05 05:59:37 UTC

[GitHub] [incubator-nuttx] Donny9 commented on a change in pull request #2216: support infrared remote control

Donny9 commented on a change in pull request #2216:
URL: https://github.com/apache/incubator-nuttx/pull/2216#discussion_r517809079



##########
File path: drivers/rc/lirc_dev.c
##########
@@ -0,0 +1,1174 @@
+/****************************************************************************
+ * 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 */
+  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                        exclsem;      /* Manages exclusive access to file handle */
+  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->exclsem, 0, 1);
+  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->exclsem);
+  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->exclsem);
+  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;
+  int ret;
+
+  ret = nxsem_wait(&fh->exclsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  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:
+  nxsem_post(&fh->exclsem);
+  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 unsigned int *val = (unsigned int *)(uintptr_t)arg;
+  int ret;
+
+  ret = nxsem_wait(&fh->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;
+            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(&fh->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;
+  int ret;
+
+  if (buflen < sizeof(unsigned) || buflen % sizeof(unsigned))
+    {
+      return -EINVAL;
+    }
+
+  count = buflen / sizeof(unsigned);
+  if (count % 2 == 0)
+    {
+      return -EINVAL;
+    }
+
+  /* tx_ir need sleep some time to wait for thr actual IR signal
+   * to be transmitted before returning
+   */
+
+  ret = lower->ops->tx_ir(lower, (unsigned *)buffer, count);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return ret;
+}
+
+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 lirc_fh_s *fh = filep->f_priv;
+  ssize_t ret;
+
+  ret = nxsem_wait(&fh->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(&fh->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);
+  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:
+  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];
+
+  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;
+  irqstate_t flags;
+  int semcount;
+  int sample;
+  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)))
+        {
+          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 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 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)))

Review comment:
       done

##########
File path: drivers/rc/lirc_dev.c
##########
@@ -0,0 +1,1174 @@
+/****************************************************************************
+ * 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 */
+  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                        exclsem;      /* Manages exclusive access to file handle */
+  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->exclsem, 0, 1);
+  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->exclsem);
+  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->exclsem);
+  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;
+  int ret;
+
+  ret = nxsem_wait(&fh->exclsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  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:
+  nxsem_post(&fh->exclsem);
+  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 unsigned int *val = (unsigned int *)(uintptr_t)arg;
+  int ret;
+
+  ret = nxsem_wait(&fh->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;
+            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(&fh->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;
+  int ret;
+
+  if (buflen < sizeof(unsigned) || buflen % sizeof(unsigned))
+    {
+      return -EINVAL;
+    }
+
+  count = buflen / sizeof(unsigned);
+  if (count % 2 == 0)
+    {
+      return -EINVAL;
+    }
+
+  /* tx_ir need sleep some time to wait for thr actual IR signal
+   * to be transmitted before returning
+   */
+
+  ret = lower->ops->tx_ir(lower, (unsigned *)buffer, count);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return ret;
+}
+
+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 lirc_fh_s *fh = filep->f_priv;
+  ssize_t ret;
+
+  ret = nxsem_wait(&fh->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(&fh->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);
+  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:
+  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];
+
+  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;
+  irqstate_t flags;
+  int semcount;
+  int sample;
+  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)))

Review comment:
       done

##########
File path: drivers/rc/lirc_dev.c
##########
@@ -0,0 +1,1174 @@
+/****************************************************************************
+ * 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 */
+  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                        exclsem;      /* Manages exclusive access to file handle */
+  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->exclsem, 0, 1);
+  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->exclsem);
+  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->exclsem);
+  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;
+  int ret;
+
+  ret = nxsem_wait(&fh->exclsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  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:
+  nxsem_post(&fh->exclsem);
+  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 unsigned int *val = (unsigned int *)(uintptr_t)arg;
+  int ret;
+
+  ret = nxsem_wait(&fh->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;
+            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(&fh->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;
+  int ret;
+
+  if (buflen < sizeof(unsigned) || buflen % sizeof(unsigned))

Review comment:
       done

##########
File path: drivers/rc/lirc_dev.c
##########
@@ -0,0 +1,1174 @@
+/****************************************************************************
+ * 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 */
+  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                        exclsem;      /* Manages exclusive access to file handle */
+  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->exclsem, 0, 1);
+  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->exclsem);
+  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->exclsem);
+  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;
+  int ret;
+
+  ret = nxsem_wait(&fh->exclsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  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:
+  nxsem_post(&fh->exclsem);
+  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 unsigned int *val = (unsigned int *)(uintptr_t)arg;
+  int ret;
+
+  ret = nxsem_wait(&fh->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;
+            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(&fh->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;
+  int ret;
+
+  if (buflen < sizeof(unsigned) || buflen % sizeof(unsigned))
+    {
+      return -EINVAL;
+    }
+
+  count = buflen / sizeof(unsigned);

Review comment:
       done




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org