diff --git a/ChangeLog b/ChangeLog index d0b605d21f..49181e8a9c 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11600,3 +11600,9 @@ * arch/arm/src/armv7-m and stm32: Add support for the IAR toolchain for the limited case of the ARMv7-M architecture and the STM32 chip. From Aleksandr Vyhovanec (2016-04-02). + * arch/arm/src/stm32/stm32f40xxx_rtc.c: Add a custom RTC driver. From + Neil Hancock (2016-04-02). + * include/nuttx/timer/rtc.h and drivers/timers/rtc.c: Simplify the RTC + interface. The old interface was way to complex and was not fully + implemented anywhere (2016-04-02). + diff --git a/arch b/arch index 2eab1d6482..97812e3a3e 160000 --- a/arch +++ b/arch @@ -1 +1 @@ -Subproject commit 2eab1d6482612e2e8d7c6b226a5275694a8d682b +Subproject commit 97812e3a3e124c579a66b07a79a5595c84123d5d diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig index 7e053175ce..b3be7f38b2 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -70,6 +70,12 @@ config RTC_ALARM Enable if the RTC hardware supports setting of an alarm. A callback function will be executed when the alarm goes off. +config RTC_NALARMS + int "Number of alarms" + default 1 + ---help--- + Number of alarms supported by the hardware. + config RTC_DRIVER bool "RTC Driver Support" default n diff --git a/drivers/timers/rtc.c b/drivers/timers/rtc.c index 6c76e449b8..9f9915a8e3 100644 --- a/drivers/timers/rtc.c +++ b/drivers/timers/rtc.c @@ -53,13 +53,32 @@ * Private Types ****************************************************************************/ +#ifdef CONFIG_RTC_ALARM +struct rtc_alarminfo_s +{ + bool active; /* True: alarm is active */ + uint8_t signo; /* Signal number for alarm notification */ + pid_t pid; /* Identifies task to be notified */ + union sigval sigvalue; /* Data passed with notification */ +}; +#endif + struct rtc_upperhalf_s { FAR struct rtc_lowerhalf_s *lower; /* Contained lower half driver */ + #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 }; /**************************************************************************** @@ -72,6 +91,10 @@ struct rtc_upperhalf_s 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 + /* Character driver methods */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS @@ -119,7 +142,7 @@ static const struct file_operations rtc_fops = ****************************************************************************/ /**************************************************************************** - * Name: rtc_destory + * Name: rtc_destroy ****************************************************************************/ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS @@ -142,6 +165,43 @@ static void rtc_destroy(FAR struct rtc_upperhalf_s *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 && id >=0 && ID < CONFIG_RTC_NALARMS); + alarminfo = &upper->alarminfo[alarmid]; + + /* Do we think that the alaram 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 expriration */ + +#ifdef CONFIG_CAN_PASS_STRUCTS + (void)sigqueue(alarminfo->pid, alarminfo->signo, + alarminfo->sigvalue); +#else + (void)sigqueue(alarminfo->pid, alarminfo->signo, + alarminfo->sigvalue->sival_ptr); +#endif + } + + /* The alarm is no longer active */ + + alarminfo->active = false; +} +#endif + /**************************************************************************** * Name: rtc_open ****************************************************************************/ @@ -289,238 +349,117 @@ static int rtc_ioctl(FAR struct file *filep, int cmd, unsigned long arg) break; #ifdef CONFIG_RTC_ALARM - /* RTC_ALM_READ reads the alarm time (for RTCs that support alarms) + /* RTC_RD_ALARM reads the alarm time * - * Argument: A writeable reference to a struct rtc_time to receive the - * RTC's alarm time. + * Argument: A writeable reference to struct rtc_rdalarm_s to receive the + * current alarm settings. */ - case RTC_ALM_READ: + case RTC_RD_ALARM: { - FAR struct rtc_time *almtime = (FAR struct rtc_time *)((uintptr_t)arg); + FAR struct rtc_rdalarm_s *alarminfo = + (FAR struct rtc_rdalarm_s *)((uintptr_t)arg); + int alarmid; - if (ops->almread) + DEBUGASSERT(alarminfo != NULL); + alarmid = alarminfo->id; + DEBUGASSERT(alarmid >= 0 && alarmid < RTC_NALARMS); + + /* Is the alarm active? */ + + if (upper->alarminfo[alarmid].active) { - ret = ops->almread(upper->lower, almtime); + /* Yes, read the alarm */ + + if (ops->rdalarm) + { + ret = ops->rdalarm(upper->lower, alarminfo); + } + } + else + { + /* No.. decline the request to return the time. */ + + alarminfo->active = false; + ret = OK; } } break; - /* RTC_ALM_SET sets the alarm time (for RTCs that support alarms). + /* 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_ALM_SET: + case RTC_SET_ALARM: { - FAR const struct rtc_time *almtime = - (FAR const struct rtc_time *)((uintptr_t)arg); + 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; - if (ops->almset) + DEBUGASSERT(alarminfo != NULL); + alarmid = alarminfo->id; + DEBUGASSERT(alarminfo->id >= 0 && alarminfo->id < RTC_NALARMS); + + /* Is the alarm active? */ + + upperinfo = &upper->alarminfo[alarmid]; + if (upperinfo->active) { - ret = ops->almset(upper->lower, almtime); + /* Yes, cancel the alarm */ + + if (ops->cancelalarm) + { + ret = ops->cancelalarm(upper->lower, alarmid); + } + } + + upperinfo->active = false; + if (ops->setalarm) + { + /* Save the signal info to be used to notify the caller when the + * alarm expires. + */ + + upperinfo->signo = alarminfo->signo; + upperinfo->pid = alarminfo->pid; + upperinfo->sigvalue = alarminfo->sigvalue; + + /* 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 = true; + } } } break; -#endif /* CONFIG_RTC_ALARM */ -#ifdef CONFIG_RTC_PERIODIC - /* RTC_IRQP_READ read the frequency for periodic interrupts (for RTCs - * that support periodic interrupts) + /* RTC_WKALRM_CANCEL cancel the alarm. * - * Argument: A pointer to a writeable unsigned long value in which to - * receive the frequency value. + * Argument: An ALARM ID value that indicates which alarm should be + * canceled. */ - case RTC_IRQP_READ: + case RTC_CANCEL_ALARM: { - FAR unsigned long *irqpfreq = (FAR unsigned long *)((uintptr_t)arg); + int alarmid = (int)arg; - if (ops->irqpread) + DEBUGASSERT(alarmid >= 0 && alarmid < RTC_NALARMS); + if (ops->cancelalarm) { - 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; -#endif /* CONFIG_RTC_PERIODIC */ - -#ifdef CONFIG_RTC_ALARM - /* 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; -#endif /* CONFIG_RTC_ALARM */ - -#ifdef CONFIG_RTC_ONESEC - /* 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; -#endif /* CONFIG_RTC_ONESEC */ - -#ifdef CONFIG_RTC_PERIODIC - /* 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; -#endif /* CONFIG_RTC_PERIODIC */ - -#ifdef CONFIG_RTC_EPOCHYEAR - /* 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; -#endif /* CONFIG_RTC_EPOCHYEAR */ - -#ifdef CONFIG_RTC_ALARM - /* 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); + ret = ops->cancelalarm(upper->lower, alarmid); } } break; diff --git a/include/nuttx/timers/rtc.h b/include/nuttx/timers/rtc.h index a6e3edcc19..ce4a2e251a 100644 --- a/include/nuttx/timers/rtc.h +++ b/include/nuttx/timers/rtc.h @@ -6,7 +6,7 @@ * * With extensions, modifications by: * - * Copyright (C) 2011-2012, 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2011-2012, 2015-2016 Gregory Nutt. All rights reserved. * Author: Gregroy Nutt * * Redistribution and use in source and binary forms, with or without @@ -107,6 +107,15 @@ # endif #endif +#ifdef CONFIG_RTC_ALARM +# ifdef CONFIG_DISABLE_SIGNALS +# error RTC driver alarm support depends on signals +# endif +# ifndef CONFIG_RTC_NALARMS +# define CONFIG_RTC_NALARMS 1 +# endif +#endif + /* The remainder of the contain of this header file is only valid if the * RTC upper half driver is built. */ @@ -134,132 +143,28 @@ #define RTC_SET_TIME _RTCIOC(0x0002) -/* RTC_ALM_READ reads the alarm time (for RTCs that support alarms) +/* RTC_RD_ALARM reads the alarm time (for RTCs that support alarms) * - * Argument: A writeable reference to a struct rtc_time to receive the RTC's + * Argument: A writeable reference to a struct rtc_rdalarm_s to receive the RTC's * alarm time. */ -#define RTC_ALM_READ _RTCIOC(0x0003) +#define RTC_RD_ALARM _RTCIOC(0x0003) -/* RTC_ALM_SET sets the alarm time (for RTCs that support alarms). +/* RTC_SET_ALARM sets the alarm time (for RTCs that support alarms). * - * Argument: A read-only reference to a struct rtc_time containing the + * Argument: A read-only reference to a struct rtc_setalarm_s containing the * new alarm time to be set. */ -#define RTC_ALM_SET _RTCIOC(0x0004) +#define RTC_SET_ALARM _RTCIOC(0x0004) -/* RTC_IRQP_READ read the frequency for periodic interrupts (for RTCs that - * support periodic interrupts) +/* RTC_WKALRM_CANCEL cancel the alarm. * - * Argument: A pointer to a writeable unsigned long value in which to - * receive the frequency value. + * Argument: An ALARM ID value that indicates which alarm should be canceled. */ -#define RTC_IRQP_READ _RTCIOC(0x0005) - -/* 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 - */ - -#define RTC_IRQP_SET _RTCIOC(0x0006) - -/* RTC_AIE_ON enable alarm interrupts (for RTCs that support alarms) - * - * Argument: None - */ - -#define RTC_AIE_ON _RTCIOC(0x0007) - -/* RTC_AIE_OFF disable the alarm interrupt (for RTCs that support alarms) - * - * Argument: None - */ - -#define RTC_AIE_OFF _RTCIOC(0x0008) - -/* RTC_UIE_ON enable the interrupt on every clock update (for RTCs that - * support this once-per-second interrupt). - * - * Argument: None - */ - -#define RTC_UIE_ON _RTCIOC(0x0009) - -/* RTC_UIE_OFF disable the interrupt on every clock update (for RTCs that - * support this once-per-second interrupt). - * - * Argument: None - */ - -#define RTC_UIE_OFF _RTCIOC(0x000a) - -/* RTC_PIE_ON enable the periodic interrupt (for RTCs that support these - * periodic interrupts). - * - * Argument: None - */ - -#define RTC_PIE_ON _RTCIOC(0x000b) - -/* RTC_PIE_OFF disable the periodic interrupt (for RTCs that support these - * periodic interrupts). - * - * Argument: None - */ - -#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 - * as an 8-bit binary number or as a BCD number. In both cases, the number is - * interpreted relative to this RTC's Epoch. The RTC's Epoch is initialized to - * 1900 on most systems but on Alpha and MIPS it might also be initialized to - * 1952, 1980, or 2000, depending on the value of an RTC register for the year. - * With some RTCs, these operations can be used to read or to set the RTC's - * Epoch, respectively. - */ - -/* RTC_EPOCH_READ read the Epoch. - * - * Argument: A reference to a writeable unsigned low variable that will - * 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. - */ - -#define RTC_EPOCH_SET _RTCIOC(0x000e) - -/* RTC_WKALM_RD and RTC_WKALM_SET. - * - * Some RTCs support a more powerful alarm interface, using these ioctls to - * read or write the RTC's alarm time (respectively) with the rtc_wkalrm. - */ - -/* RTC_WKALM_RD read the current alarm - * - * Argument: A writeable reference to struct rtc_wkalrm to receive the - * current alarm settings. - */ - -#define RTC_WKALM_RD _RTCIOC(0x000f) - -/* RTC_WKALM_SET set the alarm. - * - * Argument: A read-only reference to struct rtc_wkalrm containing the - * new alarm settings. - */ - -#define RTC_WKALM_SET _RTCIOC(0x0010) +#define RTC_CANCEL_ALARM _RTCIOC(0x005) /* Architecture-specific RTC IOCTLS should begin at RTC_USER_IOCBASE. For * example: @@ -269,7 +174,7 @@ * etc. */ -#define RTC_USER_IOCBASE 0x0011 +#define RTC_USER_IOCBASE 0x0006 /**************************************************************************** * Public Types @@ -299,21 +204,42 @@ struct rtc_time }; #ifdef CONFIG_RTC_ALARM -/* Structure used with the RTC_WKALM_RD and RTC_WKALM_SET IOCTL commands. - * - * The enabled flag is used to enable or disable the alarm interrupt, or to - * read its current status; when using these calls, RTC_AIE_ON and - * RTC_AIE_OFF are not used. The pending flag is used by RTC_WKALM_RD to - * report a pending interrupt . The time field is as used with RTC_ALM_READ - * and RTC_ALM_SET except that the tm_mday, tm_mon, and tm_year fields are - * also valid. +/* Structure used with the RTC_RD_ALARM IOCTL command and with + * rdalarm() method. */ -struct rtc_wkalrm +struct rtc_rdalarm_s { - unsigned char enabled; - unsigned char pending; - struct rtc_time time; + uint8_t id; /* Indicates the alarm being queried */ + bool active; /* Alarm actively timing or disabled */ + struct rtc_time time; /* Current RTC time (if enabled) */ +}; + +/* Structure used with the RTC_SETALARM IOCTL command. */ + +struct rtc_setalarm_s +{ + uint8_t id; /* Indicates the alarm to be set */ + uint8_t signo; /* Signal number for alarm notification */ + pid_t pid; /* Identifies task to be notified */ + union sigval sigvalue; /* Data passed with notification */ + struct rtc_time time; /* Alarm time */ +}; + +/* Callback type used by the RTC harware to notify the RTC driver when the + * alarm expires. + */ + +typedef CODE void (*rtc_alarm_callback_t)(FAR void *priv, int alarmid); + +/* Structure used with the setalarm method */ + +struct lower_setalarm_s +{ + uint8_t id; /* Indicates the alarm to be set */ + rtc_alarm_callback_t cb; /* Callback when the alarm expires */ + FAR void *priv; /* Private argurment to accompany callback */ + struct rtc_time time; /* Alarm time */ }; #endif @@ -343,77 +269,19 @@ struct rtc_ops_s FAR const struct rtc_time *rtctime); #ifdef CONFIG_RTC_ALARM - /* almread reads the alarm time (for RTCs that support alarms) */ + /* rdalarm reads the current alarm time */ - CODE int (*almread)(FAR struct rtc_lowerhalf_s *lower, - FAR struct rtc_time *almtime); + CODE int (*rdalarm)(FAR struct rtc_lowerhalf_s *lower, + FAR struct rtc_rdalarm_s *alarminfo); - /* almset sets the alarm time (for RTCs that support alarms). */ + /* setalarm sets up a new alarm. */ - CODE int (*almset)(FAR struct rtc_lowerhalf_s *lower, - FAR const struct rtc_time *almtime); -#endif + CODE int (*setalarm)(FAR struct rtc_lowerhalf_s *lower, + FAR const struct lower_setalarm_s *alarminfo); -#ifdef CONFIG_RTC_PERIODIC - /* irqpread the frequency for periodic interrupts (for RTCs that support - * periodic interrupts) - */ + /* cancelalarm cancels the alarm. */ - 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_s *lower, - unsigned long irqpfreq); -#endif - -#ifdef CONFIG_RTC_ALARM - /* aie enable/disable alarm interrupts (for RTCs that support alarms) */ - - CODE int (*aie)(FAR struct rtc_lowerhalf_s *lower, bool enable); -#endif - -#ifdef CONFIG_RTC_ONESEC - /* 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_s *lower, bool enable); -#endif - -#ifdef CONFIG_RTC_PERIODIC - /* pie enable the periodic interrupt (for RTCs that support these periodic - * interrupts). - */ - - CODE int (*pie)(FAR struct rtc_lowerhalf_s *lower, bool enable); -#endif - -#ifdef CONFIG_RTC_EPOCHYEAR - /* rdepoch read the Epoch. */ - - CODE int (*rdepoch)(FAR struct rtc_lowerhalf_s *lower, - FAR unsigned long *epoch); - - /* setepoch set the Epoch */ - - CODE int (*setepoch)(FAR struct rtc_lowerhalf_s *lower, - unsigned long epoch); -#endif - -#ifdef CONFIG_RTC_ALARM - /* rdwkalm read the current alarm */ - - 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_s *lower, - FAR const struct rtc_wkalrm *wkalrm); + CODE int (*cancelalarm)(FAR struct rtc_lowerhalf_s *lower, int alarmid); #endif #ifdef CONFIG_RTC_IOCTL @@ -478,7 +346,9 @@ extern "C" * ****************************************************************************/ +#ifdef __KERNEL__ int rtc_initialize(int minor, FAR struct rtc_lowerhalf_s *lower); +#endif #undef EXTERN #if defined(__cplusplus)