unsigned long flags;
ktime_t now;
int ret = HRTIMER_NORESTART;
+ int restart = ALARMTIMER_NORESTART;
spin_lock_irqsave(&base->lock, flags);
now = base->gettime();
timerqueue_del(&base->timerqueue, &alarm->node);
alarm->enabled = 0;
- /* Re-add periodic timers */
- if (alarm->period.tv64) {
- alarm->node.expires = ktime_add(expired, alarm->period);
- timerqueue_add(&base->timerqueue, &alarm->node);
- alarm->enabled = 1;
- }
+
spin_unlock_irqrestore(&base->lock, flags);
if (alarm->function)
- alarm->function(alarm);
+ restart = alarm->function(alarm, now);
spin_lock_irqsave(&base->lock, flags);
+
+ if (restart != ALARMTIMER_NORESTART) {
+ timerqueue_add(&base->timerqueue, &alarm->node);
+ alarm->enabled = 1;
+ }
}
if (next) {
* @function: callback that is run when the alarm fires
*/
void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
- void (*function)(struct alarm *))
+ enum alarmtimer_restart (*function)(struct alarm *, ktime_t))
{
timerqueue_init(&alarm->node);
alarm->period = ktime_set(0, 0);
}
+
+u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval)
+{
+ u64 overrun = 1;
+ ktime_t delta;
+
+ delta = ktime_sub(now, alarm->node.expires);
+
+ if (delta.tv64 < 0)
+ return 0;
+
+ if (unlikely(delta.tv64 >= interval.tv64)) {
+ s64 incr = ktime_to_ns(interval);
+
+ overrun = ktime_divns(delta, incr);
+
+ alarm->node.expires = ktime_add_ns(alarm->node.expires,
+ incr*overrun);
+
+ if (alarm->node.expires.tv64 > now.tv64)
+ return overrun;
+ /*
+ * This (and the ktime_add() below) is the
+ * correction for exact:
+ */
+ overrun++;
+ }
+
+ alarm->node.expires = ktime_add(alarm->node.expires, interval);
+ return overrun;
+}
+
+
+
+
/**
* clock2alarm - helper that converts from clockid to alarmtypes
* @clockid: clockid.
*
* Posix timer callback for expired alarm timers.
*/
-static void alarm_handle_timer(struct alarm *alarm)
+static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,
+ ktime_t now)
{
struct k_itimer *ptr = container_of(alarm, struct k_itimer,
it.alarmtimer);
if (posix_timer_event(ptr, 0) != 0)
ptr->it_overrun++;
+
+ /* Re-add periodic timers */
+ if (alarm->period.tv64) {
+ ptr->it_overrun += alarm_forward(alarm, now, alarm->period);
+ return ALARMTIMER_RESTART;
+ }
+ return ALARMTIMER_NORESTART;
}
/**
static void alarm_timer_get(struct k_itimer *timr,
struct itimerspec *cur_setting)
{
+ memset(cur_setting, 0, sizeof(struct itimerspec));
+
cur_setting->it_interval =
ktime_to_timespec(timr->it.alarmtimer.period);
cur_setting->it_value =
if (!rtcdev)
return -ENOTSUPP;
- /* Save old values */
- old_setting->it_interval =
- ktime_to_timespec(timr->it.alarmtimer.period);
- old_setting->it_value =
- ktime_to_timespec(timr->it.alarmtimer.node.expires);
+ if (old_setting)
+ alarm_timer_get(timr, old_setting);
/* If the timer was already set, cancel it */
alarm_cancel(&timr->it.alarmtimer);
*
* Wakes up the task that set the alarmtimer
*/
-static void alarmtimer_nsleep_wakeup(struct alarm *alarm)
+static enum alarmtimer_restart alarmtimer_nsleep_wakeup(struct alarm *alarm,
+ ktime_t now)
{
struct task_struct *task = (struct task_struct *)alarm->data;
alarm->data = NULL;
if (task)
wake_up_process(task);
+ return ALARMTIMER_NORESTART;
}
/**