From 95436927da26bb57df62da5876b33a7cd920f704 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 13 Feb 2015 11:49:04 -0600 Subject: Add an RTC upper half driver. This is the driver that is documented in include/nutt/rtc.h --- nuttx/drivers/timers/rtc.c | 589 +++++++++++++++++++++++++++++++++++++++++++++ nuttx/include/nuttx/rtc.h | 65 ++--- 2 files changed, 624 insertions(+), 30 deletions(-) create mode 100644 nuttx/drivers/timers/rtc.c diff --git a/nuttx/drivers/timers/rtc.c b/nuttx/drivers/timers/rtc.c new file mode 100644 index 000000000..74f041c8c --- /dev/null +++ b/nuttx/drivers/timers/rtc.c @@ -0,0 +1,589 @@ +/**************************************************************************** + * drivers/timers/rtc.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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct rtc_upperhalf_s +{ + FAR struct rtc_lowerhalf_s *lower; /* Contained lower half driver */ + uint8_t crefs; /* Number of open references */ + bool unlinked; /* True if the driver has been unlinked */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Internal logic */ + +static void rtc_destroy(FAR struct rtc_upperhalf_s *upper); + +/* Character driver methods */ + +static int rtc_open(FAR struct file *filep); +static int rtc_close(FAR struct file *filep); +static ssize_t rtc_read(FAR struct file *filep, FAR char *, size_t); +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); +static int rtc_unlink(FAR struct inode *inode); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations rtc_fops = +{ + rtc_open, /* open */ + rtc_close, /* close */ + rtc_read, /* read */ + rtc_write, /* write */ + 0, /* seek */ + rtc_ioctl, /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + 0, /* poll */ +#endif + rtc_unlink /* unlink */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: rtc_read + ****************************************************************************/ + +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 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 */ + + kmm_free(upper); +} + +/**************************************************************************** + * Name: rtc_open + ****************************************************************************/ + +static int rtc_open(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct rtc_upperhalf_s *upper; + + /* 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; + + /* Increment the count of open references on the RTC driver */ + + upper->crefs++; + DEBUGASSERT(upper->crefs > 0); + return OK; +} + +/**************************************************************************** + * Name: rtc_close + ****************************************************************************/ + +static int rtc_close(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct rtc_upperhalf_s *upper; + + /* 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; + + /* Decrement the count of open references on the RTC driver */ + + DEBUGASSERT(upper->crefs > 0); + upper->crefs--; + + /* 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; +} + +/**************************************************************************** + * 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); + + /* 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 + * 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); + } + } + break; + + /* RTC_ALM_READ reads the alarm time (for RTCs that support alarms) + * + * Argument: A writeable reference to a struct rtc_time to receive the + * RTC's alarm time. + */ + + case RTC_ALM_READ: + { + FAR struct rtc_time *almtime = (FAR struct rtc_time *)((uintptr_t)arg); + + if (ops->almread) + { + ret = ops->almread(upper->lower, almtime); + } + } + break; + + /* RTC_ALM_SET sets the alarm time (for RTCs that support alarms). + * + * Argument: A read-only reference to a struct rtc_time containing the + * new alarm time to be set. + */ + + case RTC_ALM_SET: + { + FAR const struct rtc_time *almtime = + (FAR const struct rtc_time *)((uintptr_t)arg); + + if (ops->almset) + { + ret = ops->almset(upper->lower, almtime); + } + } + break; + + /* RTC_IRQP_READ read the frequency for periodic interrupts (for RTCs + * that support periodic interrupts) + * + * Argument: A pointer to a writeable unsigned long value in which to + * receive the frequency value. + */ + + case RTC_IRQP_READ: + { + FAR unsigned long *irqpfreq = (FAR unsigned long *)((uintptr_t)arg); + + if (ops->irqpread) + { + ret = ops->irqpread(upper->lower, irqpfreq); + } + } + break; + + /* RTC_IRQP_SET set the frequency for periodic interrupts (for RTCs that + * support periodic interrupts) + * + * Argument: An unsigned long value providing the new periodic frequency + */ + + case RTC_IRQP_SET: + { + if (ops->irqpset) + { + ret = ops->irqpset(upper->lower, arg); + } + } + break; + + /* RTC_AIE_ON enable alarm interrupts (for RTCs that support alarms) + * + * Argument: None + */ + + case RTC_AIE_ON: + { + if (ops->aie) + { + ret = ops->aie(upper->lower, true); + } + } + break; + + /* RTC_AIE_OFF disable the alarm interrupt (for RTCs that support + * alarms) + * + * Argument: None + */ + + case RTC_AIE_OFF: + { + if (ops->aie) + { + ret = ops->aie(upper->lower, false); + } + } + break; + + /* RTC_UIE_ON enable the interrupt on every clock update (for RTCs that + * support this once-per-second interrupt). + * + * Argument: None + */ + + case RTC_UIE_ON: + { + if (ops->uie) + { + ret = ops->uie(upper->lower, true); + } + } + break; + + /* RTC_UIE_OFF disable the interrupt on every clock update (for RTCs + * that support this once-per-second interrupt). + * + * Argument: None + */ + + case RTC_UIE_OFF: + { + if (ops->uie) + { + ret = ops->uie(upper->lower, false); + } + } + break; + + /* RTC_PIE_ON enable the periodic interrupt (for RTCs that support these + * periodic interrupts). + * + * Argument: None + */ + + case RTC_PIE_ON: + { + if (ops->pie) + { + ret = ops->pie(upper->lower, true); + } + } + break; + + /* RTC_PIE_OFF disable the periodic interrupt (for RTCs that support + * these periodic interrupts). + * + * Argument: None + */ + + case RTC_PIE_OFF: + { + if (ops->pie) + { + ret = ops->pie(upper->lower, false); + } + } + break; + + /* RTC_EPOCH_READ read the Epoch. + * + * Argument: A reference to a writeable unsigned low variable that will + * receive the Epoch value. + */ + + case RTC_EPOCH_READ: + { + FAR unsigned long *epoch = (FAR unsigned long *)((uintptr_t)arg); + + if (ops->rdepoch) + { + ret = ops->rdepoch(upper->lower, epoch); + } + } + break; + + /* RTC_EPOCH_SET set the Epoch + * + * Argument: An unsigned long value containing the new Epoch value to be + * set. + */ + + case RTC_EPOCH_SET: + { + if (ops->setepoch) + { + ret = ops->setepoch(upper->lower, arg); + } + } + break; + + /* RTC_WKALM_RD read the current alarm + * + * Argument: A writeable reference to struct rtc_wkalrm to receive the + * current alarm settings. + */ + + case RTC_WKALM_RD: + { + FAR struct rtc_wkalrm *wkalrm = (FAR struct rtc_wkalrm *)((uintptr_t)arg); + + if (ops->rdwkalm) + { + ret = ops->rdwkalm(upper->lower, wkalrm); + } + } + break; + + /* RTC_WKALM_SET set the alarm. + * + * Argument: A read-only reference to struct rtc_wkalrm containing the + * new alarm settings. + */ + + case RTC_WKALM_SET: + { + FAR const struct rtc_wkalrm *wkalrm = + (FAR const struct rtc_wkalrm *)((uintptr_t)arg); + + if (ops->setwkalm) + { + ret = ops->setwkalm(upper->lower, wkalrm); + } + } + break; + + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Name: rtc_unlink + ****************************************************************************/ + +static int rtc_unlink(FAR struct inode *inode) +{ + FAR struct rtc_upperhalf_s *upper; + + /* Get the reference to our internal state structure from the inode + * structure. + */ + + DEBUGASSERT(inode && inode->i_private); + upper = inode->i_private; + + /* Indicate that the driver has been unlinked */ + + upper->unlinked = true; + + /* If there are no further open references to the driver, then commit + * Hara-Kiri now. + */ + + if (upper->crefs == 0) + { + rtc_destroy(upper); + } + + return OK; +} + +/**************************************************************************** + * 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_malloc(sizeof(struct rtc_upperhalf_s)); + if (!upper) + { + return -ENOMEM; + } + + /* Initialize the upper half container */ + + upper->lower = lower; /* Contain lower half driver */ + upper->crefs = 0; /* No open references */ + upper->unlinked = false; /* Driver is not unlinked */ + + /* 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) + { + kmm_free(upper); + return ret; + } + + return OK; +} diff --git a/nuttx/include/nuttx/rtc.h b/nuttx/include/nuttx/rtc.h index 20d498c48..ff337a1d4 100644 --- a/nuttx/include/nuttx/rtc.h +++ b/nuttx/include/nuttx/rtc.h @@ -118,30 +118,30 @@ /* RTC_RD_TIME returns the current RTC time. * * Argument: A writeable reference to a struct rtc_time to receive the RTC's - * time. + * time. */ #define RTC_RD_TIME _RTCIOC(0x0001) /* RTC_SET_TIME sets the RTC's time * - * Argument: A read-only reference to a struct rtc_time containing the the + * Argument: A read-only reference to a struct rtc_time containing the * new time to be set. */ #define RTC_SET_TIME _RTCIOC(0x0002) - + /* RTC_ALM_READ reads the alarm time (for RTCs that support alarms) * * Argument: A writeable reference to a struct rtc_time to receive the RTC's * alarm time. */ - + #define RTC_ALM_READ _RTCIOC(0x0003) /* RTC_ALM_SET sets the alarm time (for RTCs that support alarms). * - * Argument: A read-only reference to a struct rtc_time containing the the + * Argument: A read-only reference to a struct rtc_time containing the * new alarm time to be set. */ @@ -175,7 +175,7 @@ * * Argument: None */ - + #define RTC_AIE_OFF _RTCIOC(0x0008) /* RTC_UIE_ON enable the interrupt on every clock update (for RTCs that @@ -193,7 +193,7 @@ */ #define RTC_UIE_OFF _RTCIOC(0x000a) - + /* RTC_PIE_ON enable the periodic interrupt (for RTCs that support these * periodic interrupts). * @@ -201,7 +201,7 @@ */ #define RTC_PIE_ON _RTCIOC(0x000b) - + /* RTC_PIE_OFF disable the periodic interrupt (for RTCs that support these * periodic interrupts). * @@ -209,7 +209,7 @@ */ #define RTC_PIE_OFF _RTCIOC(0x000c) - + /* RTC_EPOCH_READ and RTC_EPOCH_SET. * * Many RTCs encode the year in an 8-bit register which is either interpreted @@ -224,11 +224,11 @@ /* RTC_EPOCH_READ read the Epoch. * * Argument: A reference to a writeable unsigned low variable that will - * receive the the Epoch value. + * receive the Epoch value. */ #define RTC_EPOCH_READ _RTCIOC(0x000d) - + /* RTC_EPOCH_SET set the Epoch * * Argument: An unsigned long value containing the new Epoch value to be set. @@ -311,80 +311,85 @@ struct rtc_wkalrm * A NULL value should be provided for any unsupported methods. */ -struct rtc_lowerhalf_f; +struct rtc_lowerhalf_s; struct rtc_ops_s { /* rdtime() returns the current RTC time. */ - CODE int (*rdtime)(FAR struct rtc_lowerhalf_f *lower, + CODE int (*rdtime)(FAR struct rtc_lowerhalf_s *lower, FAR struct rtc_time *rtctime); /* settime sets the RTC's time */ - CODE int (*settime)(FAR struct rtc_lowerhalf_f *lower, + CODE int (*settime)(FAR struct rtc_lowerhalf_s *lower, FAR const struct rtc_time *rtctime); - /* almread reads the alarm time (for RTCs that support alarms) */ - CODE int (*almread)(FAR struct rtc_lowerhalf_f *lower, + CODE int (*almread)(FAR struct rtc_lowerhalf_s *lower, FAR struct rtc_time *almtime); /* almset sets the alarm time (for RTCs that support alarms). */ - CODE int (*almset)(FAR struct rtc_lowerhalf_f *lower, + CODE int (*almset)(FAR struct rtc_lowerhalf_s *lower, FAR const struct rtc_time *almtime); /* irqpread the frequency for periodic interrupts (for RTCs that support * periodic interrupts) */ - CODE int (*irqpread)(FAR struct rtc_lowerhalf_f *lower, + CODE int (*irqpread)(FAR struct rtc_lowerhalf_s *lower, FAR unsigned long *irqpfreq); /* irqpset set the frequency for periodic interrupts (for RTCs that * support periodic interrupts) */ - CODE int (*irqpset)(FAR struct rtc_lowerhalf_f *lower, + CODE int (*irqpset)(FAR struct rtc_lowerhalf_s *lower, unsigned long irqpfreq); /* aie enable/disable alarm interrupts (for RTCs that support alarms) */ - CODE int (*aie)(FAR struct rtc_lowerhalf_f *lower, bool enable); + CODE int (*aie)(FAR struct rtc_lowerhalf_s *lower, bool enable); /* uie enable/disable the interrupt on every clock update (for RTCs that * support this once-per-second interrupt). */ - CODE int (*uie)(FAR struct rtc_lowerhalf_f *lower, bool enable); + CODE int (*uie)(FAR struct rtc_lowerhalf_s *lower, bool enable); /* pie enable the periodic interrupt (for RTCs that support these periodic * interrupts). */ - CODE int (*pie)(FAR struct rtc_lowerhalf_f *lower, bool enable); + CODE int (*pie)(FAR struct rtc_lowerhalf_s *lower, bool enable); /* rdepoch read the Epoch. */ - CODE int (*rdepoch)(FAR struct rtc_lowerhalf_f *lower, + CODE int (*rdepoch)(FAR struct rtc_lowerhalf_s *lower, FAR unsigned long *epoch); /* setepoch set the Epoch */ - CODE int (*setepoch)(FAR struct rtc_lowerhalf_f *lower, + CODE int (*setepoch)(FAR struct rtc_lowerhalf_s *lower, unsigned long epoch); /* rdwkalm read the current alarm */ - CODE int (*rdwkalm)(FAR struct rtc_lowerhalf_f *lower, - FAR struct rtc_time *rtc_wkalrm); + CODE int (*rdwkalm)(FAR struct rtc_lowerhalf_s *lower, + FAR struct rtc_wkalrm *wkalrm); /* setwkalm set the alarm. */ - CODE int (*setwkalm)(FAR struct rtc_lowerhalf_f *lower, - FAR const struct rtc_time *rtc_wkalrm); + CODE int (*setwkalm)(FAR struct rtc_lowerhalf_s *lower, + FAR const struct rtc_wkalrm *wkalrm); + + /* The driver has been unlinked and there are no further open references + * to the driver. + */ + + CODE int (*destroy)(FAR struct rtc_lowerhalf_s *lower); }; /* When the RTC driver is instantiated, a reference to struct @@ -440,6 +445,6 @@ int rtc_initialize(int minor, FAR struct rtc_lowerhalf_s *lower); } #endif -#enif /* CONFIG_RTC_DRIVER */ -#enif /* CONFIG_RTC */ +#endif /* CONFIG_RTC_DRIVER */ +#endif /* CONFIG_RTC */ #endif /* __INCLUDE_NUTTX_RTC_H */ -- cgit v1.2.3