zoukankan      html  css  js  c++  java
  • 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硬件时钟都被关闭之后,能唤醒系统,执行超时动作。

  • 相关阅读:
    [ERR] Node 10.211.55.8:7001 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
    PAT A1137 Final Grading (25 分)——排序
    PAT A1136 A Delayed Palindrome (20 分)——回文,大整数
    PAT A1134 Vertex Cover (25 分)——图遍历
    PAT A1133 Splitting A Linked List (25 分)——链表
    PAT A1132 Cut Integer (20 分)——数学题
    PAT A1130 Infix Expression (25 分)——中序遍历
    PAT A1142 Maximal Clique (25 分)——图
    PAT A1141 PAT Ranking of Institutions (25 分)——排序,结构体初始化
    PAT A1140 Look-and-say Sequence (20 分)——数学题
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/7145879.html
Copyright © 2011-2022 走看看