incubator-nuttx/drivers/timers/oneshot.c

382 lines
12 KiB
C

/****************************************************************************
* drivers/timers/oneshot.c
*
* Copyright (C) 2016-2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <fcntl.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/signal.h>
#include <nuttx/fs/fs.h>
#include <nuttx/semaphore.h>
#include <nuttx/timers/oneshot.h>
#ifdef CONFIG_ONESHOT
/****************************************************************************
* Private Type Definitions
****************************************************************************/
/* This structure describes the state of the upper half driver */
struct oneshot_dev_s
{
FAR struct oneshot_lowerhalf_s *od_lower; /* Lower-half driver state */
sem_t od_exclsem; /* Supports mutual exclusion */
/* Oneshot timer expiration notification information */
struct sigevent od_event; /* Signal info */
struct sigwork_s od_work; /* Signal work */
pid_t od_pid; /* PID to be notified */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int oneshot_open(FAR struct file *filep);
static int oneshot_close(FAR struct file *filep);
static ssize_t oneshot_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t oneshot_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
static int oneshot_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
static void oneshot_callback(FAR struct oneshot_lowerhalf_s *lower,
FAR void *arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_oneshot_ops =
{
oneshot_open, /* open */
oneshot_close, /* close */
oneshot_read, /* read */
oneshot_write, /* write */
NULL, /* seek */
oneshot_ioctl, /* ioctl */
NULL /* poll */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL /* unlink */
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: oneshot_callback
****************************************************************************/
static void oneshot_callback(FAR struct oneshot_lowerhalf_s *lower,
FAR void *arg)
{
FAR struct oneshot_dev_s *priv = (FAR struct oneshot_dev_s *)arg;
DEBUGASSERT(priv != NULL);
/* Signal the waiter.. if there is one */
nxsig_notification(priv->od_pid, &priv->od_event,
SI_QUEUE, &priv->od_work);
}
/****************************************************************************
* Name: oneshot_open
*
* Description:
* This function is called whenever the PWM device is opened.
*
****************************************************************************/
static int oneshot_open(FAR struct file *filep)
{
tmrinfo("Opening...\n");
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
return OK;
}
/****************************************************************************
* Name: oneshot_close
*
* Description:
* This function is called when the PWM device is closed.
*
****************************************************************************/
static int oneshot_close(FAR struct file *filep)
{
tmrinfo("Closing...\n");
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
return OK;
}
/****************************************************************************
* Name: oneshot_read
*
* Description:
* A dummy read method. This is provided only to satsify the VFS layer.
*
****************************************************************************/
static ssize_t oneshot_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
/* Return zero -- usually meaning end-of-file */
tmrinfo("buflen=%ld\n", (unsigned long)buflen);
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
return 0;
}
/****************************************************************************
* Name: oneshot_write
*
* Description:
* A dummy write method. This is provided only to satsify the VFS layer.
*
****************************************************************************/
static ssize_t oneshot_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
/* Return a failure */
tmrinfo("buflen=%ld\n", (unsigned long)buflen);
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
return -EPERM;
}
/****************************************************************************
* Name: oneshot_ioctl
*
* Description:
* The standard ioctl method. This is where ALL of the PWM work is done.
*
****************************************************************************/
static int oneshot_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode;
FAR struct oneshot_dev_s *priv;
int ret;
tmrinfo("cmd=%d arg=%08lx\n", cmd, (unsigned long)arg);
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
inode = filep->f_inode;
priv = (FAR struct oneshot_dev_s *)inode->i_private;
DEBUGASSERT(priv != NULL);
/* Get exclusive access to the device structures */
ret = nxsem_wait(&priv->od_exclsem);
if (ret < 0)
{
return ret;
}
/* Handle oneshot timer ioctl commands */
switch (cmd)
{
/* OSIOC_MAXDELAY - Return the maximum delay that can be supported
* by this timer.
* Argument: A reference to a struct timespec in
* which the maximum time will be returned.
*/
case OSIOC_MAXDELAY:
{
FAR struct timespec *ts = (FAR struct timespec *)((uintptr_t)arg);
DEBUGASSERT(ts != NULL);
ret = ONESHOT_MAX_DELAY(priv->od_lower, ts);
}
break;
/* OSIOC_START - Start the oneshot timer
* Argument: A reference to struct oneshot_start_s
*/
case OSIOC_START:
{
FAR struct oneshot_start_s *start;
pid_t pid;
start = (FAR struct oneshot_start_s *)((uintptr_t)arg);
DEBUGASSERT(start != NULL);
/* Save signaling information */
priv->od_event = start->event;
pid = start->pid;
if (pid == 0)
{
pid = getpid();
}
priv->od_pid = pid;
/* Start the oneshot timer */
ret = ONESHOT_START(priv->od_lower, oneshot_callback, priv,
&start->ts);
}
break;
/* OSIOC_CANCEL - Stop the timer
* Argument: A reference to a struct timespec in
* which the time remaining will be returned.
*/
case OSIOC_CANCEL:
{
FAR struct timespec *ts = (FAR struct timespec *)((uintptr_t)arg);
/* Cancel the oneshot timer */
ret = ONESHOT_CANCEL(priv->od_lower, ts);
nxsig_cancel_notification(&priv->od_work);
}
break;
/* OSIOC_CURRENT - Get the current time
* Argument: A reference to a struct timespec in
* which the current time will be returned.
*/
case OSIOC_CURRENT:
{
FAR struct timespec *ts = (FAR struct timespec *)((uintptr_t)arg);
/* Get the current time */
ret = ONESHOT_CURRENT(priv->od_lower, ts);
}
break;
default:
{
tmrerr("ERROR: Unrecognized cmd: %d arg: %ld\n", cmd, arg);
ret = -ENOTTY;
}
break;
}
nxsem_post(&priv->od_exclsem);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: oneshot_register
*
* Description:
* Register the oneshot device as 'devpath'
*
* Input Parameters:
* devpath - The full path to the driver to register. E.g., "/dev/oneshot0"
* 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 oneshot_register(FAR const char *devname,
FAR struct oneshot_lowerhalf_s *lower)
{
FAR struct oneshot_dev_s *priv;
int ret;
sninfo("devname=%s lower=%p\n", devname, lower);
DEBUGASSERT(devname != NULL && lower != NULL);
/* Allocate a new oneshot timer driver instance */
priv = (FAR struct oneshot_dev_s *)
kmm_zalloc(sizeof(struct oneshot_dev_s));
if (!priv)
{
snerr("ERROR: Failed to allocate device structure\n");
return -ENOMEM;
}
/* Initialize the new oneshot timer driver instance */
priv->od_lower = lower;
nxsem_init(&priv->od_exclsem, 0, 1);
/* And register the oneshot timer driver */
ret = register_driver(devname, &g_oneshot_ops, 0666, priv);
if (ret < 0)
{
snerr("ERROR: register_driver failed: %d\n", ret);
nxsem_destroy(&priv->od_exclsem);
kmm_free(priv);
}
return ret;
}
#endif /* CONFIG_ONESHOT */