summaryrefslogtreecommitdiff
path: root/nuttx/drivers
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2015-02-13 11:49:04 -0600
committerGregory Nutt <gnutt@nuttx.org>2015-02-13 11:49:04 -0600
commit95436927da26bb57df62da5876b33a7cd920f704 (patch)
tree1120381e414193b53fe7dc792256b7b2afa31fce /nuttx/drivers
parent7b6682a837dedda1aa1aad58ac9d830428296c74 (diff)
downloadpx4-nuttx-95436927da26bb57df62da5876b33a7cd920f704.tar.gz
px4-nuttx-95436927da26bb57df62da5876b33a7cd920f704.tar.bz2
px4-nuttx-95436927da26bb57df62da5876b33a7cd920f704.zip
Add an RTC upper half driver. This is the driver that is documented in include/nutt/rtc.h
Diffstat (limited to 'nuttx/drivers')
-rw-r--r--nuttx/drivers/timers/rtc.c589
1 files changed, 589 insertions, 0 deletions
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 <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 <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/rtc.h>
+
+/****************************************************************************
+ * 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;
+}