一、前言

严格来讲Alarm Timer也算POSIX Timer一部分,包含两种类型CLOCK_REALTIME_ALARM和CLOCK_BOOTTIME_ALARM。分别是在CLOCK_REALTIME和CLOCK_BOOTTIME后面加上_ALARM。Alarm Timer之外的POSIX Timer在内核进入cpuidle或者suspend之后,都会因为省电关闭ClockEvent设备而停止计时。而Alarm Timer恰恰借助RTC设备的长供电且具备唤醒功能,在系统进入suspend过程中,将最近一次超时expires写入RTC设备,超时后会将系统从suspend状态唤醒,执行timer超市函数。

这样在程序执行过程中,就不需要一直持有wakelock。

二、背景介绍

Alarm Timer可以说工作在两种状态下,一种是和其他Timer一样的基于hrtimer;另一种是在系统进入suspend后基于RTC设备。

RTC设备在系统外独立供电,RTC具备Alarm功能。在Alarm触发后,通过中断唤醒suspend的系统。

在device_initcall-->alarmtimer_init时,注册一个alarmtimer的platform_device,驱动为alarmtimer_driver。将alarmtimer_suspend作为钩子函数插入系统suspend流程,这样就将suspend和Alarm Timer功能挂钩了。

三、重要数据结构

struct alarm_base作为AlarmTimer时钟类型结构体,包含ALARM_REALTIME和ALARM_BOOTTIME两种。

static struct alarm_base {
  spinlock_t lock;---------------------------------互斥访问锁
  struct timerqueue_head timerqueue;-------AlarmTimer自己维护了expires红黑树。
  struct hrtimer timer;--------------------------将其加入到hrtimer_bases对应的红黑树中。
  ktime_t (*gettime)(void);--------------------获取对应类型时钟的时间函数
  clockid_t base_clockid;------------------------时钟类型ID,CLOCK_REALTIME和CLOCK_BOOTTIME
} alarm_bases[ALARM_NUMTYPE];

CLOCK_REALTIME_ALARM和CLOCK_REALTIME、CLOCK_BOOTTIME_ALARM和CLOCK_BOOTTIME都是用同样的base_clockid,但是_ALARM维护的alarm_bases[ALARM_NUMTYPE].timerqueue将他们与其他hrtimer区分开了。

struct k_clock alarm_clock作为两种类型共用的时钟/Timer函数:

    struct k_clock alarm_clock = {
.clock_getres = alarm_clock_getres,
.clock_get = alarm_clock_get,
.timer_create = alarm_timer_create,
.timer_set = alarm_timer_set,
.timer_del = alarm_timer_del,
.timer_get = alarm_timer_get,
.nsleep = alarm_timer_nsleep,
};

static struct rtc_timer rtctimer;--------------------RTC Timer

static struct rtc_device *rtcdev;-------------------RTC设备对应的结构体

struct rtc_time是RTC设备表示的时间格式:

 struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};

struct ktime_t是内核时间格式。

这两种时间格式的转换,rtc_time到ktime_t通过rtc_tm_to_ktime;ktime_t到rtc_time通过rtc_ktime_to_tm。

四、AlarmTimer正常工作状态下运行

