6a3c2aded6
* Simplify EINTR/ECANCEL error handling 1. Add semaphore uninterruptible wait function 2 .Replace semaphore wait loop with a single uninterruptible wait 3. Replace all sem_xxx to nxsem_xxx * Unify the void cast usage 1. Remove void cast for function because many place ignore the returned value witout cast 2. Replace void cast for variable with UNUSED macro
881 lines
24 KiB
C
881 lines
24 KiB
C
/****************************************************************************
|
|
* drivers/timers/rtc.c
|
|
*
|
|
* Copyright (C) 2015, 2017, 2019 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 <semaphore.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/clock.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/timers/rtc.h>
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_PERIODIC)
|
|
struct rtc_alarminfo_s
|
|
{
|
|
bool active; /* True: alarm is active */
|
|
pid_t pid; /* Identifies task to be notified */
|
|
struct sigevent event; /* Describe the way a task is to be notified */
|
|
struct sigwork_s work; /* Signal work */
|
|
};
|
|
#endif
|
|
|
|
struct rtc_upperhalf_s
|
|
{
|
|
FAR struct rtc_lowerhalf_s *lower; /* Contained lower half driver */
|
|
sem_t exclsem; /* Supports mutual exclusion */
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
uint8_t crefs; /* Number of open references */
|
|
bool unlinked; /* True if the driver has been unlinked */
|
|
#endif
|
|
|
|
#ifdef CONFIG_RTC_ALARM
|
|
/* This is an array, indexed by the alarm ID, that provides information
|
|
* needed to map an alarm expiration to a signal event.
|
|
*/
|
|
|
|
struct rtc_alarminfo_s alarminfo[CONFIG_RTC_NALARMS];
|
|
#endif
|
|
|
|
#ifdef CONFIG_RTC_PERIODIC
|
|
/* Currently only one periodic wakeup is supported. */
|
|
|
|
struct rtc_alarminfo_s periodicinfo;
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Internal logic */
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
static void rtc_destroy(FAR struct rtc_upperhalf_s *upper);
|
|
#endif
|
|
|
|
#ifdef CONFIG_RTC_ALARM
|
|
static void rtc_alarm_callback(FAR void *priv, int id);
|
|
#endif
|
|
|
|
#ifdef CONFIG_RTC_PERIODIC
|
|
static void rtc_periodic_callback(FAR void *priv, int id);
|
|
#endif
|
|
|
|
/* Character driver methods */
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
static int rtc_open(FAR struct file *filep);
|
|
static int rtc_close(FAR struct file *filep);
|
|
#endif
|
|
|
|
static ssize_t rtc_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static ssize_t rtc_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen);
|
|
static int rtc_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
static int rtc_unlink(FAR struct inode *inode);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations rtc_fops =
|
|
{
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
rtc_open, /* open */
|
|
rtc_close, /* close */
|
|
#else
|
|
NULL, /* open */
|
|
NULL, /* close */
|
|
#endif
|
|
rtc_read, /* read */
|
|
rtc_write, /* write */
|
|
NULL, /* seek */
|
|
rtc_ioctl, /* ioctl */
|
|
NULL, /* poll */
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
rtc_unlink /* unlink */
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_destroy
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
static void rtc_destroy(FAR struct rtc_upperhalf_s *upper)
|
|
{
|
|
/* If the lower half driver provided a destroy method, then call that
|
|
* method now in order to clean up resources used by the lower-half driver.
|
|
*/
|
|
|
|
DEBUGASSERT(upper->lower && upper->lower->ops);
|
|
if (upper->lower->ops->destroy)
|
|
{
|
|
upper->lower->ops->destroy(upper->lower);
|
|
}
|
|
|
|
/* And free our container */
|
|
|
|
nxsem_destroy(&upper->exclsem);
|
|
kmm_free(upper);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_alarm_callback
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_RTC_ALARM
|
|
static void rtc_alarm_callback(FAR void *priv, int alarmid)
|
|
{
|
|
FAR struct rtc_upperhalf_s *upper = (FAR struct rtc_upperhalf_s *)priv;
|
|
FAR struct rtc_alarminfo_s *alarminfo;
|
|
|
|
DEBUGASSERT(upper != NULL && alarmid >= 0 && alarmid < CONFIG_RTC_NALARMS);
|
|
alarminfo = &upper->alarminfo[alarmid];
|
|
|
|
/* Do we think that the alarm is active? It might be due to some
|
|
* race condition between a cancellation event and the alarm
|
|
* expiration.
|
|
*/
|
|
|
|
if (alarminfo->active)
|
|
{
|
|
/* Yes.. signal the alarm expiration */
|
|
|
|
nxsig_notification(alarminfo->pid, &alarminfo->event,
|
|
SI_QUEUE, &alarminfo->work);
|
|
}
|
|
|
|
/* The alarm is no longer active */
|
|
|
|
alarminfo->active = false;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_periodic_callback
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_RTC_PERIODIC
|
|
static void rtc_periodic_callback(FAR void *priv, int alarmid)
|
|
{
|
|
FAR struct rtc_upperhalf_s *upper = (FAR struct rtc_upperhalf_s *)priv;
|
|
FAR struct rtc_alarminfo_s *alarminfo;
|
|
|
|
DEBUGASSERT(upper != NULL && alarmid >= 0);
|
|
alarminfo = &upper->periodicinfo;
|
|
|
|
/* Do we think that the alarm is active? It might be due to some
|
|
* race condition between a cancellation event and the alarm
|
|
* expiration.
|
|
*/
|
|
|
|
if (alarminfo->active)
|
|
{
|
|
/* Yes.. signal the alarm expiration */
|
|
|
|
nxsig_notification(alarminfo->pid, &alarminfo->event,
|
|
SI_QUEUE, &alarminfo->work);
|
|
}
|
|
|
|
/* The alarm is no longer active */
|
|
|
|
alarminfo->active = false;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_open
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
static int rtc_open(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct rtc_upperhalf_s *upper;
|
|
int ret;
|
|
|
|
/* Get the reference to our internal state structure from the inode
|
|
* structure.
|
|
*/
|
|
|
|
DEBUGASSERT(filep);
|
|
inode = filep->f_inode;
|
|
DEBUGASSERT(inode && inode->i_private);
|
|
upper = inode->i_private;
|
|
|
|
/* Get exclusive access to the device structures */
|
|
|
|
ret = nxsem_wait(&upper->exclsem);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Increment the count of open references on the RTC driver */
|
|
|
|
upper->crefs++;
|
|
DEBUGASSERT(upper->crefs > 0);
|
|
nxsem_post(&upper->exclsem);
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_close
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
static int rtc_close(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct rtc_upperhalf_s *upper;
|
|
int ret;
|
|
|
|
/* Get the reference to our internal state structure from the inode
|
|
* structure.
|
|
*/
|
|
|
|
DEBUGASSERT(filep);
|
|
inode = filep->f_inode;
|
|
DEBUGASSERT(inode && inode->i_private);
|
|
upper = inode->i_private;
|
|
|
|
/* Get exclusive access to the device structures */
|
|
|
|
ret = nxsem_wait(&upper->exclsem);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Decrement the count of open references on the RTC driver */
|
|
|
|
DEBUGASSERT(upper->crefs > 0);
|
|
upper->crefs--;
|
|
nxsem_post(&upper->exclsem);
|
|
|
|
/* If the count has decremented to zero and the driver has been unlinked,
|
|
* then commit Hara-Kiri now.
|
|
*/
|
|
|
|
if (upper->crefs == 0 && upper->unlinked)
|
|
{
|
|
rtc_destroy(upper);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_read
|
|
****************************************************************************/
|
|
|
|
static ssize_t rtc_read(FAR struct file *filep, FAR char *buffer, size_t len)
|
|
{
|
|
return 0; /* Return EOF */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_write
|
|
****************************************************************************/
|
|
|
|
static ssize_t rtc_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t len)
|
|
{
|
|
return len; /* Say that everything was written */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_ioctl
|
|
****************************************************************************/
|
|
|
|
static int rtc_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct rtc_upperhalf_s *upper;
|
|
FAR const struct rtc_ops_s *ops;
|
|
int ret = -ENOSYS;
|
|
|
|
/* Get the reference to our internal state structure from the inode
|
|
* structure.
|
|
*/
|
|
|
|
DEBUGASSERT(filep);
|
|
inode = filep->f_inode;
|
|
DEBUGASSERT(inode && inode->i_private);
|
|
upper = inode->i_private;
|
|
DEBUGASSERT(upper->lower && upper->lower->ops);
|
|
|
|
/* Get exclusive access to the device structures */
|
|
|
|
ret = nxsem_wait(&upper->exclsem);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* We simply forward all ioctl() commands to the lower half. The upper
|
|
* half is nothing more than a thin driver shell over the lower level
|
|
* RTC implementation.
|
|
*/
|
|
|
|
ops = upper->lower->ops;
|
|
switch (cmd)
|
|
{
|
|
/* RTC_RD_TIME returns the current RTC time.
|
|
*
|
|
* Argument: A writeable reference to a struct rtc_time to receive the
|
|
* RTC's time.
|
|
*/
|
|
|
|
case RTC_RD_TIME:
|
|
{
|
|
FAR struct rtc_time *rtctime =
|
|
(FAR struct rtc_time *)((uintptr_t)arg);
|
|
|
|
if (ops->rdtime)
|
|
{
|
|
ret = ops->rdtime(upper->lower, rtctime);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* RTC_SET_TIME sets the RTC's time
|
|
*
|
|
* Argument: A read-only reference to a struct rtc_time containing
|
|
* the new time to be set.
|
|
*/
|
|
|
|
case RTC_SET_TIME:
|
|
{
|
|
FAR const struct rtc_time *rtctime =
|
|
(FAR const struct rtc_time *)((uintptr_t)arg);
|
|
|
|
if (ops->settime)
|
|
{
|
|
ret = ops->settime(upper->lower, rtctime);
|
|
if (ret >= 0)
|
|
{
|
|
/* If the RTC time was set successfully, then update the
|
|
* current system time to match.
|
|
*/
|
|
|
|
clock_synchronize();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* RTC_HAVE_SET_TIME checks if RTC's time had been set
|
|
*
|
|
* Argument: A writable reference to a bool to receive true/false return
|
|
* value of the check.
|
|
*/
|
|
|
|
case RTC_HAVE_SET_TIME:
|
|
{
|
|
FAR bool *have_set_time = (FAR bool *)((uintptr_t)arg);
|
|
|
|
if (ops->havesettime)
|
|
{
|
|
*have_set_time = ops->havesettime(upper->lower);
|
|
ret = OK;
|
|
}
|
|
}
|
|
break;
|
|
|
|
#ifdef CONFIG_RTC_ALARM
|
|
/* RTC_SET_ALARM sets the alarm time.
|
|
*
|
|
* Argument: A read-only reference to a struct rtc_time containing the
|
|
* new alarm time to be set.
|
|
*/
|
|
|
|
case RTC_SET_ALARM:
|
|
{
|
|
FAR const struct rtc_setalarm_s *alarminfo =
|
|
(FAR const struct rtc_setalarm_s *)((uintptr_t)arg);
|
|
FAR struct rtc_alarminfo_s *upperinfo;
|
|
struct lower_setalarm_s lowerinfo;
|
|
int alarmid;
|
|
|
|
DEBUGASSERT(alarminfo != NULL);
|
|
alarmid = alarminfo->id;
|
|
DEBUGASSERT(alarmid >= 0 && alarmid < CONFIG_RTC_NALARMS);
|
|
|
|
/* Is the alarm active? */
|
|
|
|
upperinfo = &upper->alarminfo[alarmid];
|
|
if (upperinfo->active)
|
|
{
|
|
/* Yes, cancel the alarm */
|
|
|
|
if (ops->cancelalarm)
|
|
{
|
|
ops->cancelalarm(upper->lower, alarmid);
|
|
}
|
|
|
|
upperinfo->active = false;
|
|
}
|
|
|
|
if (ops->setalarm)
|
|
{
|
|
pid_t pid;
|
|
|
|
/* A PID of zero means to signal the calling task */
|
|
|
|
pid = alarminfo->pid;
|
|
if (pid == 0)
|
|
{
|
|
pid = getpid();
|
|
}
|
|
|
|
/* Save the signal info to be used to notify the caller when the
|
|
* alarm expires.
|
|
*/
|
|
|
|
upperinfo->active = true;
|
|
upperinfo->pid = pid;
|
|
upperinfo->event = alarminfo->event;
|
|
|
|
/* Format the alarm info needed by the lower half driver */
|
|
|
|
lowerinfo.id = alarmid;
|
|
lowerinfo.cb = rtc_alarm_callback;
|
|
lowerinfo.priv = (FAR void *)upper;
|
|
lowerinfo.time = alarminfo->time;
|
|
|
|
/* Then set the alarm */
|
|
|
|
ret = ops->setalarm(upper->lower, &lowerinfo);
|
|
if (ret < 0)
|
|
{
|
|
upperinfo->active = false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* RTC_SET_RELATIVE sets the alarm time relative to the current time.
|
|
*
|
|
* Argument: A read-only reference to a struct rtc_setrelative_s
|
|
* containing the new relative alarm time to be set.
|
|
*/
|
|
|
|
case RTC_SET_RELATIVE:
|
|
{
|
|
FAR const struct rtc_setrelative_s *alarminfo =
|
|
(FAR const struct rtc_setrelative_s *)((uintptr_t)arg);
|
|
FAR struct rtc_alarminfo_s *upperinfo;
|
|
struct lower_setrelative_s lowerinfo;
|
|
int alarmid;
|
|
|
|
DEBUGASSERT(alarminfo != NULL);
|
|
alarmid = alarminfo->id;
|
|
DEBUGASSERT(alarmid >= 0 && alarmid < CONFIG_RTC_NALARMS);
|
|
|
|
/* Is the alarm active? */
|
|
|
|
upperinfo = &upper->alarminfo[alarmid];
|
|
if (upperinfo->active)
|
|
{
|
|
/* Yes, cancel the alarm */
|
|
|
|
if (ops->cancelalarm)
|
|
{
|
|
ops->cancelalarm(upper->lower, alarmid);
|
|
}
|
|
|
|
upperinfo->active = false;
|
|
}
|
|
|
|
if (ops->setrelative)
|
|
{
|
|
pid_t pid;
|
|
|
|
/* A PID of zero means to signal the calling task */
|
|
|
|
pid = alarminfo->pid;
|
|
if (pid == 0)
|
|
{
|
|
pid = getpid();
|
|
}
|
|
|
|
/* Save the signal info to be used to notify the caller when the
|
|
* alarm expires.
|
|
*/
|
|
|
|
upperinfo->active = true;
|
|
upperinfo->pid = pid;
|
|
upperinfo->event = alarminfo->event;
|
|
|
|
/* Format the alarm info needed by the lower half driver */
|
|
|
|
lowerinfo.id = alarmid;
|
|
lowerinfo.cb = rtc_alarm_callback;
|
|
lowerinfo.priv = (FAR void *)upper;
|
|
lowerinfo.reltime = alarminfo->reltime;
|
|
|
|
/* Then set the alarm */
|
|
|
|
ret = ops->setrelative(upper->lower, &lowerinfo);
|
|
if (ret < 0)
|
|
{
|
|
upperinfo->active = false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* RTC_CANCEL_ALARM cancel the alarm.
|
|
*
|
|
* Argument: An ALARM ID value that indicates which alarm should be
|
|
* canceled.
|
|
*/
|
|
|
|
case RTC_CANCEL_ALARM:
|
|
{
|
|
FAR struct rtc_alarminfo_s *upperinfo;
|
|
int alarmid = (int)arg;
|
|
|
|
DEBUGASSERT(alarmid >= 0 && alarmid < CONFIG_RTC_NALARMS);
|
|
|
|
upperinfo = &upper->alarminfo[alarmid];
|
|
|
|
if (ops->cancelalarm)
|
|
{
|
|
ret = ops->cancelalarm(upper->lower, alarmid);
|
|
if (ret == OK)
|
|
{
|
|
upperinfo->active = false;
|
|
nxsig_cancel_notification(&upperinfo->work);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* RTC_RD_ALARM query the alarm.
|
|
*
|
|
* Argument: A writable reference to the queried alarm.
|
|
*
|
|
*/
|
|
|
|
case RTC_RD_ALARM:
|
|
{
|
|
FAR struct rtc_rdalarm_s *alarmquery =
|
|
(FAR struct rtc_rdalarm_s *)((uintptr_t)arg);
|
|
FAR struct rtc_alarminfo_s *upperinfo;
|
|
struct lower_rdalarm_s lowerinfo;
|
|
int alarmid;
|
|
|
|
DEBUGASSERT(alarmquery != NULL);
|
|
alarmid = alarmquery->id;
|
|
DEBUGASSERT(alarmid >= 0 && alarmid < CONFIG_RTC_NALARMS);
|
|
|
|
/* Is the alarm active? */
|
|
|
|
upperinfo = &upper->alarminfo[alarmid];
|
|
alarmquery->active = upperinfo->active;
|
|
|
|
lowerinfo.id = alarmid;
|
|
lowerinfo.priv = (FAR void *)upper;
|
|
lowerinfo.time = (FAR struct rtc_time *)&alarmquery->time;
|
|
|
|
if (ops->rdalarm)
|
|
{
|
|
ret = ops->rdalarm(upper->lower, &lowerinfo);
|
|
}
|
|
}
|
|
break;
|
|
#endif /* CONFIG_RTC_ALARM */
|
|
|
|
#ifdef CONFIG_RTC_PERIODIC
|
|
/* RTC_SET_PERIODIC set a periodic wakeup.
|
|
*
|
|
* Argument: A read-only reference to a struct rtc_setperiodic_s
|
|
* containing the new wakeup period to be set.
|
|
*/
|
|
|
|
case RTC_SET_PERIODIC:
|
|
{
|
|
FAR const struct rtc_setperiodic_s *alarminfo =
|
|
(FAR const struct rtc_setperiodic_s *)((uintptr_t)arg);
|
|
FAR struct rtc_alarminfo_s *upperinfo;
|
|
struct lower_setperiodic_s lowerinfo;
|
|
int id;
|
|
|
|
DEBUGASSERT(alarminfo != NULL);
|
|
id = alarminfo->id;
|
|
DEBUGASSERT(id >= 0);
|
|
|
|
/* Is the alarm active? */
|
|
|
|
upperinfo = &upper->periodicinfo;
|
|
if (upperinfo->active)
|
|
{
|
|
/* Yes, cancel it */
|
|
|
|
if (ops->cancelperiodic)
|
|
{
|
|
ops->cancelperiodic(upper->lower, id);
|
|
}
|
|
|
|
upperinfo->active = false;
|
|
}
|
|
|
|
if (ops->setperiodic)
|
|
{
|
|
pid_t pid;
|
|
|
|
/* A PID of zero means to signal the calling task */
|
|
|
|
pid = alarminfo->pid;
|
|
if (pid == 0)
|
|
{
|
|
pid = getpid();
|
|
}
|
|
|
|
/* Save the signal info to be used to notify the caller when the
|
|
* alarm expires.
|
|
*/
|
|
|
|
upperinfo->active = true;
|
|
upperinfo->pid = pid;
|
|
upperinfo->event = alarminfo->event;
|
|
|
|
/* Format the alarm info needed by the lower half driver. */
|
|
|
|
lowerinfo.id = id;
|
|
lowerinfo.cb = rtc_periodic_callback;
|
|
lowerinfo.priv = (FAR void *)upper;
|
|
lowerinfo.period = alarminfo->period;
|
|
|
|
/* Then set the periodic wakeup. */
|
|
|
|
ret = ops->setperiodic(upper->lower, &lowerinfo);
|
|
if (ret < 0)
|
|
{
|
|
upperinfo->active = false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* RTC_CANCEL_PERIODIC cancel the periodic wakeup.
|
|
*
|
|
* Argument: An ID value that indicates which wakeup should be canceled.
|
|
*/
|
|
|
|
case RTC_CANCEL_PERIODIC:
|
|
{
|
|
FAR struct rtc_alarminfo_s *upperinfo;
|
|
int id = (int)arg;
|
|
|
|
DEBUGASSERT(id >= 0);
|
|
|
|
upperinfo = &upper->periodicinfo;
|
|
|
|
if (ops->cancelperiodic)
|
|
{
|
|
ret = ops->cancelperiodic(upper->lower, id);
|
|
if (ret == OK)
|
|
{
|
|
upperinfo->active = false;
|
|
nxsig_cancel_notification(&upperinfo->work);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#endif /* CONFIG_RTC_PERIODIC */
|
|
|
|
/* Forward any unrecognized IOCTLs to the lower half driver... they
|
|
* may represent some architecture-specific command.
|
|
*/
|
|
|
|
default:
|
|
{
|
|
ret = -ENOTTY;
|
|
#ifdef CONFIG_RTC_IOCTL
|
|
if (ops->ioctl)
|
|
{
|
|
ret = ops->ioctl(upper->lower, cmd, arg);
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
|
|
nxsem_post(&upper->exclsem);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_unlink
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
static int rtc_unlink(FAR struct inode *inode)
|
|
{
|
|
FAR struct rtc_upperhalf_s *upper;
|
|
int ret;
|
|
|
|
/* Get the reference to our internal state structure from the inode
|
|
* structure.
|
|
*/
|
|
|
|
DEBUGASSERT(inode && inode->i_private);
|
|
upper = inode->i_private;
|
|
|
|
/* Get exclusive access to the device structures */
|
|
|
|
ret = nxsem_wait(&upper->exclsem);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Indicate that the driver has been unlinked */
|
|
|
|
upper->unlinked = true;
|
|
nxsem_post(&upper->exclsem);
|
|
|
|
/* If there are no further open references to the driver, then commit
|
|
* Hara-Kiri now.
|
|
*/
|
|
|
|
if (upper->crefs == 0)
|
|
{
|
|
rtc_destroy(upper);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: rtc_initialize
|
|
*
|
|
* Description:
|
|
* Create an RTC driver by binding to the lower half RTC driver instance
|
|
* provided to this function. The resulting RTC driver will be registered
|
|
* at /dev/rtcN where N is the minor number provided to this function.
|
|
*
|
|
* Input Parameters:
|
|
* minor - The minor number of the RTC device. The N in /dev/rtcN
|
|
* lower - The lower half driver instance.
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) on success; A negated errno value on failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int rtc_initialize(int minor, FAR struct rtc_lowerhalf_s *lower)
|
|
{
|
|
FAR struct rtc_upperhalf_s *upper;
|
|
char devpath[16];
|
|
int ret;
|
|
|
|
DEBUGASSERT(lower && lower->ops && minor >= 0 && minor < 1000);
|
|
|
|
/* Allocate an upper half container structure */
|
|
|
|
upper = (FAR struct rtc_upperhalf_s *)
|
|
kmm_zalloc(sizeof(struct rtc_upperhalf_s));
|
|
|
|
if (!upper)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialize the upper half container */
|
|
|
|
upper->lower = lower; /* Contain lower half driver */
|
|
nxsem_init(&upper->exclsem, 0, 1);
|
|
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
upper->crefs = 0; /* No open references */
|
|
upper->unlinked = false; /* Driver is not unlinked */
|
|
#endif
|
|
|
|
/* Create the driver name. There is space for the a minor number up to 6
|
|
* characters
|
|
*/
|
|
|
|
snprintf(devpath, 16, "/dev/rtc%d", minor);
|
|
|
|
/* And, finally, register the new RTC driver */
|
|
|
|
ret = register_driver(devpath, &rtc_fops, 0666, upper);
|
|
if (ret < 0)
|
|
{
|
|
nxsem_destroy(&upper->exclsem);
|
|
kmm_free(upper);
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|