From 7f4c480f282726087b05c8659e54655e76838411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 4 May 2009 14:09:15 -0700 Subject: rtc: alarm: Add in-kernel alarm interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers can now create alarms that will use an hrtimer while the system is running and the rtc to wake up from suspend. Signed-off-by: Arve Hjønnevåg --- drivers/rtc/Kconfig | 10 +- drivers/rtc/Makefile | 3 +- drivers/rtc/alarm-dev.c | 286 +++++++++++++++++++++ drivers/rtc/alarm.c | 664 +++++++++++++++++++++++++----------------------- 4 files changed, 638 insertions(+), 325 deletions(-) create mode 100644 drivers/rtc/alarm-dev.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 7f1abd6949e8..411c83d391ff 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -113,9 +113,17 @@ config RTC_INTF_ALARM help Provides non-wakeup and rtc backed wakeup alarms based on rtc or elapsed realtime, and a non-wakeup alarm on the monotonic clock. - Also provides an ioctl to set the wall time which must be used + Also provides an interface to set the wall time which must be used for elapsed realtime to work. +config RTC_INTF_ALARM_DEV + bool "Android alarm device" + depends on RTC_INTF_ALARM + default y + help + Exports the alarm interface to user-space. + + config RTC_DRV_TEST tristate "Test driver/device" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0ea84a0612a8..9a4882ed8b47 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -9,7 +9,8 @@ obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o obj-$(CONFIG_RTC_CLASS) += rtc-core.o rtc-core-y := class.o interface.o -rtc-core-$(CONFIG_RTC_INTF_ALARM) += alarm.o +obj-$(CONFIG_RTC_INTF_ALARM) += alarm.o +obj-$(CONFIG_RTC_INTF_ALARM_DEV) += alarm-dev.o rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c new file mode 100644 index 000000000000..686e6f7ed480 --- /dev/null +++ b/drivers/rtc/alarm-dev.c @@ -0,0 +1,286 @@ +/* drivers/rtc/alarm-dev.c + * + * Copyright (C) 2007-2009 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ANDROID_ALARM_PRINT_INFO (1U << 0) +#define ANDROID_ALARM_PRINT_IO (1U << 1) +#define ANDROID_ALARM_PRINT_INT (1U << 2) + +static int debug_mask = ANDROID_ALARM_PRINT_INFO; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define pr_alarm(debug_level_mask, args...) \ + do { \ + if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \ + pr_info(args); \ + } \ + } while (0) + +#define ANDROID_ALARM_WAKEUP_MASK ( \ + ANDROID_ALARM_RTC_WAKEUP_MASK | \ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) + +/* support old usespace code */ +#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ +#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) + +static int alarm_opened; +static DEFINE_SPINLOCK(alarm_slock); +static struct wake_lock alarm_wake_lock; +static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); +static uint32_t alarm_pending; +static uint32_t alarm_enabled; +static uint32_t wait_pending; + +static struct alarm alarms[ANDROID_ALARM_TYPE_COUNT]; + +static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int rv = 0; + unsigned long flags; + struct timespec new_alarm_time; + struct timespec new_rtc_time; + struct timespec tmp_time; + enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); + uint32_t alarm_type_mask = 1U << alarm_type; + + if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) + return -EINVAL; + + if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return -EPERM; + if (file->private_data == NULL && + cmd != ANDROID_ALARM_SET_RTC) { + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_opened) { + spin_unlock_irqrestore(&alarm_slock, flags); + return -EBUSY; + } + alarm_opened = 1; + file->private_data = (void *)1; + spin_unlock_irqrestore(&alarm_slock, flags); + } + } + + switch (ANDROID_ALARM_BASE_CMD(cmd)) { + case ANDROID_ALARM_CLEAR(0): + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm %d clear\n", alarm_type); + alarm_try_to_cancel(&alarms[alarm_type]); + if (alarm_pending) { + alarm_pending &= ~alarm_type_mask; + if (!alarm_pending && !wait_pending) + wake_unlock(&alarm_wake_lock); + } + alarm_enabled &= ~alarm_type_mask; + spin_unlock_irqrestore(&alarm_slock, flags); + break; + + case ANDROID_ALARM_SET_OLD: + case ANDROID_ALARM_SET_AND_WAIT_OLD: + if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) { + rv = -EFAULT; + goto err1; + } + new_alarm_time.tv_nsec = 0; + goto from_old_alarm_set; + + case ANDROID_ALARM_SET_AND_WAIT(0): + case ANDROID_ALARM_SET(0): + if (copy_from_user(&new_alarm_time, (void __user *)arg, + sizeof(new_alarm_time))) { + rv = -EFAULT; + goto err1; + } +from_old_alarm_set: + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type, + new_alarm_time.tv_sec, new_alarm_time.tv_nsec); + alarm_enabled |= alarm_type_mask; + alarm_start_range(&alarms[alarm_type], + timespec_to_ktime(new_alarm_time), + timespec_to_ktime(new_alarm_time)); + spin_unlock_irqrestore(&alarm_slock, flags); + if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) + && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD) + break; + /* fall though */ + case ANDROID_ALARM_WAIT: + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm wait\n"); + if (!alarm_pending && wait_pending) { + wake_unlock(&alarm_wake_lock); + wait_pending = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); + if (rv) + goto err1; + spin_lock_irqsave(&alarm_slock, flags); + rv = alarm_pending; + wait_pending = 1; + alarm_pending = 0; + spin_unlock_irqrestore(&alarm_slock, flags); + break; + case ANDROID_ALARM_SET_RTC: + if (copy_from_user(&new_rtc_time, (void __user *)arg, + sizeof(new_rtc_time))) { + rv = -EFAULT; + goto err1; + } + rv = alarm_set_rtc(new_rtc_time); + spin_lock_irqsave(&alarm_slock, flags); + alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; + wake_up(&alarm_wait_queue); + spin_unlock_irqrestore(&alarm_slock, flags); + if (rv < 0) + goto err1; + break; + case ANDROID_ALARM_GET_TIME(0): + switch (alarm_type) { + case ANDROID_ALARM_RTC_WAKEUP: + case ANDROID_ALARM_RTC: + getnstimeofday(&tmp_time); + break; + case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP: + case ANDROID_ALARM_ELAPSED_REALTIME: + tmp_time = + ktime_to_timespec(alarm_get_elapsed_realtime()); + break; + case ANDROID_ALARM_TYPE_COUNT: + case ANDROID_ALARM_SYSTEMTIME: + ktime_get_ts(&tmp_time); + break; + } + if (copy_to_user((void __user *)arg, &tmp_time, + sizeof(tmp_time))) { + rv = -EFAULT; + goto err1; + } + break; + + default: + rv = -EINVAL; + goto err1; + } +err1: + return rv; +} + +static int alarm_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static int alarm_release(struct inode *inode, struct file *file) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&alarm_slock, flags); + if (file->private_data != 0) { + for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { + uint32_t alarm_type_mask = 1U << i; + if (alarm_enabled & alarm_type_mask) { + pr_alarm(INFO, "alarm_release: clear alarm, " + "pending %d\n", + !!(alarm_pending & alarm_type_mask)); + alarm_enabled &= ~alarm_type_mask; + } + spin_unlock_irqrestore(&alarm_slock, flags); + alarm_cancel(&alarms[i]); + spin_lock_irqsave(&alarm_slock, flags); + } + if (alarm_pending | wait_pending) { + if (alarm_pending) + pr_alarm(INFO, "alarm_release: clear " + "pending alarms %x\n", alarm_pending); + wake_unlock(&alarm_wake_lock); + wait_pending = 0; + alarm_pending = 0; + } + alarm_opened = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + return 0; +} + +static void alarm_triggered(struct alarm *alarm) +{ + unsigned long flags; + uint32_t alarm_type_mask = 1U << alarm->type; + + pr_alarm(INT, "alarm_triggered type %d\n", alarm->type); + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_enabled & alarm_type_mask) { + wake_lock_timeout(&alarm_wake_lock, 5 * HZ); + alarm_enabled &= ~alarm_type_mask; + alarm_pending |= alarm_type_mask; + wake_up(&alarm_wait_queue); + } + spin_unlock_irqrestore(&alarm_slock, flags); +} + +static const struct file_operations alarm_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = alarm_ioctl, + .open = alarm_open, + .release = alarm_release, +}; + +static struct miscdevice alarm_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "alarm", + .fops = &alarm_fops, +}; + +static int __init alarm_dev_init(void) +{ + int err; + int i; + + err = misc_register(&alarm_device); + if (err) + return err; + + for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) + alarm_init(&alarms[i], i, alarm_triggered); + wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); + + return 0; +} + +static void __exit alarm_dev_exit(void) +{ + misc_deregister(&alarm_device); + wake_lock_destroy(&alarm_wake_lock); +} + +module_init(alarm_dev_init); +module_exit(alarm_dev_exit); + diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c index cd660eee3b27..18492fded494 100644 --- a/drivers/rtc/alarm.c +++ b/drivers/rtc/alarm.c @@ -1,6 +1,6 @@ /* drivers/rtc/alarm.c * - * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2007-2009 Google, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -24,24 +24,24 @@ #include #include -#define ANDROID_ALARM_PRINT_ERRORS (1U << 0) +#define ANDROID_ALARM_PRINT_ERROR (1U << 0) #define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1) -#define ANDROID_ALARM_PRINT_INFO (1U << 2) -#define ANDROID_ALARM_PRINT_IO (1U << 3) -#define ANDROID_ALARM_PRINT_INT (1U << 4) -#define ANDROID_ALARM_PRINT_FLOW (1U << 5) - -#if 0 -#define ANDROID_ALARM_DPRINTF_MASK (~0) -#define ANDROID_ALARM_DPRINTF(debug_level_mask, args...) \ +#define ANDROID_ALARM_PRINT_TSET (1U << 2) +#define ANDROID_ALARM_PRINT_CALL (1U << 3) +#define ANDROID_ALARM_PRINT_SUSPEND (1U << 4) +#define ANDROID_ALARM_PRINT_INT (1U << 5) +#define ANDROID_ALARM_PRINT_FLOW (1U << 6) + +static int debug_mask = ANDROID_ALARM_PRINT_ERROR | \ + ANDROID_ALARM_PRINT_INIT_STATUS; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define pr_alarm(debug_level_mask, args...) \ do { \ - if (ANDROID_ALARM_DPRINTF_MASK & debug_level_mask) { \ - printk(args); \ + if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \ + pr_info(args); \ } \ } while (0) -#else -#define ANDROID_ALARM_DPRINTF(args...) -#endif #define ANDROID_ALARM_WAKEUP_MASK ( \ ANDROID_ALARM_RTC_WAKEUP_MASK | \ @@ -51,264 +51,307 @@ #define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ #define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) +struct alarm_queue { + struct rb_root alarms; + struct rb_node *first; + struct hrtimer timer; + ktime_t delta; + bool stopped; + ktime_t stopped_time; +}; + static struct rtc_device *alarm_rtc_dev; -static int alarm_opened; static DEFINE_SPINLOCK(alarm_slock); static DEFINE_MUTEX(alarm_setrtc_mutex); -static struct wake_lock alarm_wake_lock; static struct wake_lock alarm_rtc_wake_lock; -static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); -static uint32_t alarm_pending; -static uint32_t alarm_enabled; -static uint32_t wait_pending; static struct platform_device *alarm_platform_dev; -static struct hrtimer alarm_timer[ANDROID_ALARM_TYPE_COUNT]; -static struct timespec alarm_time[ANDROID_ALARM_TYPE_COUNT]; -static struct timespec elapsed_rtc_delta; +struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT]; +static bool suspended; -static void alarm_start_hrtimer(enum android_alarm_type alarm_type) +static void update_timer_locked(struct alarm_queue *base, bool head_removed) { - struct timespec hr_alarm_time; - if (!(alarm_enabled & (1U << alarm_type))) + struct alarm *alarm; + bool is_wakeup = base == &alarms[ANDROID_ALARM_RTC_WAKEUP] || + base == &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]; + + if (base->stopped) { + pr_alarm(FLOW, "changed alarm while setting the wall time\n"); + return; + } + + if (is_wakeup && !suspended && head_removed) + wake_unlock(&alarm_rtc_wake_lock); + + if (!base->first) return; - hr_alarm_time = alarm_time[alarm_type]; - if (alarm_type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP || - alarm_type == ANDROID_ALARM_ELAPSED_REALTIME) - set_normalized_timespec(&hr_alarm_time, - hr_alarm_time.tv_sec + elapsed_rtc_delta.tv_sec, - hr_alarm_time.tv_nsec + elapsed_rtc_delta.tv_nsec); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, - "alarm start hrtimer %d at %ld.%09ld\n", - alarm_type, hr_alarm_time.tv_sec, hr_alarm_time.tv_nsec); - hrtimer_start(&alarm_timer[alarm_type], - timespec_to_ktime(hr_alarm_time), HRTIMER_MODE_ABS); + + alarm = container_of(base->first, struct alarm, node); + + pr_alarm(FLOW, "selected alarm, type %d, func %pF at %lld\n", + alarm->type, alarm->function, ktime_to_ns(alarm->expires)); + + if (is_wakeup && suspended) { + pr_alarm(FLOW, "changed alarm while suspened\n"); + wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); + return; + } + + hrtimer_try_to_cancel(&base->timer); + base->timer.node.expires = ktime_add(base->delta, alarm->expires); + base->timer._softexpires = ktime_add(base->delta, alarm->softexpires); + hrtimer_start_expires(&base->timer, HRTIMER_MODE_ABS); } -static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static void alarm_enqueue_locked(struct alarm *alarm) { - int rv = 0; - unsigned long flags; - int i; - struct timespec new_alarm_time; - struct timespec new_rtc_time; - struct timespec tmp_time; - struct rtc_time rtc_new_rtc_time; - enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); - uint32_t alarm_type_mask = 1U << alarm_type; - - if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) - return -EINVAL; + struct alarm_queue *base = &alarms[alarm->type]; + struct rb_node **link = &base->alarms.rb_node; + struct rb_node *parent = NULL; + struct alarm *entry; + int leftmost = 1; + + pr_alarm(FLOW, "added alarm, type %d, func %pF at %lld\n", + alarm->type, alarm->function, ktime_to_ns(alarm->expires)); + + if (base->first == &alarm->node) + base->first = rb_next(&alarm->node); + if (!RB_EMPTY_NODE(&alarm->node)) { + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + } - if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { - if ((file->f_flags & O_ACCMODE) == O_RDONLY) - return -EPERM; - if (file->private_data == NULL && - cmd != ANDROID_ALARM_SET_RTC) { - spin_lock_irqsave(&alarm_slock, flags); - if (alarm_opened) { - spin_unlock_irqrestore(&alarm_slock, flags); - return -EBUSY; - } - alarm_opened = 1; - file->private_data = (void *)1; - spin_unlock_irqrestore(&alarm_slock, flags); + while (*link) { + parent = *link; + entry = rb_entry(parent, struct alarm, node); + /* + * We dont care about collisions. Nodes with + * the same expiry time stay together. + */ + if (alarm->expires.tv64 < entry->expires.tv64) { + link = &(*link)->rb_left; + } else { + link = &(*link)->rb_right; + leftmost = 0; } } + if (leftmost) { + base->first = &alarm->node; + update_timer_locked(base, false); + } - switch (ANDROID_ALARM_BASE_CMD(cmd)) { - case ANDROID_ALARM_CLEAR(0): - spin_lock_irqsave(&alarm_slock, flags); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, - "alarm %d clear\n", alarm_type); - hrtimer_try_to_cancel(&alarm_timer[alarm_type]); - if (alarm_pending) { - alarm_pending &= ~alarm_type_mask; - if (!alarm_pending && !wait_pending) - wake_unlock(&alarm_wake_lock); - } - alarm_enabled &= ~alarm_type_mask; - spin_unlock_irqrestore(&alarm_slock, flags); - break; + rb_link_node(&alarm->node, parent, link); + rb_insert_color(&alarm->node, &base->alarms); +} - case ANDROID_ALARM_SET_OLD: - case ANDROID_ALARM_SET_AND_WAIT_OLD: - if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) { - rv = -EFAULT; - goto err1; - } - new_alarm_time.tv_nsec = 0; - goto from_old_alarm_set; - - case ANDROID_ALARM_SET_AND_WAIT(0): - case ANDROID_ALARM_SET(0): - if (copy_from_user(&new_alarm_time, (void __user *)arg, - sizeof(new_alarm_time))) { - rv = -EFAULT; - goto err1; - } -from_old_alarm_set: - spin_lock_irqsave(&alarm_slock, flags); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, - "alarm %d set %ld.%09ld\n", alarm_type, - new_alarm_time.tv_sec, new_alarm_time.tv_nsec); - alarm_time[alarm_type] = new_alarm_time; - alarm_enabled |= alarm_type_mask; - alarm_start_hrtimer(alarm_type); - spin_unlock_irqrestore(&alarm_slock, flags); - if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) - && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD) - break; - /* fall though */ - case ANDROID_ALARM_WAIT: - spin_lock_irqsave(&alarm_slock, flags); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, "alarm wait\n"); - if (!alarm_pending && wait_pending) { - wake_unlock(&alarm_wake_lock); - wait_pending = 0; - } - spin_unlock_irqrestore(&alarm_slock, flags); - rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); - if (rv) - goto err1; - spin_lock_irqsave(&alarm_slock, flags); - rv = alarm_pending; - wait_pending = 1; - alarm_pending = 0; - if (rv & ANDROID_ALARM_WAKEUP_MASK) - wake_unlock(&alarm_rtc_wake_lock); - spin_unlock_irqrestore(&alarm_slock, flags); - break; - case ANDROID_ALARM_SET_RTC: - if (copy_from_user(&new_rtc_time, (void __user *)arg, - sizeof(new_rtc_time))) { - rv = -EFAULT; - goto err1; - } - rtc_time_to_tm(new_rtc_time.tv_sec, &rtc_new_rtc_time); +/** + * alarm_init - initialize an alarm + * @alarm: the alarm to be initialized + * @type: the alarm type to be used + * @function: alarm callback function + */ +void alarm_init(struct alarm *alarm, + enum android_alarm_type type, void (*function)(struct alarm *)) +{ + RB_CLEAR_NODE(&alarm->node); + alarm->type = type; + alarm->function = function; - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, - "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n", - new_rtc_time.tv_sec, new_rtc_time.tv_nsec, - rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min, - rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1, - rtc_new_rtc_time.tm_mday, - rtc_new_rtc_time.tm_year + 1900); + pr_alarm(FLOW, "created alarm, type %d, func %pF\n", type, function); +} - mutex_lock(&alarm_setrtc_mutex); - spin_lock_irqsave(&alarm_slock, flags); - for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) - hrtimer_try_to_cancel(&alarm_timer[i]); - getnstimeofday(&tmp_time); - elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta, - timespec_sub(tmp_time, new_rtc_time)); - spin_unlock_irqrestore(&alarm_slock, flags); - rv = do_settimeofday(&new_rtc_time); - spin_lock_irqsave(&alarm_slock, flags); - for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) - alarm_start_hrtimer(i); - spin_unlock_irqrestore(&alarm_slock, flags); - if (rv < 0) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_ERRORS, - "Failed to set time\n"); - mutex_unlock(&alarm_setrtc_mutex); - goto err1; - } - rv = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); - spin_lock_irqsave(&alarm_slock, flags); - alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; - wake_up(&alarm_wait_queue); - spin_unlock_irqrestore(&alarm_slock, flags); - mutex_unlock(&alarm_setrtc_mutex); - if (rv < 0) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_ERRORS, - "Failed to set RTC, time will be lost on reboot\n"); - goto err1; - } - break; - case ANDROID_ALARM_GET_TIME(0): - mutex_lock(&alarm_setrtc_mutex); - spin_lock_irqsave(&alarm_slock, flags); - if (alarm_type != ANDROID_ALARM_SYSTEMTIME) { - getnstimeofday(&tmp_time); - if (alarm_type >= ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP) - tmp_time = timespec_sub(tmp_time, - elapsed_rtc_delta); - } else - ktime_get_ts(&tmp_time); - spin_unlock_irqrestore(&alarm_slock, flags); - mutex_unlock(&alarm_setrtc_mutex); - if (copy_to_user((void __user *)arg, &tmp_time, - sizeof(tmp_time))) { - rv = -EFAULT; - goto err1; - } - break; - default: - rv = -EINVAL; - goto err1; - } -err1: - return rv; +/** + * alarm_start_range - (re)start an alarm + * @alarm: the alarm to be added + * @start: earliest expiry time + * @end: expiry time + */ +void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end) +{ + unsigned long flags; + + spin_lock_irqsave(&alarm_slock, flags); + alarm->softexpires = start; + alarm->expires = end; + alarm_enqueue_locked(alarm); + spin_unlock_irqrestore(&alarm_slock, flags); } -static int alarm_open(struct inode *inode, struct file *file) +/** + * alarm_try_to_cancel - try to deactivate an alarm + * @alarm: alarm to stop + * + * Returns: + * 0 when the alarm was not active + * 1 when the alarm was active + * -1 when the alarm may currently be excuting the callback function and + * cannot be stopped (it may also be inactive) + */ +int alarm_try_to_cancel(struct alarm *alarm) { - file->private_data = NULL; - return 0; + struct alarm_queue *base = &alarms[alarm->type]; + unsigned long flags; + bool first = false; + int ret = 0; + + spin_lock_irqsave(&alarm_slock, flags); + if (!RB_EMPTY_NODE(&alarm->node)) { + pr_alarm(FLOW, "canceled alarm, type %d, func %pF at %lld\n", + alarm->type, alarm->function, + ktime_to_ns(alarm->expires)); + ret = 1; + if (base->first == &alarm->node) { + base->first = rb_next(&alarm->node); + first = true; + } + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + if (first) + update_timer_locked(base, true); + } else + pr_alarm(FLOW, "tried to cancel alarm, type %d, func %pF\n", + alarm->type, alarm->function); + spin_unlock_irqrestore(&alarm_slock, flags); + if (!ret && hrtimer_callback_running(&base->timer)) + ret = -1; + return ret; } -static int alarm_release(struct inode *inode, struct file *file) +/** + * alarm_cancel - cancel an alarm and wait for the handler to finish. + * @alarm: the alarm to be cancelled + * + * Returns: + * 0 when the alarm was not active + * 1 when the alarm was active + */ +int alarm_cancel(struct alarm *alarm) +{ + for (;;) { + int ret = alarm_try_to_cancel(alarm); + if (ret >= 0) + return ret; + cpu_relax(); + } +} + +/** + * alarm_set_rtc - set the kernel and rtc walltime + * @new_time: timespec value containing the new time + */ +int alarm_set_rtc(struct timespec new_time) { int i; + int ret; unsigned long flags; + struct rtc_time rtc_new_rtc_time; + struct timespec tmp_time; + + rtc_time_to_tm(new_time.tv_sec, &rtc_new_rtc_time); + + pr_alarm(TSET, "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n", + new_time.tv_sec, new_time.tv_nsec, + rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min, + rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1, + rtc_new_rtc_time.tm_mday, + rtc_new_rtc_time.tm_year + 1900); + mutex_lock(&alarm_setrtc_mutex); spin_lock_irqsave(&alarm_slock, flags); - if (file->private_data != 0) { - for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { - uint32_t alarm_type_mask = 1U << i; - if (alarm_enabled & alarm_type_mask) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm_release: clear alarm, " - "pending %d\n", - !!(alarm_pending & alarm_type_mask)); - alarm_enabled &= ~alarm_type_mask; - } - spin_unlock_irqrestore(&alarm_slock, flags); - hrtimer_cancel(&alarm_timer[i]); - spin_lock_irqsave(&alarm_slock, flags); - } - if (alarm_pending | wait_pending) { - if (alarm_pending) - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm_release: clear pending alarms " - "%x\n", alarm_pending); - wake_unlock(&alarm_wake_lock); - wait_pending = 0; - alarm_pending = 0; - } - alarm_opened = 0; + wake_lock(&alarm_rtc_wake_lock); + getnstimeofday(&tmp_time); + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + hrtimer_try_to_cancel(&alarms[i].timer); + alarms[i].stopped = true; + alarms[i].stopped_time = timespec_to_ktime(tmp_time); } + alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta = + alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta = + ktime_sub(alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta, + timespec_to_ktime(timespec_sub(tmp_time, new_time))); spin_unlock_irqrestore(&alarm_slock, flags); - return 0; + ret = do_settimeofday(&new_time); + spin_lock_irqsave(&alarm_slock, flags); + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + alarms[i].stopped = false; + update_timer_locked(&alarms[i], false); + } + spin_unlock_irqrestore(&alarm_slock, flags); + if (ret < 0) { + pr_alarm(ERROR, "alarm_set_rtc: Failed to set time\n"); + goto err; + } + if (!alarm_rtc_dev) { + pr_alarm(ERROR, + "alarm_set_rtc: no RTC, time will be lost on reboot\n"); + goto err; + } + ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); + if (ret < 0) + pr_alarm(ERROR, "alarm_set_rtc: " + "Failed to set RTC, time will be lost on reboot\n"); +err: + wake_unlock(&alarm_rtc_wake_lock); + mutex_unlock(&alarm_setrtc_mutex); + return ret; } -static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer) +/** + * alarm_get_elapsed_realtime - get the elapsed real time in ktime_t format + * + * returns the time in ktime_t format + */ +ktime_t alarm_get_elapsed_realtime(void) { + ktime_t now; unsigned long flags; - enum android_alarm_type alarm_type = (timer - alarm_timer); - uint32_t alarm_type_mask = 1U << alarm_type; + struct alarm_queue *base = &alarms[ANDROID_ALARM_ELAPSED_REALTIME]; + spin_lock_irqsave(&alarm_slock, flags); + now = base->stopped ? base->stopped_time : ktime_get_real(); + now = ktime_sub(now, base->delta); + spin_unlock_irqrestore(&alarm_slock, flags); + return now; +} + +static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer) +{ + struct alarm_queue *base; + struct alarm *alarm; + unsigned long flags; + ktime_t now; - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT, - "alarm_timer_triggered type %d\n", alarm_type); spin_lock_irqsave(&alarm_slock, flags); - if (alarm_enabled & alarm_type_mask) { - wake_lock_timeout(&alarm_wake_lock, 5 * HZ); - alarm_enabled &= ~alarm_type_mask; - alarm_pending |= alarm_type_mask; - wake_up(&alarm_wait_queue); + + base = container_of(timer, struct alarm_queue, timer); + now = base->stopped ? base->stopped_time : hrtimer_cb_get_time(timer); + now = ktime_sub(now, base->delta); + + pr_alarm(INT, "alarm_timer_triggered type %d at %lld\n", + base - alarms, ktime_to_ns(now)); + + while (base->first) { + alarm = container_of(base->first, struct alarm, node); + if (alarm->softexpires.tv64 > now.tv64) { + pr_alarm(FLOW, "don't call alarm, %pF, %lld (s %lld)\n", + alarm->function, ktime_to_ns(alarm->expires), + ktime_to_ns(alarm->softexpires)); + break; + } + base->first = rb_next(&alarm->node); + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + pr_alarm(CALL, "call alarm, type %d, func %pF, %lld (s %lld)\n", + alarm->type, alarm->function, + ktime_to_ns(alarm->expires), + ktime_to_ns(alarm->softexpires)); + spin_unlock_irqrestore(&alarm_slock, flags); + alarm->function(alarm); + spin_lock_irqsave(&alarm_slock, flags); } + if (!base->first) + pr_alarm(FLOW, "no more alarms of type %d\n", base - alarms); + update_timer_locked(base, true); spin_unlock_irqrestore(&alarm_slock, flags); return HRTIMER_NORESTART; } @@ -318,11 +361,11 @@ static void alarm_triggered_func(void *p) struct rtc_device *rtc = alarm_rtc_dev; if (!(rtc->irq_data & RTC_AF)) return; - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT, "rtc alarm triggered\n"); + pr_alarm(INT, "rtc alarm triggered\n"); wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); } -int alarm_suspend(struct platform_device *pdev, pm_message_t state) +static int alarm_suspend(struct platform_device *pdev, pm_message_t state) { int err = 0; unsigned long flags; @@ -332,89 +375,85 @@ int alarm_suspend(struct platform_device *pdev, pm_message_t state) unsigned long rtc_alarm_time; struct timespec rtc_current_timespec; struct timespec rtc_delta; - struct timespec elapsed_realtime_alarm_time; + struct alarm_queue *wakeup_queue = NULL; + struct alarm_queue *tmp_queue = NULL; + + pr_alarm(SUSPEND, "alarm_suspend(%p, %d)\n", pdev, state.event); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, - "alarm_suspend(%p, %d)\n", pdev, state.event); spin_lock_irqsave(&alarm_slock, flags); - if (alarm_pending && !wake_lock_active(&alarm_wake_lock)) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm pending\n"); - err = -EBUSY; - goto err1; - } - if (alarm_enabled & ANDROID_ALARM_WAKEUP_MASK) { - spin_unlock_irqrestore(&alarm_slock, flags); - if (alarm_enabled & ANDROID_ALARM_RTC_WAKEUP_MASK) - hrtimer_cancel(&alarm_timer[ANDROID_ALARM_RTC_WAKEUP]); - if (alarm_enabled & ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) - hrtimer_cancel(&alarm_timer[ - ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]); + suspended = true; + spin_unlock_irqrestore(&alarm_slock, flags); + hrtimer_cancel(&alarms[ANDROID_ALARM_RTC_WAKEUP].timer); + hrtimer_cancel(&alarms[ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK].timer); + + tmp_queue = &alarms[ANDROID_ALARM_RTC_WAKEUP]; + if (tmp_queue->first) + wakeup_queue = tmp_queue; + tmp_queue = &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]; + if (tmp_queue->first && (!wakeup_queue || + hrtimer_get_expires(&tmp_queue->timer).tv64 < + hrtimer_get_expires(&wakeup_queue->timer).tv64)) + wakeup_queue = tmp_queue; + if (wakeup_queue) { rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); rtc_current_timespec.tv_nsec = 0; rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_timespec.tv_sec); save_time_delta(&rtc_delta, &rtc_current_timespec); - set_normalized_timespec(&elapsed_realtime_alarm_time, - alarm_time[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP] - .tv_sec + elapsed_rtc_delta.tv_sec, - alarm_time[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP] - .tv_nsec + elapsed_rtc_delta.tv_nsec); - if ((alarm_enabled & ANDROID_ALARM_RTC_WAKEUP_MASK) && - (!(alarm_enabled & - ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) || - timespec_compare(&alarm_time[ANDROID_ALARM_RTC_WAKEUP], - &elapsed_realtime_alarm_time) < 0)) - rtc_alarm_time = timespec_sub( - alarm_time[ANDROID_ALARM_RTC_WAKEUP], - rtc_delta).tv_sec; - else - rtc_alarm_time = timespec_sub( - elapsed_realtime_alarm_time, rtc_delta).tv_sec; + + rtc_alarm_time = timespec_sub(ktime_to_timespec( + hrtimer_get_expires(&wakeup_queue->timer)), + rtc_delta).tv_sec; + rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time); rtc_alarm.enabled = 1; rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + pr_alarm(SUSPEND, "rtc alarm set at %ld, now %ld, rtc delta %ld.%09ld\n", rtc_alarm_time, rtc_current_time, rtc_delta.tv_sec, rtc_delta.tv_nsec); if (rtc_current_time + 1 >= rtc_alarm_time) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm about to go off\n"); + pr_alarm(SUSPEND, "alarm about to go off\n"); memset(&rtc_alarm, 0, sizeof(rtc_alarm)); rtc_alarm.enabled = 0; rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); spin_lock_irqsave(&alarm_slock, flags); + suspended = false; wake_lock_timeout(&alarm_rtc_wake_lock, 2 * HZ); - alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP); - alarm_start_hrtimer( - ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); + update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], + false); + update_timer_locked(&alarms[ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], false); err = -EBUSY; spin_unlock_irqrestore(&alarm_slock, flags); } - } else { -err1: - spin_unlock_irqrestore(&alarm_slock, flags); } return err; } -int alarm_resume(struct platform_device *pdev) +static int alarm_resume(struct platform_device *pdev) { struct rtc_wkalrm alarm; - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, - "alarm_resume(%p)\n", pdev); - if (alarm_enabled & ANDROID_ALARM_WAKEUP_MASK) { - memset(&alarm, 0, sizeof(alarm)); - alarm.enabled = 0; - rtc_set_alarm(alarm_rtc_dev, &alarm); - alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP); - alarm_start_hrtimer(ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); - } + unsigned long flags; + + pr_alarm(SUSPEND, "alarm_resume(%p)\n", pdev); + + memset(&alarm, 0, sizeof(alarm)); + alarm.enabled = 0; + rtc_set_alarm(alarm_rtc_dev, &alarm); + + spin_lock_irqsave(&alarm_slock, flags); + suspended = false; + update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], false); + update_timer_locked(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], + false); + spin_unlock_irqrestore(&alarm_slock, flags); + return 0; } @@ -422,19 +461,6 @@ static struct rtc_task alarm_rtc_task = { .func = alarm_triggered_func }; -static struct file_operations alarm_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = alarm_ioctl, - .open = alarm_open, - .release = alarm_release, -}; - -static struct miscdevice alarm_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "alarm", - .fops = &alarm_fops, -}; - static int rtc_alarm_add_device(struct device *dev, struct class_interface *class_intf) { @@ -448,9 +474,6 @@ static int rtc_alarm_add_device(struct device *dev, goto err1; } - err = misc_register(&alarm_device); - if (err) - goto err1; alarm_platform_dev = platform_device_register_simple("alarm", -1, NULL, 0); if (IS_ERR(alarm_platform_dev)) { @@ -461,16 +484,14 @@ static int rtc_alarm_add_device(struct device *dev, if (err) goto err3; alarm_rtc_dev = rtc; + pr_alarm(INIT_STATUS, "using rtc device, %s, for alarms", rtc->name); mutex_unlock(&alarm_setrtc_mutex); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, "alarm: parent %p\n", - alarm_platform_dev->dev.power.pm_parent); return 0; err3: platform_device_unregister(alarm_platform_dev); err2: - misc_deregister(&alarm_device); err1: mutex_unlock(&alarm_setrtc_mutex); return err; @@ -480,9 +501,9 @@ static void rtc_alarm_remove_device(struct device *dev, struct class_interface *class_intf) { if (dev == &alarm_rtc_dev->dev) { + pr_alarm(INIT_STATUS, "lost rtc device for alarms"); rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task); platform_device_unregister(alarm_platform_dev); - misc_deregister(&alarm_device); alarm_rtc_dev = NULL; } } @@ -503,7 +524,7 @@ static struct platform_driver alarm_driver = { static int __init alarm_late_init(void) { unsigned long flags; - struct timespec system_time; + struct timespec tmp_time, system_time; /* this needs to run after the rtc is read at boot */ spin_lock_irqsave(&alarm_slock, flags); @@ -511,33 +532,32 @@ static int __init alarm_late_init(void) * elasped realtime to be (boot_systemtime + rtc - boot_rtc) == * (rtc - (boot_rtc - boot_systemtime)) */ - getnstimeofday(&elapsed_rtc_delta); + getnstimeofday(&tmp_time); ktime_get_ts(&system_time); - elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta, system_time); - spin_unlock_irqrestore(&alarm_slock, flags); + alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta = + alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta = + timespec_to_ktime(timespec_sub(tmp_time, system_time)); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm_late_init: rtc to elapsed realtime delta %ld.%09ld\n", - elapsed_rtc_delta.tv_sec, elapsed_rtc_delta.tv_nsec); + spin_unlock_irqrestore(&alarm_slock, flags); return 0; } -static int __init alarm_init(void) +static int __init alarm_driver_init(void) { int err; int i; for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { - hrtimer_init(&alarm_timer[i], CLOCK_REALTIME, HRTIMER_MODE_ABS); - alarm_timer[i].function = alarm_timer_triggered; + hrtimer_init(&alarms[i].timer, + CLOCK_REALTIME, HRTIMER_MODE_ABS); + alarms[i].timer.function = alarm_timer_triggered; } - hrtimer_init(&alarm_timer[ANDROID_ALARM_SYSTEMTIME], + hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - alarm_timer[ANDROID_ALARM_SYSTEMTIME].function = alarm_timer_triggered; + alarms[ANDROID_ALARM_SYSTEMTIME].timer.function = alarm_timer_triggered; err = platform_driver_register(&alarm_driver); if (err < 0) goto err1; - wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc"); rtc_alarm_interface.class = rtc_class; err = class_interface_register(&rtc_alarm_interface); @@ -548,7 +568,6 @@ static int __init alarm_init(void) err2: wake_lock_destroy(&alarm_rtc_wake_lock); - wake_lock_destroy(&alarm_wake_lock); platform_driver_unregister(&alarm_driver); err1: return err; @@ -558,11 +577,10 @@ static void __exit alarm_exit(void) { class_interface_unregister(&rtc_alarm_interface); wake_lock_destroy(&alarm_rtc_wake_lock); - wake_lock_destroy(&alarm_wake_lock); platform_driver_unregister(&alarm_driver); } late_initcall(alarm_late_init); -module_init(alarm_init); +module_init(alarm_driver_init); module_exit(alarm_exit); -- cgit v1.2.3