static int __init alarmtimer_init(void)
{
struct platform_device *pdev;
int error = ;
int i;
struct k_clock alarm_clock = {--------------------------------------AlarmTimer的Clock/Timer/Sleep函数。
.clock_getres = alarm_clock_getres,
.clock_get = alarm_clock_get,
.timer_create = alarm_timer_create,
.timer_set = alarm_timer_set,
.timer_del = alarm_timer_del,
.timer_get = alarm_timer_get,
.nsleep = alarm_timer_nsleep,
}; alarmtimer_rtc_timer_init();---------------------------------------初始化一个struct rtc_timer,将其加入struct rtc_device的timerqueue红黑树里面。 posix_timers_register_clock(CLOCK_REALTIME_ALARM, &alarm_clock);---填充posix_clocks[MAX_CLOCKS]的_ALARM部分
posix_timers_register_clock(CLOCK_BOOTTIME_ALARM, &alarm_clock); /* Initialize alarm bases */
alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME;---------初始化alarm_bases[ALARM_NUMTYPE]
alarm_bases[ALARM_REALTIME].gettime = &ktime_get_real;
alarm_bases[ALARM_BOOTTIME].base_clockid = CLOCK_BOOTTIME;
alarm_bases[ALARM_BOOTTIME].gettime = &ktime_get_boottime;
for (i = ; i < ALARM_NUMTYPE; i++) {
timerqueue_init_head(&alarm_bases[i].timerqueue);
spin_lock_init(&alarm_bases[i].lock);
hrtimer_init(&alarm_bases[i].timer,
alarm_bases[i].base_clockid,
HRTIMER_MODE_ABS);
alarm_bases[i].timer.function = alarmtimer_fired;
} error = alarmtimer_rtc_interface_setup();-------------------------获取系统的struct rtc_device设备,给rtcdev。
if (error)
return error; error = platform_driver_register(&alarmtimer_driver);-------------注册alarmtimer_driver,主要就是suspend钩子函数。
if (error)
goto out_if; pdev = platform_device_register_simple("alarmtimer", -, NULL, );-注册alarmtimer设备。
if (IS_ERR(pdev)) {
error = PTR_ERR(pdev);
goto out_drv;
}
ws = wakeup_source_register("alarmtimer");------------------------返回注册的Wakeup Source,alarmtimer_suspend使用。
return ; out_drv:
platform_driver_unregister(&alarmtimer_driver);
out_if:
alarmtimer_rtc_interface_remove();
return error;
}

AlarmTimer是struct k_itimer中的alarmtimer成员:

struct k_itimer {
...
union {
...
struct {
struct alarm alarmtimer;
ktime_t interval;
} alarm;
struct rcu_head rcu;
} it;
};

struct alarm如下:

struct alarm {
struct timerqueue_node node;----------------------------------------红黑树节点
enum alarmtimer_restart (*function)(struct alarm *, ktime_t now);---Alarm超时函数
enum alarmtimer_type type;------------------------------------------ALARM_REALTIME、ALARM_BOOTTIME
int state;--------------------------------------------------#define ALARMTIMER_STATE_INACTIVE 0x00、#define ALARMTIMER_STATE_ENQUEUED 0x01、#define ALARMTIMER_STATE_CALLBACK 0x02
void *data;
};

在了解了AlarmTimer初始化和基本数据结构之后,和其他POSIX Timer一样,重点在struct k_clock提供的函数。

1. AlarmTimer定时器

alarm_timer_create主要填充当前timer的struct alarm结构体:

static int alarm_timer_create(struct k_itimer *new_timer)
{
enum alarmtimer_type type;
struct alarm_base *base; if (!alarmtimer_get_rtcdev())--------------------------是否有可用RTC设备
return -ENOTSUPP;
if (!capable(CAP_WAKE_ALARM))--------------------------当前进程是否具有CAP_WAKE_ALARM能力,需要root权限。
return -EPERM; type = clock2alarm(new_timer->it_clock);---------------从Timer类型到alarm_base进行转换。
base = &alarm_bases[type];
alarm_init(&new_timer->it.alarm.alarmtimer, type, alarm_handle_timer);-----初始化当前timer的struct alarm结构
return ;
}

在填充好结构体之后,设置expires,并且启动一个hrtimer。

static int alarm_timer_set(struct k_itimer *timr, int flags,
struct itimerspec *new_setting,
struct itimerspec *old_setting)
{
ktime_t exp; if (!rtcdev)
return -ENOTSUPP;
if (flags & ~TIMER_ABSTIME)
return -EINVAL; if (old_setting)
alarm_timer_get(timr, old_setting); /* If the timer was already set, cancel it */
if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < )
return TIMER_RETRY; /* start the timer */
timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval);---------间隔定时
exp = timespec_to_ktime(new_setting->it_value);--------------------------------首次超时值
/* Convert (if necessary) to absolute time */
if (flags != TIMER_ABSTIME) {--------------------------------------------------如果不是绝对时间,需要转换成绝对时间
ktime_t now; now = alarm_bases[timr->it.alarm.alarmtimer.type].gettime();
exp = ktime_add(now, exp);
} alarm_start(&timr->it.alarm.alarmtimer, exp);--------------------------------启动AlarmTimer,插入当前alarm_base的timerqueue,如有需要设置一个hrtimer。
return ;
}

alarm_timer_del是alarm_timer_set的逆操作,用于删除一个AlarmTimer。将其从alarm_base的timerqueue移除,如果已经被插入hrtimer,则取消。

