LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Linux时间子系统之二:Alarm Timer

一、前言

严格来讲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设备表示的时间格式:

 1 struct rtc_time {
 2     int tm_sec;
 3     int tm_min;
 4     int tm_hour;
 5     int tm_mday;
 6     int tm_mon;
 7     int tm_year;
 8     int tm_wday;
 9     int tm_yday;
10     int tm_isdst;
11 };

 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 = 0;
    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 = 0; 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", -1, NULL, 0);-注册alarmtimer设备。
    if (IS_ERR(pdev)) {
        error = PTR_ERR(pdev);
        goto out_drv;
    }
    ws = wakeup_source_register("alarmtimer");------------------------返回注册的Wakeup Source,alarmtimer_suspend使用。
    return 0;

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 0;
}

 

在填充好结构体之后,设置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) < 0)
        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 0;
}

 

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) < 0)
        return TIMER_RETRY;

    return 0;
}

  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) > 0) {
        cur_setting->it_value = ktime_to_timespec(relative_expiry_time);
    } else {
        cur_setting->it_value.tv_sec = 0;
        cur_setting->it_value.tv_nsec = 0;
    }

    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 = 0; 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", -1, NULL, 0);
...
}

/* 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 0;

    /* Find the soonest timer to expire*/
    for (i = 0; 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 == 0)
        return 0;

    if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) {-----------------------如果expires小于2秒,保持系统唤醒2秒,并中断suspend流程。
        __pm_wakeup_event(ws, 2 * 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(0, 0));---设置rtctimer
    if (ret < 0)
        __pm_wakeup_event(ws, 1 * 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 -1;
    if (!device_may_wakeup(rtc->dev.parent))
        return -1;

    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 0;
}

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, 0, 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 != 0)
        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 == 0)
            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 == 0) {
            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 == 23 && old.tm_min == 59) ||
                (new.tm_hour == 23 && new.tm_min == 59)))
                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, 0, 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硬件时钟都被关闭之后,能唤醒系统,执行超时动作。

 

posted on 2017-07-10 15:06  ArnoldLu  阅读(9288)  评论(0编辑  收藏  举报

导航