From 8f0c912fff4b944e54d03e04dcc64166b98d91b9 Mon Sep 17 00:00:00 2001 From: Alan Carvalho de Assis Date: Tue, 13 Oct 2015 07:27:16 -0600 Subject: [PATCH] Add Zero Cross device driver support --- ChangeLog | 3 + drivers/sensors/Kconfig | 4 + drivers/sensors/Make.defs | 6 + drivers/sensors/zerocross.c | 546 ++++++++++++++++++++++++++++++++++++ include/nuttx/fs/ioctl.h | 65 +++-- 5 files changed, 595 insertions(+), 29 deletions(-) create mode 100644 drivers/sensors/zerocross.c diff --git a/ChangeLog b/ChangeLog index fb499c6313..af0e99f236 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11027,3 +11027,6 @@ This lower half driver is only usable in a limited number of situations, but can still serve as a module for the lower half button driver. (2015-10-13). + * drivers/sensors/zerocross.c and include/nuttx/sensors/zerocross.h: + Add Zero Cross device driver support. From Alan Carvalho de Assis + (2015-10-13). diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 510696bce7..a9d0d3c364 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -123,3 +123,7 @@ config LM92 config QENCODER bool "Qencoder" default n + +config ZEROCROSS + bool "Zero Cross Sensor" + default n diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index 9a00862dfa..89a10808e9 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -101,6 +101,12 @@ ifeq ($(CONFIG_QENCODER),y) CSRCS += qencoder.c endif +# Zero Cross upper half + +ifeq ($(CONFIG_ZEROCROSS),y) + CSRCS += zerocross.c +endif + # Include sensor driver build support DEPPATH += --dep-path sensors diff --git a/drivers/sensors/zerocross.c b/drivers/sensors/zerocross.c new file mode 100644 index 0000000000..22f2f499a9 --- /dev/null +++ b/drivers/sensors/zerocross.c @@ -0,0 +1,546 @@ +/**************************************************************************** + * drivers/sensors/zerocross.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Compilation Switches + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef CONFIG_ZEROCROSS + +#ifdef CONFIG_DISABLE_SIGNALS +#error "This driver needs SIGNAL support, remove CONFIG_DISABLE_SIGNALS" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Debug ********************************************************************/ + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +/* This structure describes the state of the upper half driver */ + +struct zc_upperhalf_s +{ + FAR struct zc_lowerhalf_s *lower; /* lower-half state */ + sem_t exclsem; /* Supports mutual exclusion */ + + /* The following is a singly linked list of open references to the + * zero cross device. + */ + + FAR struct zc_open_s *zu_open; + +}; + +/* This structure describes the state of one open zero cross driver instance */ + +struct zc_open_s +{ + /* Supports a singly linked list */ + + FAR struct zc_open_s *do_flink; + + /* The following will be true if we are closing */ + + volatile bool do_closing; + + /* Zero cross event notification information */ + + pid_t do_pid; + struct zc_notify_s do_notify; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int zc_open(FAR struct file *filep); +static int zc_close(FAR struct file *filep); +static ssize_t zc_read(FAR struct file *filep, FAR char *buffer, size_t + buflen); +static ssize_t zc_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int zc_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +static void zerocross_enable(FAR struct zc_upperhalf_s *priv); +static void zerocross_interrupt(FAR const struct zc_lowerhalf_s *lower, + FAR void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_zcops = +{ + zc_open, /* open */ + zc_close, /* close */ + zc_read, /* read */ + zc_write, /* write */ + 0, /* seek */ + zc_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +}; + +volatile int sample = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: zerocross_enable + ****************************************************************************/ + +static void zerocross_enable(FAR struct zc_upperhalf_s *priv) +{ + FAR const struct zc_lowerhalf_s *lower; + irqstate_t flags; + + DEBUGASSERT(priv && priv->lower); + lower = priv->lower; + + /* This routine is called both task level and interrupt level, so + * interrupts must be disabled. + */ + + flags = irqsave(); + + /* Enable interrupts */ + + DEBUGASSERT(lower->zc_enable); + + /* Enable interrupts with the new button set */ + + lower->zc_enable(lower, (zc_interrupt_t)zerocross_interrupt, priv); + + irqrestore(flags); +} + +/**************************************************************************** + * Name: zerocross_interrupt + ****************************************************************************/ + +static void zerocross_interrupt(FAR const struct zc_lowerhalf_s *lower, + FAR void *arg) +{ + FAR struct zc_upperhalf_s *priv = (FAR struct zc_upperhalf_s *)arg; + FAR struct zc_open_s *opriv; + irqstate_t flags; + + /* This routine is called both task level and interrupt level, so + * interrupts must be disabled. + */ + + flags = irqsave(); + + /* Update sample value */ + + sample++; + + /* Visit each opened reference and notify a zero cross event */ + + for (opriv = priv->zu_open; opriv; opriv = opriv->do_flink) + { + /* Signal the waiter */ + +#ifdef CONFIG_CAN_PASS_STRUCTS + union sigval value; + value.sival_int = (int)sample; + (void)sigqueue(opriv->do_pid, opriv->do_notify.zc_signo, value); +#else + (void)sigqueue(opriv->do_pid, opriv->do_notify.zc_signo, + (FAR void *)sample); +#endif + } + + irqrestore(flags); +} + +/************************************************************************************ + * Name: zc_open + * + * Description: + * This function is called whenever the PWM device is opened. + * + ************************************************************************************/ + +static int zc_open(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct zc_upperhalf_s *priv; + FAR const struct zc_lowerhalf_s *lower; + FAR struct zc_open_s *opriv; + int ret; + + DEBUGASSERT(filep && filep->f_inode); + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + priv = (FAR struct zc_upperhalf_s *)inode->i_private; + + /* Get exclusive access to the driver structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + snvdbg("ERROR: sem_wait failed: %d\n", ret); + return ret; + } + + /* Allocate a new open structure */ + + opriv = (FAR struct zc_open_s *)kmm_zalloc(sizeof(struct zc_open_s)); + if (!opriv) + { + snvdbg("ERROR: Failled to allocate open structure\n"); + ret = -ENOMEM; + goto errout_with_sem; + } + + /* Attach the open structure to the device */ + + opriv->do_flink = priv->zu_open; + priv->zu_open = opriv; + + /* Attach the open structure to the file structure */ + + filep->f_priv = (FAR void *)opriv; + ret = OK; + +errout_with_sem: + sem_post(&priv->exclsem); + return ret; +} + +/************************************************************************************ + * Name: zc_close + * + * Description: + * This function is called when the PWM device is closed. + * + ************************************************************************************/ + +static int zc_close(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct zc_upperhalf_s *priv; + FAR struct zc_open_s *opriv; + FAR struct zc_open_s *curr; + FAR struct zc_open_s *prev; + irqstate_t flags; + bool closing; + int ret; + + DEBUGASSERT(filep && filep->f_priv && filep->f_inode); + opriv = filep->f_priv; + inode = filep->f_inode; + DEBUGASSERT(inode->i_private); + priv = (FAR struct zc_upperhalf_s *)inode->i_private; + + /* Handle an improbable race conditions with the following atomic test + * and set. + * + * This is actually a pretty feeble attempt to handle this. The + * improbable race condition occurs if two different threads try to + * close the zero cross driver at the same time. The rule: don't do + * that! It is feeble because we do not really enforce stale pointer + * detection anyway. + */ + + flags = irqsave(); + closing = opriv->do_closing; + opriv->do_closing = true; + irqrestore(flags); + + if (closing) + { + /* Another thread is doing the close */ + + return OK; + } + + /* Get exclusive access to the driver structure */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + snvdbg("ERROR: sem_wait failed: %d\n", ret); + return ret; + } + + /* Find the open structure in the list of open structures for the device */ + + for (prev = NULL, curr = priv->zu_open; + curr && curr != opriv; + prev = curr, curr = curr->do_flink); + + DEBUGASSERT(curr); + if (!curr) + { + snvdbg("ERROR: Failed to find open entry\n"); + ret = -ENOENT; + goto errout_with_exclsem; + } + + /* Remove the structure from the device */ + + if (prev) + { + prev->do_flink = opriv->do_flink; + } + else + { + priv->zu_open = opriv->do_flink; + } + + /* And free the open structure */ + + kmm_free(opriv); + + /* Enable/disable interrupt handling */ + + zerocross_enable(priv); + ret = OK; + +errout_with_exclsem: + sem_post(&priv->exclsem); + return ret; +} + +/************************************************************************************ + * Name: zc_read + * + * Description:O + * A dummy read method. This is provided only to satsify the VFS layer. + * + ************************************************************************************/ + +static ssize_t zc_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + /* Return zero -- usually meaning end-of-file */ + + return 0; +} + +/************************************************************************************ + * Name: zc_write + * + * Description: + * A dummy write method. This is provided only to satsify the VFS layer. + * + ************************************************************************************/ + +static ssize_t zc_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) +{ + /* Return a failure */ + + return -EPERM; +} + +/************************************************************************************ + * Name: zc_ioctl + * + * Description: + * The standard ioctl method. This is where ALL of the PWM work is done. + * + ************************************************************************************/ + +static int zc_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode; + FAR struct zc_upperhalf_s *priv; + FAR struct zc_open_s *opriv; + FAR struct zc_lowerhalf_s *lower; + int ret; + + snvdbg("cmd: %d arg: %ld\n", cmd, arg); + DEBUGASSERT(filep && filep->f_priv && filep->f_inode); + opriv = filep->f_priv; + inode = filep->f_inode; + DEBUGASSERT(inode->i_private) + priv = (FAR struct zc_upperhalf_s *)inode->i_private; + + /* Get exclusive access to the device structures */ + + ret = sem_wait(&priv->exclsem); + if (ret < 0) + { + return ret; + } + + /* Handle built-in ioctl commands */ + + ret = -EINVAL; + switch (cmd) + { +#ifndef CONFIG_DISABLE_SIGNALS + /* Command: ZCIOC_REGISTER + * Description: Register to receive a signal whenever there is zero + * cross detection interrupt. + * Argument: A read-only pointer to an instance of struct + * zc_notify_s + * Return: Zero (OK) on success. Minus one will be returned on + * failure with the errno value set appropriately. + */ + + case ZCIOC_REGISTER: + { + FAR struct zc_notify_s *notify = + (FAR struct zc_notify_s *)((uintptr_t)arg); + + if (notify) + { + /* Save the notification events */ + + opriv->do_notify.zc_signo = notify->zc_signo; + opriv->do_pid = getpid(); + + /* Enable/disable interrupt handling */ + + zerocross_enable(priv); + ret = OK; + } + } + break; +#endif + + default: + { + snvdbg("Forwarding unrecognized cmd: %d arg: %ld\n", cmd, arg); + ret = -ENOTTY; + } + break; + } + + sem_post(&priv->exclsem); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: zc_register + * + * Description: + * Register the Zero Cross lower half device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/zc0" + * lower - An instance of the lower half interface + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. The following + * possible error values may be returned (most are returned by + * register_driver()): + * + * EINVAL - 'path' is invalid for this operation + * EEXIST - An inode already exists at 'path' + * ENOMEM - Failed to allocate in-memory resources for the operation + * + ****************************************************************************/ + +int zc_register(FAR const char *devname, FAR struct zc_lowerhalf_s *lower) +{ + FAR struct zc_upperhalf_s *priv; + int ret; + + DEBUGASSERT(devname && lower); + + /* Allocate a new zero cross driver instance */ + + priv = (FAR struct zc_upperhalf_s *) + kmm_zalloc(sizeof(struct zc_upperhalf_s)); + + if (!priv) + { + snvdbg("ERROR: Failed to allocate device structure\n"); + return -ENOMEM; + } + + /* Make sure that zero cross interrupt is disabled */ + + DEBUGASSERT(lower->zc_enable); + lower->zc_enable(lower, NULL, NULL); + + /* Initialize the new zero cross driver instance */ + + priv->lower = lower; + sem_init(&priv->exclsem, 0, 1); + + /* And register the zero cross driver */ + + ret = register_driver(devname, &g_zcops, 0666, priv); + if (ret < 0) + { + snvdbg("ERROR: register_driver failed: %d\n", ret); + sem_destroy(&priv->exclsem); + kmm_free(priv); + } + + return ret; +} + +#endif /* CONFIG_ZEROCROSS */ diff --git a/include/nuttx/fs/ioctl.h b/include/nuttx/fs/ioctl.h index 7a5d17f702..6814787005 100644 --- a/include/nuttx/fs/ioctl.h +++ b/include/nuttx/fs/ioctl.h @@ -78,7 +78,8 @@ #define _RELAYBASE (0x1900) /* Relay devices ioctl commands */ #define _CANBASE (0x1a00) /* CAN ioctl commands */ #define _BTNBASE (0x1b00) /* Button ioctl commands */ -#define _BOARDBASE (0x1c00) /* boardctl ioctl commands */ +#define _ZCBASE (0x1c00) /* Zero Cross ioctl commands */ +#define _BOARDBASE (0x1d00) /* boardctl ioctl commands */ /* Macros used to manage ioctl commands */ @@ -268,56 +269,56 @@ /* NuttX Audio driver ioctl definitions *************************************/ /* (see nuttx/audio/audio.h) */ -#define _AUDIOIOCVALID(c) (_IOC_TYPE(c)==_AUDIOIOCBASE) -#define _AUDIOIOC(nr) _IOC(_AUDIOIOCBASE,nr) +#define _AUDIOIOCVALID(c) (_IOC_TYPE(c)==_AUDIOIOCBASE) +#define _AUDIOIOC(nr) _IOC(_AUDIOIOCBASE,nr) /* Segment LCD driver ioctl definitions *************************************/ /* (see nuttx/include/lcd/slcd_codec.h */ -#define _SLCDIOCVALID(c) (_IOC_TYPE(c)==_SLCDIOCBASE) -#define _SLCDIOC(nr) _IOC(_SLCDIOCBASE,nr) +#define _SLCDIOCVALID(c) (_IOC_TYPE(c)==_SLCDIOCBASE) +#define _SLCDIOC(nr) _IOC(_SLCDIOCBASE,nr) /* Wireless driver ioctl definitions ****************************************/ /* (see nuttx/include/wireless/wireless.h */ -#define _WLIOCVALID(c) (_IOC_TYPE(c)==_WLIOCBASE) -#define _WLIOC(nr) _IOC(_WLIOCBASE,nr) +#define _WLIOCVALID(c) (_IOC_TYPE(c)==_WLIOCBASE) +#define _WLIOC(nr) _IOC(_WLIOCBASE,nr) /* Application Config Data driver ioctl definitions *************************/ /* (see nuttx/include/configdata.h */ -#define _CFGDIOCVALID(c) (_IOC_TYPE(c)==_CFGDIOCBASE) -#define _CFGDIOC(nr) _IOC(_CFGDIOCBASE,nr) +#define _CFGDIOCVALID(c) (_IOC_TYPE(c)==_CFGDIOCBASE) +#define _CFGDIOC(nr) _IOC(_CFGDIOCBASE,nr) /* Timer driver ioctl commands **********************************************/ /* (see nuttx/include/timer.h */ -#define _TCIOCVALID(c) (_IOC_TYPE(c)==_TCIOCBASE) -#define _TCIOC(nr) _IOC(_TCIOCBASE,nr) +#define _TCIOCVALID(c) (_IOC_TYPE(c)==_TCIOCBASE) +#define _TCIOC(nr) _IOC(_TCIOCBASE,nr) /* Discrete joystick driver ioctl definitions *******************************/ /* (see nuttx/include/input/djoystick.h */ -#define _DJOYIOCVALID(c) (_IOC_TYPE(c)==_DJOYBASE) -#define _DJOYIOC(nr) _IOC(_DJOYBASE,nr) +#define _DJOYIOCVALID(c) (_IOC_TYPE(c)==_DJOYBASE) +#define _DJOYIOC(nr) _IOC(_DJOYBASE,nr) /* Analog joystick driver ioctl definitions *********************************/ /* (see nuttx/include/input/ajoystick.h */ -#define _AJOYIOCVALID(c) (_IOC_TYPE(c)==_AJOYBASE) -#define _AJOYIOC(nr) _IOC(_AJOYBASE,nr) +#define _AJOYIOCVALID(c) (_IOC_TYPE(c)==_AJOYBASE) +#define _AJOYIOC(nr) _IOC(_AJOYBASE,nr) /* FIFOs and pipe driver ioctl definitions **********************************/ -#define _PIPEIOCVALID(c) (_IOC_TYPE(c)==_PIPEBASE) -#define _PIPEIOC(nr) _IOC(_PIPEBASE,nr) +#define _PIPEIOCVALID(c) (_IOC_TYPE(c)==_PIPEBASE) +#define _PIPEIOC(nr) _IOC(_PIPEBASE,nr) -#define PIPEIOC_POLICY _PIPEIOC(0x0001) /* Set buffer policy - * IN: unsigned long integer - * 0=free on last close - * (default) - * 1=fre when empty - * OUT: None */ +#define PIPEIOC_POLICY _PIPEIOC(0x0001) /* Set buffer policy + * IN: unsigned long integer + * 0=free on last close + * (default) + * 1=fre when empty + * OUT: None */ /* RTC driver ioctl definitions *********************************************/ /* (see nuttx/include/rtc.h */ @@ -328,20 +329,26 @@ /* Relay driver ioctl definitions *******************************************/ /* (see nuttx/power/relay.h */ -#define _RELAYIOCVALID(c) (_IOC_TYPE(c)==_RELAYBASE) -#define _RELAYIOC(nr) _IOC(_RELAYBASE,nr) +#define _RELAYIOCVALID(c) (_IOC_TYPE(c)==_RELAYBASE) +#define _RELAYIOC(nr) _IOC(_RELAYBASE,nr) /* CAN driver ioctl definitions *********************************************/ /* (see nuttx/can.h */ -#define _CANIOCVALID(c) (_IOC_TYPE(c)==_CANBASE) -#define _CANIOC(nr) _IOC(_CANBASE,nr) +#define _CANIOCVALID(c) (_IOC_TYPE(c)==_CANBASE) +#define _CANIOC(nr) _IOC(_CANBASE,nr) /* Button driver ioctl definitions ******************************************/ /* (see nuttx/can.h */ -#define _BTNIOCVALID(c) (_IOC_TYPE(c)==_BTNBASE) -#define _BTNIOC(nr) _IOC(_BTNBASE,nr) +#define _BTNIOCVALID(c) (_IOC_TYPE(c)==_BTNBASE) +#define _BTNIOC(nr) _IOC(_BTNBASE,nr) + +/* Zero Cross driver ioctl definitions **************************************/ +/* (see nuttx/include/sensor/zerocross.h */ + +#define _ZCIOCVALID(c) (_IOC_TYPE(c)==_ZCBASE) +#define _ZCIOC(nr) _IOC(_ZCBASE,nr) /* boardctl() command definitions *******************************************/