static int alarm_timer_del(struct k_itimer *timr)
{
if (!rtcdev)
return -ENOTSUPP; if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < )
return TIMER_RETRY; return ;
}

alarm_timer_get获取timr的超时itimerspec。

/**
* alarm_timer_get - posix timer_get interface
* @new_timer: k_itimer pointer
* @cur_setting: itimerspec data to fill
*
* Copies out the current itimerspec data
*/
static void alarm_timer_get(struct k_itimer *timr,
struct itimerspec *cur_setting)
{
ktime_t relative_expiry_time =
alarm_expires_remaining(&(timr->it.alarm.alarmtimer)); if (ktime_to_ns(relative_expiry_time) > ) {
cur_setting->it_value = ktime_to_timespec(relative_expiry_time);
} else {
cur_setting->it_value.tv_sec = ;
cur_setting->it_value.tv_nsec = ;
} cur_setting->it_interval = ktime_to_timespec(timr->it.alarm.interval);
}

新增的struct alarm的note作为一个节点插入到alarm_base的timerqueue中。

struct timerqueue_node {
struct rb_node node;
ktime_t expires;---------------红黑树按照expires大小排列
};

节点的插入、删除的典型路径是,

alarm_timer_set-->alarm_start-->alarmtimer_enqueue-->timerqueue_add

alarm_timer_del-->alarm_try_to_cancel-->alarmtimer_remove-->timerqueue_del

操作alarm_base的timerqueue有如下几个地方,这样保证无论是删除、插入、超时都是最新的alarm_base->timerqueue插入到hrtimer中。

alarmtimer_init---------初始化红黑树头
alarmtimer_enqueue------将timer加入timerqueue。如果当前timer是最新timer,则创建hrtimer
alarmtimer_remove-------将timer移除timerqueue。如果当前timer是最新timer,删除hrtimer。取最近timer重新设置hrtimer。
alarmtimer_fired--------将超时timer移除出timerqueue,如果是restart类型,则重新插入。如果timerqueue不为空,则设置下一次expires,返回HRTIMER_RESTART。
static int __init alarmtimer_init(void)
{
...
for (i = ; i < ALARM_NUMTYPE; i++) {
timerqueue_init_head(&alarm_bases[i].timerqueue);
spin_lock_init(&alarm_bases[i].lock);
hrtimer_init(&alarm_bases[i].timer,------------------------------在alarmtimer_init中初始化hrtimer,ALARM_REALTIME和ALARM_BOOTTIME共两个hrtimer。这两个timer的主要区别在于不同的base_clockid。
alarm_bases[i].base_clockid,
HRTIMER_MODE_ABS);
alarm_bases[i].timer.function = alarmtimer_fired;
}
...
} static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm)
{
... if (&alarm->node == timerqueue_getnext(&base->timerqueue)) {
hrtimer_try_to_cancel(&base->timer);-----------------------------努力取消alarm_base->timer,0:timer不在hrtimer_clock_base->active里面,1:将timer从hrtimer_clock_base->active里面移除,-1:timer的callback函数正在被执行,不能被停止。
hrtimer_start(&base->timer, alarm->node.expires,-----------------将最近expires赋给alarm_base->timer,注意这里不同base的区别。alarm_base的timerqueue红黑树和hrtimer_clock_base的active红黑树区别。
HRTIMER_MODE_ABS);
}
} static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm)
{
... if (next == &alarm->node) {
hrtimer_try_to_cancel(&base->timer);----------------------------取消alarm_base->timer
next = timerqueue_getnext(&base->timerqueue);-------------------取最近expires
if (!next)
return;
hrtimer_start(&base->timer, next->expires, HRTIMER_MODE_ABS);---更新alarm_base->timer的expires
}
} static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
{
... if (next) {
hrtimer_set_expires(&base->timer, next->expires);----------在处理完expired timer之后,设置最近一次expires的alarm_base->timer。
ret = HRTIMER_RESTART;
}
...
} static int alarm_clock_getres(const clockid_t which_clock, struct timespec *tp)
{
... return hrtimer_get_res(baseid, tp);---------------------------获取base_clockid的精度
}

AlarmTimer中关于timerqueue的操作,一个timerqueue节点共用一个hrtimer。

