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