From 2ed6e132410b2983f321cce5559d5dadb903685e Mon Sep 17 00:00:00 2001 From: dongjiuzhu Date: Fri, 23 Oct 2020 16:36:11 +0800 Subject: [PATCH] driver/rc: support IR remote control N/A guide link: https://www.kernel.org/doc/html/v4.14/media/uapi/rc/lirc-dev.html Change-Id: I84abfc12578d444d72c8c0df31d06d1a85ce0725 Signed-off-by: dongjiuzhu --- Kconfig | 38 ++ drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/rc/Kconfig | 10 + drivers/rc/Make.defs | 33 + drivers/rc/lirc_dev.c | 1168 +++++++++++++++++++++++++++++++++++ include/debug.h | 18 + include/nuttx/fs/ioctl.h | 6 + include/nuttx/lirc.h | 264 ++++++++ include/nuttx/rc/lirc_dev.h | 271 ++++++++ 10 files changed, 1810 insertions(+) create mode 100644 drivers/rc/Kconfig create mode 100644 drivers/rc/Make.defs create mode 100644 drivers/rc/lirc_dev.c create mode 100644 include/nuttx/lirc.h create mode 100644 include/nuttx/rc/lirc_dev.h diff --git a/Kconfig b/Kconfig index c90b68fa65..233fee3d3d 100644 --- a/Kconfig +++ b/Kconfig @@ -1417,6 +1417,44 @@ config DEBUG_PWM_INFO endif # DEBUG_PWM +config DEBUG_RC + bool "RC Debug Features" + default n + depends on DRIVERS_RC + ---help--- + Enable RC debug features. + + Support for this debug option is architecture-specific and may not + be available for some MCUs. + +if DEBUG_RC + +config DEBUG_RC_ERROR + bool "RC Error Output" + default n + depends on DEBUG_ERROR + ---help--- + Enable RC driver error output to SYSLOG. + + Support for this debug option is architecture-specific and may not + be available for some MCUs. + +config DEBUG_RC_WARN + bool "RC Warnings Output" + default n + depends on DEBUG_WARN + ---help--- + Enable RC driver warning output to SYSLOG. + +config DEBUG_RC_INFO + bool "RC Informational Output" + default n + depends on DEBUG_INFO + ---help--- + Enable RC driver informational output to SYSLOG. + +endif # DEBUG_RC + config DEBUG_RTC bool "RTC Debug Features" default n diff --git a/drivers/Kconfig b/drivers/Kconfig index f5a057d36d..57c8961b6b 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -112,3 +112,4 @@ source drivers/1wire/Kconfig source drivers/syslog/Kconfig source drivers/platform/Kconfig source drivers/rf/Kconfig +source drivers/rc/Kconfig diff --git a/drivers/Makefile b/drivers/Makefile index 15fdaa26aa..d8d1a76cda 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -74,6 +74,7 @@ include wireless/Make.defs include contactless/Make.defs include 1wire/Make.defs include rf/Make.defs +include rc/Make.defs ifeq ($(CONFIG_SPECIFIC_DRIVERS),y) include platform/Make.defs diff --git a/drivers/rc/Kconfig b/drivers/rc/Kconfig new file mode 100644 index 0000000000..c76c7ec6b5 --- /dev/null +++ b/drivers/rc/Kconfig @@ -0,0 +1,10 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig DRIVERS_RC + bool "Remote Control Device Support" + default n + ---help--- + Drivers for various remote control diff --git a/drivers/rc/Make.defs b/drivers/rc/Make.defs new file mode 100644 index 0000000000..4deffdb88c --- /dev/null +++ b/drivers/rc/Make.defs @@ -0,0 +1,33 @@ +############################################################################ +# drivers/rc/Make.defs +# +# 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. +# +############################################################################ + +# Include remote control drivers + +ifeq ($(CONFIG_DRIVERS_RC),y) + +CSRCS += lirc_dev.c + +# Include remote control driver build support + +DEPPATH += --dep-path rc +VPATH += :rc +CFLAGS += ${shell $(INCDIR) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)rc} + +endif # CONFIG_REMOTE_CONTROL diff --git a/drivers/rc/lirc_dev.c b/drivers/rc/lirc_dev.c new file mode 100644 index 0000000000..057a6b34ca --- /dev/null +++ b/drivers/rc/lirc_dev.c @@ -0,0 +1,1168 @@ +/**************************************************************************** + * 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 + +#include +#include +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define DEVNAME_FMT "/dev/lirc%d" +#define DEVNAME_MAX 32 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes the state of the upper half driver */ + +struct lirc_upperhalf_s +{ + struct list_node fh; /* list of struct lirc_fh_s object */ + FAR struct lirc_lowerhalf_s *lower; /* the handle of lower half driver */ + sem_t exclsem; /* Manages exclusive access to lowerhalf */ + bool gap; /* true if we're in a gap */ + uint64_t gap_start; /* time when gap starts */ + uint64_t gap_duration; /* duration of initial gap */ +}; + +/* This structure describes lirc circular buffer */ + +struct lirc_buffer_s +{ + uint32_t head; + uint32_t tail; + uint32_t size; + FAR void *data; +}; + +/* The structure describes an open lirc file */ + +struct lirc_fh_s +{ + struct list_node node; /* list of open file handles */ + FAR struct lirc_lowerhalf_s *lower; /* the pointer to lirc_lowerhalf_s */ + FAR struct lirc_buffer_s *buffer; /* buffer for incoming IR */ + FAR struct pollfd *fd; /* poll structures of threads waiting for driver events */ + sem_t waitsem; /* sem of wait buffer for ready */ + int carrier_low; /* when setting the carrier range, first the low end must be + * set with an ioctl and then the high end with another ioctl + */ + unsigned char send_mode; /* lirc mode for sending, LIRC_MODE_PULSE LIRC_MODE_SCANCODE */ + unsigned char rec_mode; /* lirc mode for receiving, LIRC_MODE_MODE2 LIRC_MODE_SCANCODE */ + bool send_timeout_reports; /* report timeouts in lirc raw IR. */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void lirc_pollnotify(FAR struct lirc_fh_s *fh, pollevent_t eventset); +static int lirc_open(FAR struct file *filep); +static int lirc_close(FAR struct file *filep); +static int lirc_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +static int lirc_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); +static ssize_t lirc_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static ssize_t lirc_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_lirc_fops = +{ + lirc_open, /* open */ + lirc_close, /* close */ + lirc_read, /* read */ + lirc_write, /* write */ + NULL, /* seek */ + lirc_ioctl, /* ioctl */ + lirc_poll, /* poll */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void lirc_buffer_free(FAR struct lirc_buffer_s *buffer) +{ + kmm_free(buffer); +} + +static uint32_t lirc_buffer_len(FAR struct lirc_buffer_s *buffer) +{ + return buffer->head - buffer->tail; +} + +static uint32_t lirc_buffer_empty(FAR struct lirc_buffer_s *buffer) +{ + return !lirc_buffer_len(buffer); +} + +static uint32_t lirc_buffer_unused(FAR struct lirc_buffer_s *buffer) +{ + return buffer->size - lirc_buffer_len(buffer); +} + +static uint32_t lirc_buffer_read(FAR struct lirc_buffer_s *buffer, + FAR void *dest, uint32_t bytes) +{ + uint32_t len = lirc_buffer_len(buffer); + uint32_t off = buffer->tail % buffer->size; + + if (bytes > len) + { + bytes = len; + } + + len = buffer->size - off; + if (bytes < len) + { + len = bytes; + } + + memcpy(dest, buffer->data + off, len); + memcpy(dest + len, buffer->data, bytes - len); + buffer->tail += bytes; + + return bytes; +} + +static uint32_t lirc_buffer_write(FAR struct lirc_buffer_s *buffer, + FAR const void *src, uint32_t bytes) +{ + uint32_t space = lirc_buffer_unused(buffer); + uint32_t off = buffer->head % buffer->size; + + if (bytes > space) + { + bytes = space; + } + + space = buffer->size - off; + if (bytes < space) + { + space = bytes; + } + + memcpy(buffer->data + off, src, space); + memcpy(buffer->data, src + space, bytes - space); + buffer->head += bytes; + + return bytes; +} + +static int lirc_buffer_create(FAR struct lirc_buffer_s **buffer, + uint32_t bytes) +{ + FAR struct lirc_buffer_s *tmp; + + tmp = kmm_malloc(sizeof((*tmp)) + bytes); + if (!tmp) + { + rcerr("Faild to malloc memory for circular buffer\n"); + return -ENOMEM; + } + + tmp->size = bytes; + tmp->data = tmp + 1; + tmp->head = 0; + tmp->tail = 0; + + *buffer = tmp; + + return 0; +} + +static void lirc_pollnotify(FAR struct lirc_fh_s *fh, + pollevent_t eventset) +{ + int semcount; + + if (fh->fd) + { + fh->fd->revents |= (fh->fd->events & eventset); + + if (fh->fd->revents != 0) + { + rcinfo("Report events: %02x\n", fh->fd->revents); + + nxsem_get_value(fh->fd->sem, &semcount); + if (semcount < 1) + { + nxsem_post(fh->fd->sem); + } + } + } +} + +static int lirc_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lirc_upperhalf_s *upper = inode->i_private; + FAR struct lirc_lowerhalf_s *lower = upper->lower; + FAR struct lirc_fh_s *fh; + irqstate_t flags; + int ret; + + fh = kmm_zalloc(sizeof(*fh)); + if (!fh) + { + return -ENOMEM; + } + + switch (lower->ops->driver_type) + { + case LIRC_DRIVER_SCANCODE: + fh->rec_mode = LIRC_MODE_SCANCODE; + break; + default: + fh->rec_mode = LIRC_MODE_MODE2; + break; + } + + if (lower->ops->tx_scancode) + { + fh->send_mode = LIRC_MODE_SCANCODE; + } + else if (lower->ops->tx_ir) + { + fh->send_mode = LIRC_MODE_PULSE; + } + + if (lirc_buffer_create(&fh->buffer, lower->buffer_bytes)) + { + ret = -ENOMEM; + goto buffer_err; + } + + nxsem_init(&fh->waitsem, 0, 0); + nxsem_set_protocol(&fh->waitsem, SEM_PRIO_NONE); + + fh->lower = lower; + fh->send_timeout_reports = true; + + if (list_is_empty(&upper->fh)) + { + ret = lower->ops->open(lower); + if (ret < 0) + { + goto open_err; + } + } + + flags = enter_critical_section(); + list_add_tail(&upper->fh, &fh->node); + leave_critical_section(flags); + + filep->f_priv = fh; + return 0; + +open_err: + nxsem_destroy(&fh->waitsem); + lirc_buffer_free(fh->buffer); +buffer_err: + kmm_free(fh); + return ret; +} + +static int lirc_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lirc_upperhalf_s *upper = inode->i_private; + FAR struct lirc_fh_s *fh = filep->f_priv; + FAR struct lirc_lowerhalf_s *lower = fh->lower; + irqstate_t flags; + + flags = enter_critical_section(); + list_delete(&fh->node); + leave_critical_section(flags); + + nxsem_destroy(&fh->waitsem); + lirc_buffer_free(fh->buffer); + + kmm_free(fh); + if (list_is_empty(&upper->fh)) + { + lower->ops->close(lower); + } + + return 0; +} + +static int lirc_poll(FAR struct file *filep, + FAR struct pollfd *fds, bool setup) +{ + FAR struct lirc_fh_s *fh = filep->f_priv; + pollevent_t eventset = 0; + irqstate_t flags; + int ret = 0; + + flags = enter_critical_section(); + if (setup) + { + if (fh->fd) + { + ret = -EBUSY; + goto errout; + } + + fh->fd = fds; + fds->priv = &fh->fd; + + if (!lirc_buffer_empty(fh->buffer)) + { + eventset = (fds->events & (POLLIN | POLLRDNORM)); + } + + if (eventset) + { + lirc_pollnotify(fh, eventset); + } + } + else if (fds->priv != NULL) + { + FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv; + + if (!slot) + { + ret = -EIO; + goto errout; + } + + *slot = NULL; + fds->priv = NULL; + } + +errout: + leave_critical_section(flags); + return ret; +} + +static int lirc_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct lirc_fh_s *fh = filep->f_priv; + FAR struct lirc_lowerhalf_s *lower = fh->lower; + FAR struct lirc_upperhalf_s *upper = lower->priv; + FAR unsigned int *val = (unsigned int *)(uintptr_t)arg; + int ret; + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + + switch (cmd) + { + case LIRC_GET_FEATURES: + switch (lower->ops->driver_type) + { + case LIRC_DRIVER_SCANCODE: + *val = LIRC_CAN_REC_SCANCODE; + break; + case LIRC_DRIVER_IR_RAW: + *val = LIRC_CAN_REC_MODE2; + break; + default: + *val = 0; + break; + } + + if (lower->rx_resolution) + { + *val |= LIRC_CAN_GET_REC_RESOLUTION; + } + + if (lower->ops->tx_ir) + { + *val |= LIRC_CAN_SEND_PULSE; + } + + if (lower->ops->tx_scancode) + { + *val |= LIRC_CAN_SEND_SCANCODE; + } + + if (lower->ops->s_tx_mask) + { + *val |= LIRC_CAN_SET_TRANSMITTER_MASK; + } + + if (lower->ops->s_tx_carrier) + { + *val |= LIRC_CAN_SET_SEND_CARRIER; + } + + if (lower->ops->s_tx_duty_cycle) + { + *val |= LIRC_CAN_SET_SEND_DUTY_CYCLE; + } + + if (lower->ops->s_rx_carrier_range) + { + *val |= LIRC_CAN_SET_REC_CARRIER | + LIRC_CAN_SET_REC_CARRIER_RANGE; + } + + if (lower->ops->s_learning_mode) + { + *val |= LIRC_CAN_USE_WIDEBAND_RECEIVER; + } + + if (lower->ops->s_carrier_report) + { + *val |= LIRC_CAN_MEASURE_CARRIER; + } + + if (lower->max_timeout) + { + *val |= LIRC_CAN_SET_REC_TIMEOUT; + } + break; + + /* mode support */ + + case LIRC_GET_REC_MODE: + if (lower->ops->driver_type == LIRC_DRIVER_IR_RAW_TX) + { + ret = -ENOTTY; + } + else + { + *val = fh->rec_mode; + } + break; + + case LIRC_SET_REC_MODE: + switch (lower->ops->driver_type) + { + case LIRC_DRIVER_IR_RAW_TX: + ret = -ENOTTY; + break; + + case LIRC_DRIVER_SCANCODE: + if (arg != LIRC_MODE_SCANCODE) + { + ret = -EINVAL; + } + break; + + case LIRC_DRIVER_IR_RAW: + if (arg != LIRC_MODE_MODE2) + { + ret = -EINVAL; + } + break; + } + + if (ret >= 0) + { + fh->rec_mode = arg; + } + break; + + case LIRC_GET_SEND_MODE: + if (!lower->ops->tx_ir && !lower->ops->tx_scancode) + { + ret = -ENOTTY; + } + else + { + *val = fh->send_mode; + } + break; + + case LIRC_SET_SEND_MODE: + if ((arg == LIRC_MODE_PULSE && lower->ops->tx_ir) || + (arg == LIRC_MODE_SCANCODE && lower->ops->tx_scancode)) + { + fh->send_mode = arg; + } + else + { + ret = -EINVAL; + } + break; + + /* TX settings */ + + case LIRC_SET_TRANSMITTER_MASK: + if (!lower->ops->s_tx_mask) + { + ret = -ENOTTY; + } + else + { + ret = lower->ops->s_tx_mask(lower, arg); + } + break; + + case LIRC_SET_SEND_CARRIER: + if (!lower->ops->s_tx_carrier) + { + ret = -ENOTTY; + } + else + { + ret = lower->ops->s_tx_carrier(lower, arg); + } + break; + + case LIRC_SET_SEND_DUTY_CYCLE: + if (!lower->ops->s_tx_duty_cycle) + { + ret = -ENOTTY; + } + else if (arg <= 0 || arg >= 100) + { + ret = -EINVAL; + } + else + { + ret = lower->ops->s_tx_duty_cycle(lower, arg); + } + break; + + /* RX settings */ + + case LIRC_SET_REC_CARRIER: + if (!lower->ops->s_rx_carrier_range) + { + ret = -ENOTTY; + } + else if (arg <= 0) + { + ret = -EINVAL; + } + else + { + ret = lower->ops->s_rx_carrier_range(lower, + fh->carrier_low, arg); + } + break; + + case LIRC_SET_REC_CARRIER_RANGE: + if (!lower->ops->s_rx_carrier_range) + { + ret = -ENOTTY; + } + else if (arg <= 0) + { + ret = -EINVAL; + } + else + { + fh->carrier_low = arg; + } + break; + + case LIRC_GET_REC_RESOLUTION: + if (!lower->rx_resolution) + { + ret = -ENOTTY; + } + else + { + *val = lower->rx_resolution; + } + break; + + case LIRC_SET_WIDEBAND_RECEIVER: + if (!lower->ops->s_learning_mode) + { + ret = -ENOTTY; + } + else + { + ret = lower->ops->s_learning_mode(lower, !!arg); + } + break; + + case LIRC_SET_MEASURE_CARRIER_MODE: + if (!lower->ops->s_carrier_report) + { + ret = -ENOTTY; + } + else + { + ret = lower->ops->s_carrier_report(lower, !!arg); + } + break; + + /* Generic timeout support */ + + case LIRC_GET_MIN_TIMEOUT: + if (!lower->min_timeout) + { + ret = -ENOTTY; + } + else + { + *val = lower->min_timeout; + } + break; + + case LIRC_GET_MAX_TIMEOUT: + if (!lower->max_timeout) + { + ret = -ENOTTY; + } + else + { + *val = lower->max_timeout; + } + break; + + case LIRC_SET_REC_TIMEOUT: + if (!lower->max_timeout) + { + ret = -ENOTTY; + } + else + { + if (arg < lower->min_timeout || arg > lower->max_timeout) + { + ret = -EINVAL; + } + else if (lower->ops->s_timeout) + { + ret = lower->ops->s_timeout(lower, arg); + } + else + { + lower->timeout = arg; + } + } + break; + + case LIRC_GET_REC_TIMEOUT: + if (!lower->timeout) + { + ret = -ENOTTY; + } + else + { + *val = lower->timeout; + } + break; + + case LIRC_SET_REC_TIMEOUT_REPORTS: + if (lower->ops->driver_type != LIRC_DRIVER_IR_RAW) + { + ret = -ENOTTY; + } + else + { + fh->send_timeout_reports = !!arg; + } + break; + + default: + ret = -ENOTTY; + } + + nxsem_post(&upper->exclsem); + return ret; +} + +static ssize_t lirc_write_pulse(FAR struct file *filep, + FAR const char *buffer, size_t buflen) +{ + FAR struct lirc_fh_s *fh = filep->f_priv; + FAR struct lirc_lowerhalf_s *lower = fh->lower; + size_t count; + + if (buflen < sizeof(unsigned int) || buflen % sizeof(unsigned int)) + { + return -EINVAL; + } + + count = buflen / sizeof(unsigned int); + if (count % 2 == 0) + { + return -EINVAL; + } + + /* tx_ir need sleep some time to wait for thr actual IR signal + * to be transmitted before returning + */ + + return lower->ops->tx_ir(lower, (FAR unsigned int *)buffer, count); +} + +static ssize_t lirc_write_scancode(FAR struct file *filep, + FAR const char *buffer, size_t buflen) +{ + FAR struct lirc_fh_s *fh = filep->f_priv; + FAR struct lirc_lowerhalf_s *lower = fh->lower; + + if (buflen != sizeof(struct lirc_scancode)) + { + return -EINVAL; + } + + /* tx_scancode need sleep some time to wait for thr actual IR signal + * to be transmitted before returning + */ + + return lower->ops->tx_scancode(lower, + (FAR struct lirc_scancode *)buffer); +} + +static ssize_t lirc_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct lirc_upperhalf_s *upper = inode->i_private; + FAR struct lirc_fh_s *fh = filep->f_priv; + ssize_t ret; + + ret = nxsem_wait(&upper->exclsem); + if (ret < 0) + { + return ret; + } + + if (fh->send_mode == LIRC_MODE_SCANCODE) + { + ret = lirc_write_scancode(filep, buffer, buflen); + } + else + { + ret = lirc_write_pulse(filep, buffer, buflen); + } + + nxsem_post(&upper->exclsem); + return ret; +} + +static ssize_t lirc_read_scancode(FAR struct file *filep, FAR char *buffer, + size_t length) +{ + FAR struct lirc_fh_s *fh = filep->f_priv; + irqstate_t flags; + ssize_t len; + int ret; + + if (length < sizeof(struct lirc_scancode) || + length % sizeof(struct lirc_scancode)) + { + return -EINVAL; + } + + flags = enter_critical_section(); + do + { + if (lirc_buffer_empty(fh->buffer)) + { + if (filep->f_oflags & O_NONBLOCK) + { + ret = -EAGAIN; + goto err; + } + + ret = nxsem_wait_uninterruptible(&fh->waitsem); + if (ret < 0) + { + goto err; + } + } + + len = lirc_buffer_read(fh->buffer, buffer, length); + } + while (len == 0); + +err: + leave_critical_section(flags); + return ret < 0 ? ret : len; +} + +static ssize_t lirc_read_mode2(FAR struct file *filep, FAR char *buffer, + size_t length) +{ + FAR struct lirc_fh_s *fh = filep->f_priv; + irqstate_t flags; + ssize_t len = 0; + int ret; + + if (length < sizeof(unsigned int) || length % sizeof(unsigned int)) + { + return -EINVAL; + } + + flags = enter_critical_section(); + do + { + if (lirc_buffer_empty(fh->buffer)) + { + if (filep->f_oflags & O_NONBLOCK) + { + ret = -EAGAIN; + goto err; + } + + ret = nxsem_wait_uninterruptible(&fh->waitsem); + if (ret < 0) + { + goto err; + } + } + + len = lirc_buffer_read(fh->buffer, buffer, length); + } + while (len == 0); + +err: + leave_critical_section(flags); + return ret < 0 ? ret : len; +} + +static ssize_t lirc_read(FAR struct file *filep, FAR char *buffer, + size_t len) +{ + FAR struct lirc_fh_s *fh = filep->f_priv; + + if (fh->rec_mode == LIRC_MODE_MODE2) + { + return lirc_read_mode2(filep, buffer, len); + } + else /* LIRC_MODE_SCANCODE */ + { + return lirc_read_scancode(filep, buffer, len); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lirc_register + * + * Description: + * This function binds an instance of a "lower half" lirc driver with the + * "upper half" RC device and registers that device so that can be used + * by application code. + * + * We will register the chararter device. ex: /dev/lirc%d(0, 1, ...) + * + * Input Parameters: + * lower - A pointer to an instance of lower half lirc driver. + * devno - The user specifies device number, from 0. If the + * devno alerady exists, -EEXIST will be returned. + * + * Returned Value: + * OK if the driver was successfully register; A negated errno value is + * returned on any failure. + * + ****************************************************************************/ + +int lirc_register(FAR struct lirc_lowerhalf_s *lower, int devno) +{ + FAR struct lirc_upperhalf_s *upper; + char path[DEVNAME_MAX]; + int ret; + + DEBUGASSERT(lower != NULL); + + /* Allocate and init the upper-half data structure */ + + upper = kmm_zalloc(sizeof(struct lirc_upperhalf_s)); + if (!upper) + { + snerr("ERROR: Allocation failed\n"); + return -ENOMEM; + } + + upper->lower = lower; + list_initialize(&upper->fh); + nxsem_init(&upper->exclsem, 0, 1); + lower->priv = upper; + + /* Register remote control character device */ + + snprintf(path, DEVNAME_MAX, DEVNAME_FMT, devno); + ret = register_driver(path, &g_lirc_fops, 0666, upper); + if (ret < 0) + { + goto drv_err; + } + + return ret; + +drv_err: + nxsem_destroy(&upper->exclsem); + kmm_free(upper); + return ret; +} + +/**************************************************************************** + * Name: lirc_unregister + * + * Description: + * This function unregister character node and release all resource about + * upper half driver. + * + * Input Parameters: + * lower - A pointer to an instance of lower half lirc driver. + * devno - The user specifies device number, from 0. + ****************************************************************************/ + +void lirc_unregister(FAR struct lirc_lowerhalf_s *lower, int devno) +{ + FAR struct lirc_upperhalf_s *upper = lower->priv; + char path[DEVNAME_MAX]; + + nxsem_destroy(&upper->exclsem); + snprintf(path, DEVNAME_MAX, DEVNAME_FMT, devno); + rcinfo("UnRegistering %s\n", path); + unregister_driver(path); + kmm_free(upper); +} + +/**************************************************************************** + * Name: lirc_raw_event + * + * Description: + * Lirc lowerhalf driver sends IR data to lirc upperhalf buffer, to + * notify userspace to read IR data. + * + * The type of data is struct lirc_raw_event_s. + * + * Input Parameters: + * lower - A pointer to an instance of lower half lirc driver. + * ev - The data of receiving from IR device + ****************************************************************************/ + +void lirc_raw_event(FAR struct lirc_lowerhalf_s *lower, + struct lirc_raw_event_s ev) +{ + FAR struct lirc_upperhalf_s *upper = lower->priv; + FAR struct list_node *node; + FAR struct list_node *tmp; + FAR struct lirc_fh_s *fh; + unsigned int sample; + irqstate_t flags; + int semcount; + int gap; + + /* Packet start */ + + if (ev.reset) + { + /* Userspace expects a long space event before the start of + * the signal to use as a sync. This may be done with repeat + * packets and normal samples. But if a reset has been sent + * then we assume that a long time has passed, so we send a + * space with the maximum time value. + */ + + sample = LIRC_SPACE(LIRC_VALUE_MASK); + rcinfo("delivering reset sync space to lirc_dev\n"); + } + else if (ev.carrier_report) + { + /* Carrier reports */ + + sample = LIRC_FREQUENCY(ev.carrier); + rcinfo("carrier report (freq: %d)\n", sample); + } + else if (ev.timeout) + { + /* Packet end */ + + if (upper->gap) + { + return; + } + + upper->gap = true; + upper->gap_start = lirc_get_timestamp() / 1000; + upper->gap_duration = ev.duration; + + sample = LIRC_TIMEOUT(ev.duration); + rcinfo("timeout report (duration: %d)\n", sample); + } + else + { + /* Normal sample */ + + if (upper->gap) + { + upper->gap_duration += (lirc_get_timestamp() / 1000) - + upper->gap_start; + + /* Cap by LIRC_VALUE_MASK */ + + upper->gap_duration = MIN(upper->gap_duration, LIRC_VALUE_MASK); + gap = LIRC_SPACE(upper->gap_duration); + + flags = enter_critical_section(); + list_for_every_safe(&upper->fh, node, tmp) + { + fh = (FAR struct lirc_fh_s *)node; + if (lirc_buffer_write(fh->buffer, &gap, sizeof(int))) + { + lirc_pollnotify(fh, POLLIN | POLLRDNORM); + nxsem_get_value(&fh->waitsem, &semcount); + if (semcount < 1) + { + nxsem_post(&fh->waitsem); + } + } + + leave_critical_section(flags); + upper->gap = false; + } + } + + sample = ev.pulse ? LIRC_PULSE(ev.duration) : LIRC_SPACE(ev.duration); + rcinfo("delivering %uus %d to lirc\n", ev.duration, ev.pulse ? 1 : 0); + } + + flags = enter_critical_section(); + list_for_every_safe(&upper->fh, node, tmp) + { + fh = (FAR struct lirc_fh_s *)node; + if (LIRC_IS_TIMEOUT(sample) && !fh->send_timeout_reports) + { + continue; + } + + if (lirc_buffer_write(fh->buffer, &sample, sizeof(unsigned int))) + { + lirc_pollnotify(fh, POLLIN | POLLRDNORM); + nxsem_get_value(&fh->waitsem, &semcount); + if (semcount < 1) + { + nxsem_post(&fh->waitsem); + } + } + } + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: lirc_scancode_event + * + * Description: + * Lirc lowerhalf driver sends IR data to lirc upperhalf buffer, to + * notify userspace to read IR data. + * + * The type of data is struct lirc_scancode. + * + * Input Parameters: + * lower - A pointer to an instance of lower half lirc driver. + * lsc - The data of receiving from IR device + ****************************************************************************/ + +void lirc_scancode_event(FAR struct lirc_lowerhalf_s *lower, + FAR struct lirc_scancode *lsc) +{ + FAR struct lirc_upperhalf_s *upper = lower->priv; + FAR struct list_node *node; + FAR struct list_node *tmp; + FAR struct lirc_fh_s *fh; + irqstate_t flags; + int semcount; + + lsc->timestamp = lirc_get_timestamp(); + + flags = enter_critical_section(); + list_for_every_safe(&upper->fh, node, tmp) + { + fh = (FAR struct lirc_fh_s *)node; + if (lirc_buffer_write(fh->buffer, lsc, sizeof(*lsc))) + { + lirc_pollnotify(fh, POLLIN | POLLRDNORM); + nxsem_get_value(&fh->waitsem, &semcount); + if (semcount < 1) + { + nxsem_post(&fh->waitsem); + } + } + } + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: lirc_sample_event + * + * Description: + * Lirc lowerhalf driver sends raw IR data to lirc upperhalf buffer, to + * notify userspace to read IR data. + * + * The type of data is a sequence of pulse and space codes, as a seriers + * of unsigned int values. + * + * The upper 8 bits determine the packet type, and the lower 24 bits the + * payload. + * + * Input Parameters: + * lower - A pointer to an instance of lower half lirc driver. + * sample - The data of receiving from IR device + ****************************************************************************/ + +void lirc_sample_event(FAR struct lirc_lowerhalf_s *lower, + unsigned int sample) +{ + FAR struct lirc_upperhalf_s *upper = lower->priv; + FAR struct list_node *node; + FAR struct list_node *tmp; + FAR struct lirc_fh_s *fh; + irqstate_t flags; + int semcount; + + flags = enter_critical_section(); + list_for_every_safe(&upper->fh, node, tmp) + { + fh = (FAR struct lirc_fh_s *)node; + if (lirc_buffer_write(fh->buffer, &sample, sizeof(unsigned int))) + { + lirc_pollnotify(fh, POLLIN | POLLRDNORM); + nxsem_get_value(&fh->waitsem, &semcount); + if (semcount < 1) + { + nxsem_post(&fh->waitsem); + } + } + } + + leave_critical_section(flags); +} diff --git a/include/debug.h b/include/debug.h index f4bc4d3027..324c130a3d 100644 --- a/include/debug.h +++ b/include/debug.h @@ -595,6 +595,24 @@ # define pwminfo _none #endif +#ifdef CONFIG_DEBUG_RC_ERROR +# define rcerr _err +#else +# define rcerr _none +#endif + +#ifdef CONFIG_DEBUG_RC_WARN +# define rcwarn _warn +#else +# define rcwarn _none +#endif + +#ifdef CONFIG_DEBUG_RC_INFO +# define rcinfo _info +#else +# define rcinfo _none +#endif + #ifdef CONFIG_DEBUG_RTC_ERROR # define rtcerr _err #else diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 3ff8c2aa03..8526c9117b 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -99,6 +99,7 @@ #define _RPTUNBASE (0x2b00) /* Remote processor tunnel ioctl commands */ #define _NOTECTLBASE (0x2c00) /* Note filter control ioctl commands*/ #define _NOTERAMBASE (0x2d00) /* Noteram device ioctl commands*/ +#define _RCIOCBASE (0x2e00) /* Remote Control device ioctl commands */ #define _WLIOCBASE (0x8b00) /* Wireless modules ioctl network commands */ /* boardctl() commands share the same number space */ @@ -539,6 +540,11 @@ #define _NOTERAMIOCVALID(c) (_IOC_TYPE(c) == _NOTERAMBASE) #define _NOTERAMIOC(nr) _IOC(_NOTERAMBASE, nr) +/* Remote Control drivers ***************************************************/ + +#define _RCIOCVALID(c) (_IOC_TYPE(c)==_RCIOCBASE) +#define _RCIOC(nr) _IOC(_RCIOCBASE,nr) + /* Wireless driver network ioctl definitions ********************************/ /* (see nuttx/include/wireless/wireless.h */ diff --git a/include/nuttx/lirc.h b/include/nuttx/lirc.h new file mode 100644 index 0000000000..3a1d8adf7b --- /dev/null +++ b/include/nuttx/lirc.h @@ -0,0 +1,264 @@ +/**************************************************************************** + * include/nuttx/lirc.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_LIRC_H +#define __INCLUDE_NUTTX_LIRC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PULSE_BIT 0x01000000 +#define PULSE_MASK 0x00FFFFFF + +#define LIRC_MODE2_SPACE 0x00000000 +#define LIRC_MODE2_PULSE 0x01000000 +#define LIRC_MODE2_FREQUENCY 0x02000000 +#define LIRC_MODE2_TIMEOUT 0x03000000 + +#define LIRC_VALUE_MASK 0x00FFFFFF +#define LIRC_MODE2_MASK 0xFF000000 + +#define LIRC_SPACE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_SPACE) +#define LIRC_PULSE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_PULSE) +#define LIRC_FREQUENCY(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_FREQUENCY) +#define LIRC_TIMEOUT(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_TIMEOUT) + +#define LIRC_VALUE(val) ((val)&LIRC_VALUE_MASK) +#define LIRC_MODE2(val) ((val)&LIRC_MODE2_MASK) + +#define LIRC_IS_SPACE(val) (LIRC_MODE2(val) == LIRC_MODE2_SPACE) +#define LIRC_IS_PULSE(val) (LIRC_MODE2(val) == LIRC_MODE2_PULSE) +#define LIRC_IS_FREQUENCY(val) (LIRC_MODE2(val) == LIRC_MODE2_FREQUENCY) +#define LIRC_IS_TIMEOUT(val) (LIRC_MODE2(val) == LIRC_MODE2_TIMEOUT) + +/* used heavily by lirc userspace */ + +#define lirc_t int + +/* lirc compatible hardware features */ + +#define LIRC_MODE2SEND(x) (x) +#define LIRC_SEND2MODE(x) (x) +#define LIRC_MODE2REC(x) ((x) << 16) +#define LIRC_REC2MODE(x) ((x) >> 16) + +#define LIRC_MODE_RAW 0x00000001 +#define LIRC_MODE_PULSE 0x00000002 +#define LIRC_MODE_MODE2 0x00000004 +#define LIRC_MODE_SCANCODE 0x00000008 +#define LIRC_MODE_LIRCCODE 0x00000010 + +#define LIRC_CAN_SEND_RAW LIRC_MODE2SEND(LIRC_MODE_RAW) +#define LIRC_CAN_SEND_PULSE LIRC_MODE2SEND(LIRC_MODE_PULSE) +#define LIRC_CAN_SEND_MODE2 LIRC_MODE2SEND(LIRC_MODE_MODE2) +#define LIRC_CAN_SEND_SCANCODE LIRC_MODE2SEND(LIRC_MODE_SCANCODE) +#define LIRC_CAN_SEND_LIRCCODE LIRC_MODE2SEND(LIRC_MODE_LIRCCODE) + +#define LIRC_CAN_SEND_MASK 0x0000003f + +#define LIRC_CAN_SET_SEND_CARRIER 0x00000100 +#define LIRC_CAN_SET_SEND_DUTY_CYCLE 0x00000200 +#define LIRC_CAN_SET_TRANSMITTER_MASK 0x00000400 + +#define LIRC_CAN_REC_RAW LIRC_MODE2REC(LIRC_MODE_RAW) +#define LIRC_CAN_REC_PULSE LIRC_MODE2REC(LIRC_MODE_PULSE) +#define LIRC_CAN_REC_MODE2 LIRC_MODE2REC(LIRC_MODE_MODE2) +#define LIRC_CAN_REC_SCANCODE LIRC_MODE2REC(LIRC_MODE_SCANCODE) +#define LIRC_CAN_REC_LIRCCODE LIRC_MODE2REC(LIRC_MODE_LIRCCODE) + +#define LIRC_CAN_REC_MASK LIRC_MODE2REC(LIRC_CAN_SEND_MASK) + +#define LIRC_CAN_SET_REC_CARRIER (LIRC_CAN_SET_SEND_CARRIER << 16) +#define LIRC_CAN_SET_REC_DUTY_CYCLE (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16) + +#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000 +#define LIRC_CAN_SET_REC_CARRIER_RANGE 0x80000000 +#define LIRC_CAN_GET_REC_RESOLUTION 0x20000000 +#define LIRC_CAN_SET_REC_TIMEOUT 0x10000000 +#define LIRC_CAN_SET_REC_FILTER 0x08000000 + +#define LIRC_CAN_MEASURE_CARRIER 0x02000000 +#define LIRC_CAN_USE_WIDEBAND_RECEIVER 0x04000000 + +#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK) +#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK) + +#define LIRC_CAN_NOTIFY_DECODE 0x01000000 + +/* IOCTL commands for lirc driver */ + +#define LIRC_GET_FEATURES _RCIOC(0x0000) +#define LIRC_GET_SEND_MODE _RCIOC(0x0001) +#define LIRC_GET_REC_MODE _RCIOC(0x0002) +#define LIRC_GET_REC_RESOLUTION _RCIOC(0x0007) +#define LIRC_GET_MIN_TIMEOUT _RCIOC(0x0008) +#define LIRC_GET_MAX_TIMEOUT _RCIOC(0x0009) + +/* code length in bits, currently only for LIRC_MODE_LIRCCODE */ + +#define LIRC_GET_LENGTH _RCIOC(0x000f) +#define LIRC_SET_SEND_MODE _RCIOC(0x0011) +#define LIRC_SET_REC_MODE _RCIOC(0x0012) + +/* Note: these can reset the according pulse_width */ + +#define LIRC_SET_SEND_CARRIER _RCIOC(0x0013) +#define LIRC_SET_REC_CARRIER _RCIOC(0x0014) +#define LIRC_SET_SEND_DUTY_CYCLE _RCIOC(0x0015) +#define LIRC_SET_TRANSMITTER_MASK _RCIOC(0x0017) + +/* when a timeout != 0 is set the driver will send a + * LIRC_MODE2_TIMEOUT data packet, otherwise LIRC_MODE2_TIMEOUT is + * never sent, timeout is disabled by default + */ + +#define LIRC_SET_REC_TIMEOUT _RCIOC(0x0018) + +/* 1 enables, 0 disables timeout reports in MODE2 */ + +#define LIRC_SET_REC_TIMEOUT_REPORTS _RCIOC(0x0019) + +/* if enabled from the next key press on the driver will send + * LIRC_MODE2_FREQUENCY packets + */ + +#define LIRC_SET_MEASURE_CARRIER_MODE _RCIOC(0x001d) + +/* to set a range use LIRC_SET_REC_CARRIER_RANGE with the + * lower bound first and later LIRC_SET_REC_CARRIER with the upper bound + */ + +#define LIRC_SET_REC_CARRIER_RANGE _RCIOC(0x001f) + +#define LIRC_SET_WIDEBAND_RECEIVER _RCIOC(0x0023) + +/* Return the recording timeout, which is either set by + * the ioctl LIRC_SET_REC_TIMEOUT or by the kernel after setting + * the protocols. + */ + +#define LIRC_GET_REC_TIMEOUT _RCIOC(0x0024) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* struct lirc_scancode - decoded scancode with protocol for use with + * LIRC_MODE_SCANCODE + * + * timestamp: Timestamp in nanoseconds using CLOCK_MONOTONIC when IR + * was decoded. + * flags: should be 0 for transmit. When receiving scancodes, + * LIRC_SCANCODE_FLAG_TOGGLE or LIRC_SCANCODE_FLAG_REPEAT can be set + * depending on the protocol + * rc_proto: see enum rc_proto + * keycode: the translated keycode. Set to 0 for transmit. + * scancode: the scancode received or to be sent + */ + +struct lirc_scancode +{ + uint64_t timestamp; + uint16_t flags; + uint16_t rc_proto; + uint32_t keycode; + uint64_t scancode; +}; + +/* Set if the toggle bit of rc-5 or rc-6 is enabled */ + +#define LIRC_SCANCODE_FLAG_TOGGLE 1 + +/* Set if this is a nec or sanyo repeat */ + +#define LIRC_SCANCODE_FLAG_REPEAT 2 + +/* enum rc_proto - the Remote Controller protocol + * + * RC_PROTO_UNKNOWN: Protocol not known + * RC_PROTO_OTHER: Protocol known but proprietary + * RC_PROTO_RC5: Philips RC5 protocol + * RC_PROTO_RC5X_20: Philips RC5x 20 bit protocol + * RC_PROTO_RC5_SZ: StreamZap variant of RC5 + * RC_PROTO_JVC: JVC protocol + * RC_PROTO_SONY12: Sony 12 bit protocol + * RC_PROTO_SONY15: Sony 15 bit protocol + * RC_PROTO_SONY20: Sony 20 bit protocol + * RC_PROTO_NEC: NEC protocol + * RC_PROTO_NECX: Extended NEC protocol + * RC_PROTO_NEC32: NEC 32 bit protocol + * RC_PROTO_SANYO: Sanyo protocol + * RC_PROTO_MCIR2_KBD: RC6-ish MCE keyboard + * RC_PROTO_MCIR2_MSE: RC6-ish MCE mouse + * RC_PROTO_RC6_0: Philips RC6-0-16 protocol + * RC_PROTO_RC6_6A_20: Philips RC6-6A-20 protocol + * RC_PROTO_RC6_6A_24: Philips RC6-6A-24 protocol + * RC_PROTO_RC6_6A_32: Philips RC6-6A-32 protocol + * RC_PROTO_RC6_MCE: MCE (Philips RC6-6A-32 subtype) protocol + * RC_PROTO_SHARP: Sharp protocol + * RC_PROTO_XMP: XMP protocol + * RC_PROTO_CEC: CEC protocol + * RC_PROTO_IMON: iMon Pad protocol + * RC_PROTO_RCMM12: RC-MM protocol 12 bits + * RC_PROTO_RCMM24: RC-MM protocol 24 bits + * RC_PROTO_RCMM32: RC-MM protocol 32 bits + * RC_PROTO_XBOX_DVD: Xbox DVD Movie Playback Kit protocol + */ + +enum rc_proto +{ + RC_PROTO_UNKNOWN = 0, + RC_PROTO_OTHER = 1, + RC_PROTO_RC5 = 2, + RC_PROTO_RC5X_20 = 3, + RC_PROTO_RC5_SZ = 4, + RC_PROTO_JVC = 5, + RC_PROTO_SONY12 = 6, + RC_PROTO_SONY15 = 7, + RC_PROTO_SONY20 = 8, + RC_PROTO_NEC = 9, + RC_PROTO_NECX = 10, + RC_PROTO_NEC32 = 11, + RC_PROTO_SANYO = 12, + RC_PROTO_MCIR2_KBD = 13, + RC_PROTO_MCIR2_MSE = 14, + RC_PROTO_RC6_0 = 15, + RC_PROTO_RC6_6A_20 = 16, + RC_PROTO_RC6_6A_24 = 17, + RC_PROTO_RC6_6A_32 = 18, + RC_PROTO_RC6_MCE = 19, + RC_PROTO_SHARP = 20, + RC_PROTO_XMP = 21, + RC_PROTO_CEC = 22, + RC_PROTO_IMON = 23, + RC_PROTO_RCMM12 = 24, + RC_PROTO_RCMM24 = 25, + RC_PROTO_RCMM32 = 26, + RC_PROTO_XBOX_DVD = 27, +}; + +#endif diff --git a/include/nuttx/rc/lirc_dev.h b/include/nuttx/rc/lirc_dev.h new file mode 100644 index 0000000000..3c357bcc8f --- /dev/null +++ b/include/nuttx/rc/lirc_dev.h @@ -0,0 +1,271 @@ +/**************************************************************************** + * include/nuttx/rc/lirc_dev.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_RC_LIRC_DEV_H +#define __INCLUDE_NUTTX_RC_LIRC_DEV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +static inline uint64_t lirc_get_timestamp(void) +{ + struct timespec ts; + +#ifdef CONFIG_CLOCK_MONOTONIC + clock_gettime(CLOCK_MONOTONIC, &ts); +#else + clock_gettime(CLOCK_REALTIME, &ts); +#endif + + return 1000000000ull * ts.tv_sec + ts.tv_nsec; +} + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* enum lirc_driver_type_e - type of the LIRC driver. + * + * LIRC_DRIVER_SCANCODE: Driver or hardware generates a scancode. + * LIRC_DRIVER_IR_RAW: Driver or hardware generates pulse/space sequences. + * It needs a Infra-Red pulse/space decoder + * LIRC_DRIVER_IR_RAW_TX: Device transmitter only, + * driver requires pulse/space data sequence. + */ + +enum lirc_driver_type_e +{ + LIRC_DRIVER_SCANCODE = 0, + LIRC_DRIVER_IR_RAW, + LIRC_DRIVER_IR_RAW_TX, +}; + +/* The Raw interface is specific to InfraRed. */ + +struct lirc_raw_event_s +{ + union + { + uint32_t duration; + uint32_t carrier; + }; + + uint8_t duty_cycle; + + unsigned pulse:1; + unsigned reset:1; + unsigned timeout:1; + unsigned carrier_report:1; +}; + +/* struct lirc_ops_s - The lower half driver operations + * driver_type: specifies if protocol decoding is done in hard/software + * open: allow drivers to enable polling/irq when IR input dev is opened. + * close: allow drivers to disable polling/irq when IR input dev is closed. + * s_tx_mask: set transmitter mask (for devices with multiple tx outputs) + * s_tx_carrier: set transmit carrier frequency + * s_tx_duty_cycle: set transmit duty cycle (0% - 100%) + * s_rx_carrier_range: inform driver about carrier it is expected to handle + * tx_ir: transmit raw data to IR + * tx_scancode: transmit scancodes to IR + * s_learning_mode: enable wide band receiver used for learning + * s_carrier_report: enable carrier reports + * s_timeout: set hardware timeout in us + */ + +struct lirc_lowerhalf_s; +struct lirc_ops_s +{ + enum lirc_driver_type_e driver_type; + CODE int (*open)(FAR struct lirc_lowerhalf_s *lower); + CODE void (*close)(FAR struct lirc_lowerhalf_s *lower); + CODE int (*s_tx_mask)(FAR struct lirc_lowerhalf_s *lower, + unsigned int mask); + CODE int (*s_tx_carrier)(FAR struct lirc_lowerhalf_s *lower, + unsigned int carrier); + CODE int (*s_tx_duty_cycle)(FAR struct lirc_lowerhalf_s *lower, + unsigned int duty_cycle); + CODE int (*s_rx_carrier_range)(FAR struct lirc_lowerhalf_s *lower, + unsigned int min, unsigned int max); + CODE int (*tx_ir)(FAR struct lirc_lowerhalf_s *lower, + FAR unsigned int *txbuf, unsigned int n); + CODE int (*tx_scancode)(FAR struct lirc_lowerhalf_s *lower, + FAR struct lirc_scancode *txbuf); + CODE int (*s_learning_mode)(FAR struct lirc_lowerhalf_s *lower, + int enable); + CODE int (*s_carrier_report) (FAR struct lirc_lowerhalf_s *lower, + int enable); + CODE int (*s_timeout)(FAR struct lirc_lowerhalf_s *lower, + unsigned int timeout); +}; + +/* struct lirc_lowerhalf_s - represents a remote control device + * ops: the lirc lowerhalf driver operations + * priv: the pointer to lirc upperhalf handle, it's updated by lirc_register. + * timeout: optional time after which device stops sending data + * min_timeout: minimum timeout supported by device + * max_timeout: maximum timeout supported by device + * buffer_bytes: The size of intermediate buffer, in bytes unit. we recommend + * size is a multiple of unsigned int for LIRC_DRIVER_IR_RAW, + * is a multiple of struct lirc_scancode for + * LIRC_DRIVER_SCANCODE. we don't need to set buffer_bytes for + * LIRC_DRIVER_IR_RAW_TX. + * rx_resolution: resolution (in us) of input sampler + * tx_resolution: resolution (in us) of output sampler + */ + +struct lirc_lowerhalf_s +{ + FAR const struct lirc_ops_s *ops; + FAR void *priv; + unsigned int timeout; + unsigned int min_timeout; + unsigned int max_timeout; + unsigned int buffer_bytes; + unsigned int rx_resolution; + unsigned int tx_resolution; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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 int sample); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif +#endif