timerqueue_init_head(&alarm_bases[i].timerqueue);-------初始化alarm_base->timerqueue,红黑树根节点
timerqueue_init(&alarm->node);--------------------------初始化一个timerqueue节点
next = timerqueue_getnext(&base->timerqueue);-----------获取alarm_base->timerqueue的最左侧节点
timerqueue_add(&base->timerqueue, &alarm->node);--------将节点插入alarm_base->timerqueue
timerqueue_del(&base->timerqueue, &alarm->node);--------从alarm_base->timerqueue中删除

五、AlarmTimer在进入Suspend时、Suspend中、Resume时状态分析

AlarmTimer被当做一个platform_device,主要是为了提供suspend钩子函数。在系统执行suspend流程的时候,针对AlarmTimer进行转移到RTC。

static int __init alarmtimer_init(void)
{
...
error = platform_driver_register(&alarmtimer_driver);
if (error)
goto out_if; pdev = platform_device_register_simple("alarmtimer", -, NULL, );
...
} /* Suspend hook structures */
static const struct dev_pm_ops alarmtimer_pm_ops = {
.suspend = alarmtimer_suspend,
}; static struct platform_driver alarmtimer_driver = {
.driver = {
.name = "alarmtimer",
.pm = &alarmtimer_pm_ops,
}
};

alarmtimer_suspend函数的核心功能是在进入睡眠之前,遍历alarm_bases->timerqueue,取最近一次timer的expires;将此expires写入RTC定时器,RTC超时后会唤醒系统。

static int alarmtimer_suspend(struct device *dev)
{
...
rtc = alarmtimer_get_rtcdev();-------------------------------------获取RTC设备
/* If we have no rtcdev, just return */
if (!rtc)
return ; /* Find the soonest timer to expire*/
for (i = ; i < ALARM_NUMTYPE; i++) {------------------------------遍历ALARM_REALTIME和ALARM_BOOTTIME两个alarm_base,取各自最近expires
struct alarm_base *base = &alarm_bases[i];
struct timerqueue_node *next;
ktime_t delta; spin_lock_irqsave(&base->lock, flags);
next = timerqueue_getnext(&base->timerqueue);
spin_unlock_irqrestore(&base->lock, flags);
if (!next)
continue;
delta = ktime_sub(next->expires, base->gettime());
if (!min.tv64 || (delta.tv64 < min.tv64))---------------------比较两次expires,取最小者
min = delta;
}
if (min.tv64 == )
return ; if (ktime_to_ns(min) < * NSEC_PER_SEC) {-----------------------如果expires小于2秒,保持系统唤醒2秒,并中断suspend流程。
__pm_wakeup_event(ws, * MSEC_PER_SEC);
return -EBUSY;
} /* Setup an rtc timer to fire that far in the future */
rtc_timer_cancel(rtc, &rtctimer);-------------------------------取消当前rtctimer
rtc_read_time(rtc, &tm);
now = rtc_tm_to_ktime(tm);
now = ktime_add(now, min);--------------------------------------获取RTC时间,将rtc_timer转换成ktimer_t,将RTC时间加上AlarmTimer超时。 /* Set alarm, if in the past reject suspend briefly to handle */
ret = rtc_timer_start(rtc, &rtctimer, now, ktime_set(, ));---设置rtctimer
if (ret < )
__pm_wakeup_event(ws, * MSEC_PER_SEC);
return ret;
}

在此之后系统继续suspend流程,然后RTC到期进行resume唤醒。

六、RTC相关

AlarmTimer区别与其他的POSIX Timer就在于其和RTC设备的关联。

AlarmTimer和RTC的关联有几处:

1. alarmtimer_init中初始化rtctimer,通过rtc_class接口获得rtcdev:alarmtimer_rtc_timer_init、alarmtimer_rtc_interface_setup

2. 使用alarmtimer_get_rtcdev获取当前rtc_device

3. RTC定时器相关操作:rtc_timer_init、rtc_timer_cancel、rtc_read_time、rtc_tm_to_ktime、rtc_timer_start、

alarmtimer_rtc_interface_setup通过rtc_class接口获取RTC设备:

static int alarmtimer_rtc_add_device(struct device *dev,--------------根据rtc_class全局变量,找到其下面的RTC设备。如果有多个,取最后一个。
struct class_interface *class_intf)
{
unsigned long flags;
struct rtc_device *rtc = to_rtc_device(dev);----------------------根据struct device找到对应的rtc设备。 if (rtcdev)
return -EBUSY; if (!rtc->ops->set_alarm)
return -;
if (!device_may_wakeup(rtc->dev.parent))
return -; spin_lock_irqsave(&rtcdev_lock, flags);
if (!rtcdev) {
rtcdev = rtc;------------------------------------------------局部全局变量rtcdev
/* hold a reference so it doesn't go away */
get_device(dev);
}
spin_unlock_irqrestore(&rtcdev_lock, flags);
return ;
} static struct class_interface alarmtimer_rtc_interface = {
.add_dev = &alarmtimer_rtc_add_device,---------------------class_interface_register中会调用此函数
}; static int alarmtimer_rtc_interface_setup(void)
{
alarmtimer_rtc_interface.class = rtc_class;
return class_interface_register(&alarmtimer_rtc_interface);
} static void alarmtimer_rtc_interface_remove(void)
{
class_interface_unregister(&alarmtimer_rtc_interface);
}

alarmtimer_rtc_timer_init初始化一个rtctimer:

static inline void alarmtimer_rtc_timer_init(void)
{
rtc_timer_init(&rtctimer, NULL, NULL);
}

RTC设备对外接口主要在drivers/rtc/interface.c中,其中RTC Timer相关包括:rtc_timer_init、rtc_timer_start、rtc_timer_cancel

/* rtc_timer_init - Initializes an rtc_timer
* @timer: timer to be intiialized
* @f: function pointer to be called when timer fires
* @data: private data passed to function pointer
*
* Kernel interface to initializing an rtc_timer.
*/
void rtc_timer_init(struct rtc_timer *timer, void (*f)(void* p), void* data)----------初始化timer的node、task.func、task.private_date,node将会被插入到RTC设备的timerqueue中。
{
timerqueue_init(&timer->node);
timer->enabled = 0;
timer->task.func = f;
timer->task.private_data = data;
} /* rtc_timer_start - Sets an rtc_timer to fire in the future
* @ rtc: rtc device to be used
* @ timer: timer being set
* @ expires: time at which to expire the timer
* @ period: period that the timer will recur
*
* Kernel interface to set an rtc_timer
*/
int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer,-----------------将timer插入rtc->timerqueue,如有需要选择最近的timer设置Alarm。
ktime_t expires, ktime_t period)
{
int ret = 0;
mutex_lock(&rtc->ops_lock);
if (timer->enabled)-------------------------------------------------------------是否已经被使能?已经被使能则移除
rtc_timer_remove(rtc, timer); timer->node.expires = expires;
timer->period = period;---------------------------------------------------------expires是超时点,period是容许的宽限 ret = rtc_timer_enqueue(rtc, timer);--------------------------------------------将timer->node插入rtc->timerqueue mutex_unlock(&rtc->ops_lock);
return ret;
} /* rtc_timer_cancel - Stops an rtc_timer
* @ rtc: rtc device to be used
* @ timer: timer being set
*
* Kernel interface to cancel an rtc_timer
*/
int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer* timer)------------rtc_timer_start的反操作
{
int ret = 0;
mutex_lock(&rtc->ops_lock);
if (timer->enabled)
rtc_timer_remove(rtc, timer);
mutex_unlock(&rtc->ops_lock);
return ret;
}

rtc_timer_start和rtc_timer_cancel通过rtc_timer_enqueue和rtc_timer_remove来主动插入/删除timer节点,操作节点的还有一个地方是超时函数rtc_timer_do_work。这三个函数能保证RTC设备的timer及时更新。

rtc->timerqueue也是红黑树,基本操作也是timerqueue_add、timerqueue_del、timerqueue_getnext。

针对RTC设备的操作都是通过rtc_device->ops来执行。

读取RTC时间:

static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;
if (!rtc->ops)
err = -ENODEV;
else if (!rtc->ops->read_time)
err = -EINVAL;
else {
memset(tm, , sizeof(struct rtc_time));
err = rtc->ops->read_time(rtc->dev.parent, tm);
}
return err;
} int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err; err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err; err = __rtc_read_time(rtc, tm);
mutex_unlock(&rtc->ops_lock);
return err;
}

设置RTC时间:

int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err; err = rtc_valid_tm(tm);
if (err != )
return err; err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err; if (!rtc->ops)
err = -ENODEV;
else if (rtc->ops->set_time)
err = rtc->ops->set_time(rtc->dev.parent, tm);
else if (rtc->ops->set_mmss) {
unsigned long secs;
err = rtc_tm_to_time(tm, &secs);
if (err == )
err = rtc->ops->set_mmss(rtc->dev.parent, secs);
} else
err = -EINVAL; mutex_unlock(&rtc->ops_lock);
/* A timer might have just expired */
schedule_work(&rtc->irqwork);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_time); int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
{
int err; err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err; if (!rtc->ops)
err = -ENODEV;
else if (rtc->ops->set_mmss)
err = rtc->ops->set_mmss(rtc->dev.parent, secs);
else if (rtc->ops->read_time && rtc->ops->set_time) {
struct rtc_time new, old; err = rtc->ops->read_time(rtc->dev.parent, &old);
if (err == ) {
rtc_time_to_tm(secs, &new); /*
* avoid writing when we're going to change the day of
* the month. We will retry in the next minute. This
* basically means that if the RTC must not drift
* by more than 1 minute in 11 minutes.
*/
if (!((old.tm_hour == && old.tm_min == ) ||
(new.tm_hour == && new.tm_min == )))
err = rtc->ops->set_time(rtc->dev.parent,
&new);
}
}
else
err = -EINVAL; mutex_unlock(&rtc->ops_lock);
/* A timer might have just expired */
schedule_work(&rtc->irqwork); return err;
}
EXPORT_SYMBOL_GPL(rtc_set_mmss);

读取Alarm时间:

static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err; err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err; if (rtc->ops == NULL)
err = -ENODEV;
else if (!rtc->ops->read_alarm)
err = -EINVAL;
else {
memset(alarm, , sizeof(struct rtc_wkalrm));
err = rtc->ops->read_alarm(rtc->dev.parent, alarm);
} mutex_unlock(&rtc->ops_lock);
return err;
}

设置Alarm时间:

static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
struct rtc_time tm;
long now, scheduled;
int err; err = rtc_valid_tm(&alarm->time);
if (err)
return err;
rtc_tm_to_time(&alarm->time, &scheduled); /* Make sure we're not setting alarms in the past */
err = __rtc_read_time(rtc, &tm);
rtc_tm_to_time(&tm, &now);
if (scheduled <= now)
return -ETIME;
/*
* XXX - We just checked to make sure the alarm time is not
* in the past, but there is still a race window where if
* the is alarm set for the next second and the second ticks
* over right here, before we set the alarm.
*/ if (!rtc->ops)
err = -ENODEV;
else if (!rtc->ops->set_alarm)
err = -EINVAL;
else
err = rtc->ops->set_alarm(rtc->dev.parent, alarm); return err;
}

打开关闭irq:

int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
{
int err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err; if (rtc->aie_timer.enabled != enabled) {
if (enabled)
err = rtc_timer_enqueue(rtc, &rtc->aie_timer);
else
rtc_timer_remove(rtc, &rtc->aie_timer);
} if (err)
/* nothing */;
else if (!rtc->ops)
err = -ENODEV;
else if (!rtc->ops->alarm_irq_enable)
err = -EINVAL;
else
err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled); mutex_unlock(&rtc->ops_lock);
return err;
}
EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable); static void rtc_alarm_disable(struct rtc_device *rtc)
{
if (!rtc->ops || !rtc->ops->alarm_irq_enable)
return; rtc->ops->alarm_irq_enable(rtc->dev.parent, false);
}

七、总结

AlarmTimer涉及到alarm_bases维护的一套数据、hrtimer、suspend流程、rtc设备、rtc timer。他的核心思想就是在系统进入睡眠,hrtimer硬件时钟都被关闭之后,能唤醒系统,执行超时动作。

Linux时间子系统之二:Alarm Timer的更多相关文章

  1. Linux时间子系统(十七) ARM generic timer驱动代码分析

    一.前言 关注ARM平台上timer driver(clocksource chip driver和clockevent chip driver)的驱动工程师应该会注意到timer硬件的演化过程.在单 ...

  2. Linux时间子系统(十二) periodic tick

    一.tick device概念介绍 1.数据结构 在内核中,使用struct tick_device来抽象系统中的tick设备,如下: struct tick_device {     struct ...

  3. Linux时间子系统之二:表示时间的单位和结构

    人们习惯用于表示时间的方法是:年.月.日.时.分.秒.毫秒.星期等等,但是在内核中,为了软件逻辑和代码的方便性,它使用了一些不同的时间表示方法,并为这些表示方法定义了相应的变量和数据结构,本节的内容就 ...

  4. Linux时间子系统之二:表示时间的单位和结构【转】

    本文转载自:http://blog.csdn.net/droidphone/article/details/7979295 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+] ...

  5. Linux时间子系统专题汇总

    关于Linux时间子系统有两个系列文章讲的非常好,分别是WowoTech和DroidPhone. 还有两本书分别是介绍: Linux用户空间时间子系统<Linux/UNIX系统编程手册>的 ...

  6. Linux时间子系统之(二):软件架构

    专题文档汇总目录 Notes:从框架上讲解了时间子系统,从底向上包括CPU Local TImer.Global Counter.Clock Souce/Clock Events模块管理.Tick D ...

  7. Linux时间子系统之(六):POSIX timer

    专题文档汇总目录 Notes:首先讲解了POSIX timer的标识(唯一识别).POSIX Timer的组织(管理POSIX Timer).内核中如何抽象POSIX Timer:然后分析了POSIX ...

  8. Linux时间子系统之(十二):periodic tick

    专题文档汇总目录 Notes:TickDevice模式,以及clocckevent设备.TickDevice设备的初始化,TickDevice是如何加入到系统中的.周期性Tick的产生. 原文地址:L ...

  9. Linux时间子系统之(十七):ARM generic timer驱动代码分析

    专题文档汇总目录 Notes:ARM平台Clock/Timer架构:System counter.Timer以及两者之间关系:Per cpu timer通过CP15访问,System counter通 ...

随机推荐

  1. 01_Nginx安装,nginx下部署项目,nginx.conf配置文件修改,相关文件配置

     1.下载Nginx,进入Nginx下载地址:http://nginx.org/ 点击nginx-1.8.0,进入:http://nginx.org/en/download.html,下载文件: ...

  2. CSS解决无空格太长的字母,数字不会自动换行的问题

    其实很简单,代码如下所示,注意 Style: <div class="detail_title" style="word-break: break-all;&quo ...

  3. android JNI调用机制

    JNI的出现使得开发者既可以利用Java语言跨平台.类库丰 富.开发便捷等特点,又可以利用Native语言的高效. JNI是JVM实现中的一部分,因此Native语言和Java代码都运行在JVM的宿主 ...

  4. HI258摄像头旋转配置问题

    {0x28, 0x04}, //Full row start y-flip  {0x29, 0x01}, //Pre1 row start no-flip {0x2a, 0x02}, //Pre1 r ...

  5. HBase BlockCache

    1. Cache 读写  调用逻辑:  hmaster.handleCreateTable->HRegion.createHRegion-> HRegion. initialize-> ...

  6. 熊猫猪新系统测试之三:iOS 8.0.2

    本来本猫要等到8.1版本出来后再做测试的,结果等来等去就是迟迟不推送更新呀!说好10月20号的iOS 8.1呢?为了一鼓作气写完,就先不等了.先拿手头的iOS 8.0.2系统做一下测试吧! 8.x系统 ...

  7. Integer 和int 比较

    在jdk1.5的环境下,有如下4条语句: 1 2 3 4 Integer i01 = 59; int i02 = 59; Integer i03 =Integer.valueOf(59); Integ ...

  8. 模拟IC芯片设计开发的流程

    模拟IC芯片设计开发的流程 IC的设计,模拟和数字, 还有混合IC, 在设计方法, 注意点, 工具等有明显的区别, 我主要以模拟无线接收IC系统设计为例说明. 一个IC芯片的设计开发大致包括如下步骤. ...

  9. FFPLAY的原理(七)

    同步音频 现在我们已经有了一个比较像样的播放器.所以让我们看一下还有哪些零碎的东西没处理.上次,我们掩饰了一点同步问题,也就是同步音频到视频而不是其它的同 步方式.我们将采用和视频一样的方式:做一个内 ...

  10. SVN中与资源库同步时报告了错误。1 中的 0 个资源已经同步

    SVN中与资源库同步时报告了错误.1 中的 0 个资源已经同步 原因: 文件被lock, 此时再次提交则出错,解决办法就是clean释放锁即可再次提交: 解决方案: 右键项目–>team–> ...